From c5d593685085e43ebf20d72d52302cc4884f85c9 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 12 Jun 2023 10:48:28 -0400 Subject: [PATCH 001/105] lint: controls --- client/src/components/Controls.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/client/src/components/Controls.js b/client/src/components/Controls.js index f1aac5a0..01d8e030 100644 --- a/client/src/components/Controls.js +++ b/client/src/components/Controls.js @@ -48,9 +48,8 @@ const Controls = ({toggleHelp}) => { const navigate = useNavigate(); const chroms = useSelector(state => state.chroms); - const chrom = useSelector(state => state.ui.chrom); + const {chrom, position} = useSelector(state => state.ui); const positions = useSelector(state => state.positions); - const position = useSelector(state => state.ui.position); const userData = useSelector(state => state.user); const dispatch = useDispatch(); @@ -82,8 +81,7 @@ const Controls = ({toggleHelp}) => { } }, 200, {leading: false, trailing: true}), [dispatch, searchIsLongEnough, chrom]); - const changePosition = useCallback(pos => dispatch(setPosition(pos)), - [dispatch]); + const changePosition = useCallback(pos => dispatch(setPosition(pos)), [dispatch]); // --- Effects -------------------------------------------------------------- @@ -107,16 +105,13 @@ const Controls = ({toggleHelp}) => { }, [dispatch, paramsChrom, chrom]); // If the chromosome/search domain is changed in Redux, reset the position list - useEffect(() => {dispatch(fetchPositions({chrom, position: ""}));}, - [dispatch, chrom]); + useEffect(() => {dispatch(fetchPositions({chrom, position: ""}));}, [dispatch, chrom]); // If the position/search term is changed in Redux, fetch the latest autocomplete results useEffect(() => {debouncedFetch(position);}, [position]); // If the position data changes, reset the selected index - useEffect(() => { - setIndex(0); - }, [positions]); + useEffect(() => {setIndex(0);}, [positions]); // -------------------------------------------------------------------------- From adea46a263f519453345050950fff4bb41d42dbe Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 12 Jun 2023 10:48:53 -0400 Subject: [PATCH 002/105] fix: update ui if peak changes in url in PeakResults --- client/src/components/PeakResults.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/client/src/components/PeakResults.js b/client/src/components/PeakResults.js index 8f333b31..777d2d6a 100644 --- a/client/src/components/PeakResults.js +++ b/client/src/components/PeakResults.js @@ -1,5 +1,5 @@ import React, {useEffect} from 'react' -import {useSelector} from 'react-redux'; +import {useDispatch, useSelector} from 'react-redux'; import {useNavigate, useParams} from "react-router-dom"; import { Container, TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap' import { groupBy, prop } from 'rambda' @@ -8,12 +8,15 @@ import cx from 'clsx' import Icon from './Icon' import PeakAssay from './PeakAssay' +import {doSearch, setChrom, setPosition} from "../actions"; const groupAndSortPeaks = memoizeOne(groupBy(prop('assay'))); const PeakResults = () => { + const dispatch = useDispatch(); const navigate = useNavigate(); const params = useParams(); + const {chrom, position, assay: activeAssay} = params; const assays = useSelector(state => state.assays.list || []); @@ -23,6 +26,8 @@ const PeakResults = () => { const isEmpty = useSelector(state => state.peaks.isLoaded && state.peaks.list.length === 0); const peaks = useSelector(state => state.peaks.list || []); + const {ui: {chrom: uiChrom, position: uiPosition}} = useSelector(state => state.ui); + const peaksByAssay = groupAndSortPeaks(peaks); const assaysWithFeatures = Object.keys(peaksByAssay); const entries = Object.entries(peaksByAssay); @@ -39,6 +44,23 @@ const PeakResults = () => { } }, [activeAssay, chrom, position, peaksLoaded]); + useEffect(() => { + // Some weird back/forward behaviour can occur if we don't update the UI state and re-fetch peaks + // when the URL changes (which we ensure via effect dependencies). + + if (!chrom || !position) return; // If chromosome or position are undefined, don't do any UI updates + + if (uiChrom !== chrom || uiPosition !== position) { + // URL has changed, but UI wasn't updated - dependencies ensure this case but not the opposite. + dispatch(setChrom(chrom)); + dispatch(setPosition(position)); + dispatch(doSearch()); // Re-fetch the peaks if the URL changes + } + + // Explicitly don't depend on uiChrom or uiPosition + // - only check if our URL doesn't match the UI if the URL changes, not if the UI changes. + }, [chrom, position]); + return
{ chrom && position && (peaksLoading || peaksLoaded) && From 072dbb9c31328ba4d761678362b2336587f20eaa Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 12 Jun 2023 11:00:42 -0400 Subject: [PATCH 003/105] chore(config): add note on switching to hg38 --- config.example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.example.js b/config.example.js index c60b67fd..17522d5b 100644 --- a/config.example.js +++ b/config.example.js @@ -70,7 +70,7 @@ module.exports = { manhattan: { minPValue: 0.10, binSize: 100000, // 100 kb bins - chromosomeSizes: { + chromosomeSizes: { // This is for HG19; if you want HG38, you will have to change these values "1": 249250621, "2": 243199373, "3": 198022430, From 29825257c85c815f2354a2f07b6325ce472ebfe0 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 08:06:53 -0400 Subject: [PATCH 004/105] chore: rm unused front end box plot component --- client/src/components/BoxPlot.js | 332 ------------------------------- 1 file changed, 332 deletions(-) delete mode 100644 client/src/components/BoxPlot.js diff --git a/client/src/components/BoxPlot.js b/client/src/components/BoxPlot.js deleted file mode 100644 index a3b88510..00000000 --- a/client/src/components/BoxPlot.js +++ /dev/null @@ -1,332 +0,0 @@ -import React from 'react'; -import { scaleLinear } from 'd3-scale' -import { ETHNICITY_BOX_COLOR } from '../constants/app' -import { getDomain } from '../helpers/boxplot' - -const SUPERSCRIPT = '⁰¹²³⁴⁵⁶⁷⁸⁹⁻⁺' - -const FONT_SIZE = 14 - -const BAR_WIDTH = 40 -const BAR_HALF_WIDTH = BAR_WIDTH / 2 - -const PADDING = 40 - -const textStyles = { - fontSize: FONT_SIZE, - textAnchor: 'middle', -} - -export default function BoxPlot({ - title, - data, - width, - height, - domain = getDomain(data), -}) { - const horizPadding = 25; - - const dimension = { - x: PADDING + horizPadding, - y: PADDING, - width: width - PADDING, - height: height - PADDING, - } - - const yAxis = getYAxisDetails(domain) - const yDomain = [yAxis.start, yAxis.end] - - const start = dimension.width / (data.length + 2) - const xScale = scaleLinear() - .range([dimension.x + start, dimension.width]) - .domain([0, data.length]) - - return ( - - - - - { title } - - { - data.map((d, i) => - - ) - } - - ) -} - -function InnerBar({ xStart, xStop, yScale, stats, fill }) { - const border = '#333333'; - - const xLine = (xStart + xStop) / 2; - - return - - - - - - ; -} - -function Bar({ data, x, y, height, domain }) { - // const min = data.min - // const max = data.max - - /** - * @type {{ - * min: number, - * quartile_1: number, - * median: number, - * quartile_3: number, - * max: number - * }} Stats - */ - const stats = data.stats; - - /** - * @type {{ - * AF: Stats, - * EU: Stats - * }} - */ - const statsByEthnicity = data.statsByEthnicity; - - const afStats = statsByEthnicity.AF; - const euStats = statsByEthnicity.EU; - - const xMin = x - BAR_HALF_WIDTH - const xMax = x + BAR_HALF_WIDTH - - const yScale = scaleLinear().range([height, y]).domain(domain) - - if (data.n === 0) { - const delta = domain[1] - domain[0] - const text = 'Empty' - const fill = 'rgba(0, 0, 0, 0.01)' - const border = '#bbbbbb' - const color = 'rgba(0, 0, 0, 0.1)' - - return ( - - - - { text } - - - ) - } - - /* We've disabled point-level display for security reasons - David L, 2022-01-31 */ - - if (!afStats || !euStats) { - // Use overall stats, since we do not have enough AF/EU points to render ethnicity-level - // box plots securely. - return ; - } - - return ( - - {/* AF */} - - - {/* EU */} - - - ) -} - -function YAxis({ domain, step, x, y, height }) { - const label = "Signal" - - const points = [] - for (let i = domain[0]; i <= domain[1]; i += step) { - points.push(i) - } - if (points[points.length - 1] !== domain[1]) - points.push(domain[1]) - - const scale = - scaleLinear().range([height, y]).domain(domain) - - let lastY = 0 - - return ( - - - {label} - { - points.map(point => { - const y = scale(point) - - const rounded = Number(Number(point).toPrecision(3)) - const abs = Math.abs(rounded) - - const text = - (abs < 0.01 || abs > 999) && abs !== 0 ? - toExponentString(rounded) : rounded.toString() - - const result = ( - - - { - Math.abs(lastY - y) > FONT_SIZE && - - { text } - - } - - ) - - if (Math.abs(lastY - y) > FONT_SIZE) - lastY = y - - return result - }) - } - - ) -} - -function XAxis({ data, scale, x, height, width }) { - return ( - - - { - data.map((d, i) => { - return ( - - - - { d.name } - - - { `(n = ${d.data.n})` } - - - ) - }) - } - - ) -} - -function Line({ position, stroke = 'black' }) { - return ( - - ) -} - -function Rect({ position, stroke = 'black', fill = 'rgba(0, 0, 0, 0)', ...rest }) { - return ( - - ) -} - - -function getYAxisDetails(domain) { - if (!domain) - return { start: 0, end: 1, step: 0.5 } - - const [min, max] = domain - - const delta = max !== min ? max - min : 1 - - const padding = Math.round(delta * 0.1 * 4) / 4 - - let start = min - padding - let end = max + padding - - if (0 < min && Math.abs(min) < (delta * 0.5)) - start = 0 - - start = Math.round(start * 100) / 100 - end = Math.ceil(end) - - // let factor = 1 - // while (true) { - // const newEnd = Math.ceil(end * factor) / factor - // end = newEnd - // break - // } - - const step = (end - start) / 10 - - return { - start, - end, - step, - } -} - -function middle(a, b) { - const d = b - a - return a + d / 2 -} - -function toExponentString(n) { - return n.toExponential().toString().replace(/e(.)(\d+)/, (m, sign, numbers) => { - const signChar = SUPERSCRIPT[sign === '-' ? 10 : 11] - const exponent = numbers.toString().split('').map(c => SUPERSCRIPT[+c]).join('') - return signChar + exponent - }) -} From b16886bffa0d46a14308bffa581941aa35297142 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 08:07:58 -0400 Subject: [PATCH 005/105] chore: remove unused front end svg pattern --- client/src/components/App.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/client/src/components/App.js b/client/src/components/App.js index f4f90cd7..b662e834 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -8,7 +8,6 @@ import PeakResults from './PeakResults' import HelpModal from "./HelpModal"; import TermsModal from "./TermsModal"; -import {ETHNICITY_BOX_COLOR} from "../constants/app"; import {saveUser} from "../actions"; import ContactModal from "./ContactModal"; import AboutPage from "./pages/AboutPage"; @@ -97,20 +96,6 @@ class App extends Component { render() { return (
- - - - - - - - }> } /> From 8e724348f3e4e69ef687b8f7bad44bd83572b3b5 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 08:08:58 -0400 Subject: [PATCH 006/105] refact: rewrite App as functional component --- client/src/components/App.js | 52 +++++++++++++++++------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/client/src/components/App.js b/client/src/components/App.js index b662e834..fe6d306f 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -92,34 +92,30 @@ const RoutedApp = () => { }; -class App extends Component { - render() { - return ( -
- - }> - } /> - } /> - } /> - - - } /> - - - }> - } /> - } /> - } /> - - } /> - } /> - - }/> - -
- ) - } -} +const App = () => ( +
+ + }> + } /> + } /> + } /> + + + } /> + + + }> + } /> + } /> + } /> + + } /> + } /> + + }/> + +
+); export default App; From 48db23e451eac3d23b868588b13b422ca86f69af Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 08:09:19 -0400 Subject: [PATCH 007/105] lint --- client/src/components/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/App.js b/client/src/components/App.js index fe6d306f..d52d103c 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -1,4 +1,4 @@ -import React, {Component, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {Navigate, Outlet, Route, Routes, useLocation, useNavigate} from "react-router-dom"; import {useDispatch, useSelector} from "react-redux"; From ea3cbd25dca1f33cfde8b72c170430b65c8a17d0 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 15:16:38 -0400 Subject: [PATCH 008/105] lint: remove dead code & reformat --- client/src/actions.js | 2 +- client/src/helpers/boxplot.js | 40 ----------------------------------- helpers/boxplot.js | 1 - routes/autocomplete.mjs | 4 ++-- routes/messages.mjs | 6 +----- 5 files changed, 4 insertions(+), 49 deletions(-) delete mode 100644 client/src/helpers/boxplot.js diff --git a/client/src/actions.js b/client/src/actions.js index ab2cfebc..18f53991 100644 --- a/client/src/actions.js +++ b/client/src/actions.js @@ -24,7 +24,7 @@ export const user = createFetchActions(k.USER); export const messages = createFetchActions(k.MESSAGES); export const fetchAssays = createFetchFunction(api.fetchAssays, assays); -export const fetchChroms = createFetchFunction(api.fetchChroms, chroms); +// export const fetchChroms = createFetchFunction(api.fetchChroms, chroms); export const fetchPositions = createFetchFunction(api.fetchPositions, positions); export const cacheValues = createFetchFunction(api.cacheValues, values); export const fetchPeaks = createFetchFunction(api.fetchPeaks, peaks); diff --git a/client/src/helpers/boxplot.js b/client/src/helpers/boxplot.js deleted file mode 100644 index 49697775..00000000 --- a/client/src/helpers/boxplot.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * boxplot.js - */ - - -export function getDomain(categories) { - if (categories.length === 0) { - return undefined; - } - - let min = Infinity; - let max = -Infinity; - - categories.forEach(({ data }) => { - if (data.stats.min < min) - min = data.stats.min - if (data.stats.max > max) - max = data.stats.max - }); - - return [ - min, - max, - ]; -} - -export function combineDomains(ds) { - const fds = ds.filter(Boolean) - if (fds.length === 0) - return [0, 10] - let min = Infinity - let max = -Infinity - fds.forEach(d => { - if (d[0] < min) - min = d[0] - if (d[1] > max) - max = d[1] - }) - return [min, max] -} diff --git a/helpers/boxplot.js b/helpers/boxplot.js index 620a267d..d7fee813 100644 --- a/helpers/boxplot.js +++ b/helpers/boxplot.js @@ -278,7 +278,6 @@ async function boxPlot({title, data, domain, transform}) { const yDomain = [yAxis.start, yAxis.end]; const start = plotDimensions.width / (data.length + 2); - // const xScale = TODO; const xScale = (await scaleLinear()) .range([plotDimensions.x + start, plotDimensions.width]) diff --git a/routes/autocomplete.mjs b/routes/autocomplete.mjs index 70134390..eb2e2f51 100644 --- a/routes/autocomplete.mjs +++ b/routes/autocomplete.mjs @@ -13,8 +13,8 @@ const router = express.Router(); router.get('/chroms', (_req, res) => { Peaks.chroms() - .then(dataHandler(res)) - .catch(errorHandler(res)); + .then(dataHandler(res)) + .catch(errorHandler(res)); }); router.get('/positions', ensureLogIn, ({query}, res) => { diff --git a/routes/messages.mjs b/routes/messages.mjs index 9d842cf2..7685de30 100644 --- a/routes/messages.mjs +++ b/routes/messages.mjs @@ -5,10 +5,6 @@ import express from "express"; import { dataHandler } from "../helpers/handlers.mjs"; -const router = express.Router(); - -router.get("/list", (req, res) => { +export default express.Router().get("/list", (req, res) => { dataHandler(res)(req.session?.messages ?? []); }); - -export default router; From b55ed4a8cc2d3f9e0caee9403b7d837f51e8e8c6 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 15:24:45 -0400 Subject: [PATCH 009/105] feat: configurable assembly/conditions/ethnicities --- app.mjs | 3 + client/src/actions.js | 36 ++++-- client/src/api.js | 5 +- client/src/components/PeakAssay.js | 85 +++++++------- client/src/components/PeakBoxplot.js | 49 ++++---- client/src/components/pages/DatasetsPage.js | 30 +++-- client/src/constants/ActionTypes.js | 3 + client/src/constants/app.js | 12 -- client/src/helpers/conditions.js | 7 -- client/src/helpers/reducers.js | 37 ++++++ client/src/index.js | 23 +++- client/src/reducers.js | 124 ++++---------------- config.example.js | 65 ++++++---- helpers/boxplot.js | 60 ++++------ helpers/defaultValues.mjs | 7 ++ routes/assembly.mjs | 8 ++ routes/conditions.mjs | 9 ++ routes/ethnicities.mjs | 9 ++ scripts/calculate-top-peaks.js | 2 +- 19 files changed, 299 insertions(+), 275 deletions(-) delete mode 100644 client/src/helpers/conditions.js create mode 100644 client/src/helpers/reducers.js create mode 100644 helpers/defaultValues.mjs create mode 100644 routes/assembly.mjs create mode 100644 routes/conditions.mjs create mode 100644 routes/ethnicities.mjs diff --git a/app.mjs b/app.mjs index 661c456f..011cccf8 100644 --- a/app.mjs +++ b/app.mjs @@ -56,7 +56,10 @@ if (!process.env.VARWIG_DISABLE_AUTH) { app.use('/api/assays', (await import('./routes/assays.mjs')).default); app.use('/api/autocomplete', (await import('./routes/autocomplete.mjs')).default); app.use('/api/messages', (await import('./routes/messages.mjs')).default); +app.use('/api/assembly', (await import('./routes/assembly.mjs')).default); app.use('/api/overview', (await import('./routes/overview.mjs')).default); +app.use('/api/conditions', (await import('./routes/conditions.mjs')).default); +app.use('/api/ethnicities', (await import('./routes/ethnicities.mjs')).default); app.use('/api/peaks', (await import('./routes/peaks.mjs')).default); app.use('/api/sessions', (await import('./routes/sessions.mjs')).default); app.use('/api/samples', (await import('./routes/samples.mjs')).default); diff --git a/client/src/actions.js b/client/src/actions.js index 18f53991..307c35b4 100644 --- a/client/src/actions.js +++ b/client/src/actions.js @@ -3,7 +3,7 @@ import axios from 'axios' import * as api from './api' import * as k from './constants/ActionTypes.js' -import {ASSEMBLY, BASE_URL} from "./constants/app"; +import {BASE_URL} from "./constants/app"; import {constructUCSCUrl} from "./helpers/ucsc"; export const setChrom = createAction(k.SET_CHROM); @@ -18,6 +18,9 @@ export const chroms = createFetchActions(k.CHROMS); export const positions = createFetchActions(k.POSITIONS); export const values = createFetchActions(k.VALUES); export const peaks = createFetchActions(k.PEAKS); +export const assembly = createFetchActions(k.ASSEMBLY); +export const conditions = createFetchActions(k.CONDITIONS); +export const ethnicities = createFetchActions(k.ETHNICITIES); export const overviewConfig = createFetchActions(k.OVERVIEW_CONFIG); export const manhattanData = createFetchActions(k.MANHATTAN_DATA); export const user = createFetchActions(k.USER); @@ -28,6 +31,9 @@ export const fetchAssays = createFetchFunction(api.fetchAssays, export const fetchPositions = createFetchFunction(api.fetchPositions, positions); export const cacheValues = createFetchFunction(api.cacheValues, values); export const fetchPeaks = createFetchFunction(api.fetchPeaks, peaks); +export const fetchAssembly = createFetchFunction(api.fetchAssembly, assembly); +export const fetchConditions = createFetchFunction(api.fetchConditions, conditions); +export const fetchEthnicities = createFetchFunction(api.fetchEthnicities, ethnicities); export const fetchOverviewConfig = createFetchFunction(api.fetchOverviewConfig, overviewConfig); export const fetchManhattanData = createFetchFunction(api.fetchManhattanData, manhattanData); export const fetchUser = createFetchFunction(api.fetchUser, user); @@ -44,32 +50,42 @@ export const doSearch = () => (dispatch, getState) => { } }; -export const mergeTracks = peak => dispatch => { +const buildUCSCHighlight = (asm, chr, start, end, color) => `${asm}.${chr}:${start}-${end}${color}`; + +export const mergeTracks = peak => (dispatch, getState) => { + const assembly = getState().assembly.data?.id; + + if (!assembly) { + console.error(`Could not retrieve assembly ID - got ${assembly}`); + return; + } + const session = {...peak}; + const {feature, snp} = session; const padding = 500; - const featureChrom = `chr${session.feature.chrom}`; - const snpChrom = `chr${session.snp.chrom}`; + const featureChrom = `chr${feature.chrom}`; + const snpChrom = `chr${snp.chrom}`; api.createSession(session) .then(sessionID => { - const snpPosition = session.snp.position; + const snpPosition = snp.position; const displayWindow = featureChrom === snpChrom - ? [Math.min(session.feature.start, snpPosition), Math.max(session.feature.end, snpPosition)] - : [session.feature.start, session.feature.end]; + ? [Math.min(feature.start, snpPosition), Math.max(feature.end, snpPosition)] + : [feature.start, feature.end]; const position = `${featureChrom}:${displayWindow[0]-padding}-${displayWindow[1]+padding}`; const hubURL = `${BASE_URL}/api/ucsc/hub/${sessionID}`; const ucscURL = constructUCSCUrl([ - ["db", ASSEMBLY], + ["db", assembly], ["hubClear", hubURL], // ["hubClear", permaHubURL], ["position", position], // Highlight the SNP in red, and the feature in light yellow ["highlight", [ - `${ASSEMBLY}.${featureChrom}:${session.feature.start}-${session.feature.end}#FFEECC`, - `${ASSEMBLY}.${snpChrom}:${session.snp.position}-${session.snp.position+1}#FF9F9F`, + buildUCSCHighlight(assembly, featureChrom, feature.start, feature.end, "#FFEECC"), + buildUCSCHighlight(assembly, snpChrom, snp.position, snp.position + 1, "#FF9F9F"), ].join("|")], ]); diff --git a/client/src/api.js b/client/src/api.js index ef7b8645..f005b6c3 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -31,8 +31,11 @@ export function fetchPositions(params, cancelToken) { return get('/autocomplete/positions', params, {cancelToken}) } -export const fetchOverviewConfig = () => get('/overview/config'); +export const fetchAssembly = () => get('/assembly'); +export const fetchConditions = () => get('/conditions'); +export const fetchEthnicities = () => get('/ethnicities'); +export const fetchOverviewConfig = () => get('/overview/config'); export const fetchManhattanData = ({chrom, assay}) => get(`/overview/assays/${assay}/topBinned/${chrom}`); diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index a8841464..2f085940 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -1,5 +1,5 @@ -import {useEffect, useMemo, useState} from 'react'; -import {useDispatch} from 'react-redux'; +import {useCallback, useEffect, useMemo, useState} from 'react'; +import {useDispatch, useSelector} from 'react-redux'; import { Button, Container, @@ -15,8 +15,9 @@ import {useTable, usePagination, useSortBy} from "react-table"; import Icon from "./Icon"; import PeakBoxplot from "./PeakBoxplot"; import {cacheValues, mergeTracks} from "../actions"; -import {ASSEMBLY} from "../constants/app"; -import {CONDITION_FLU, CONDITION_NI, conditionName} from "../helpers/conditions"; + + +const PAGE_SIZES = [10, 20, 30, 40, 50]; const PeakAssay = ({peaks}) => { @@ -36,10 +37,14 @@ const PeakAssay = ({peaks}) => { const selectedPeakData = peaks.find(p => p.id === selectedPeak); - const fetchSome = (exclude = []) => - peaks.filter(p => !exclude.includes(p.id)).slice(0, 10).forEach(p => { - dispatch(cacheValues(p, {id: p.id})); - }); + const fetchSome = useCallback((exclude = []) => + peaks + .filter(p => !exclude.includes(p.id)) + .slice(0, 10) + .forEach(p => { + dispatch(cacheValues(p, {id: p.id})); + }), + [peaks, dispatch]); // Fetch some peaks at the start for performance useEffect(() => { @@ -50,7 +55,7 @@ const PeakAssay = ({peaks}) => { } else { fetchSome(); } - }, [selectedPeakData]); + }, [selectedPeakData, fetchSome]); return ( @@ -75,6 +80,9 @@ const PeakAssay = ({peaks}) => { }; const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { + const {id: assembly} = useSelector(state => state.assembly.data) ?? {}; + const conditions = useSelector(state => state.conditions.list); + const [tooltipsShown, setTooltipsShown] = useState({}); const toggleTooltip = tooltipID => () => setTooltipsShown({ @@ -86,12 +94,12 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { { id: "snp", Header: "SNP", - accessor: row => { - const k = `row${row.id}-snp`; + accessor: ({id, snp}) => { + const k = `row${id}-snp`; return
- {row.snp.id} + {snp.id} - [{ASSEMBLY}] chr{row.snp.chrom}:{row.snp.position} + [{assembly}] chr{snp.chrom}:{snp.position}
; }, @@ -102,16 +110,17 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { Header: "Feature", className: "PeaksTable__feature", accessor: row => { - const k = `row${row.id}-feature`; + const {id, feature} = row; + const k = `row${id}-feature`; const featureText = formatFeature(row); const showTooltip = !featureText.startsWith("chr"); return
{featureText} {showTooltip ? ( - [{ASSEMBLY}] chr{row.feature.chrom}:{row.feature.start}-{row.feature.end} + [{assembly}] chr{feature.chrom}:{feature.start}-{feature.end} {" "} - {row.feature.strand ? `(strand: ${row.feature.strand})` : null} + {feature.strand ? `(strand: ${feature.strand})` : null} ) : null}
; @@ -122,10 +131,7 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { id: "distance", Header: "SNP-Feature Distance", className: "PeaksTable__distance", - accessor: row => { - const snpPos = row.snp.position; - const {start, end} = row.feature; - + accessor: ({snp: {position: snpPos}, feature: {start, end}}) => { if (start <= snpPos && snpPos <= end) { return "contained"; } @@ -141,26 +147,20 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { }, disableSortBy: true, }, - { - id: "valueNI", - Header: p Value ({conditionName(CONDITION_NI)}), - accessor: row => { - const fixed = row.valueNI.toPrecision(5); - const floatStr = row.valueNI.toString(); - return floatStr.length < fixed.length ? floatStr : fixed; - }, - sortType: (r1, r2, col) => r1.original[col] < r2.original[col] ? -1 : 1, - }, - { - id: "valueFlu", - Header: p Value ({conditionName(CONDITION_FLU)}), - accessor: row => { - const fixed = row.valueFlu.toPrecision(5); - const floatStr = row.valueFlu.toString(); - return floatStr.length < fixed.length ? floatStr : fixed; - }, - sortType: (r1, r2, col) => r1.original[col] < r2.original[col] ? -1 : 1, - }, + ...conditions.map(({id, name}) => { + const k = `value${id}`; + // noinspection JSUnusedGlobalSymbols + return { + id: k, + Header: p Value ({name}), + accessor: row => { + const fixed = row[k].toPrecision(5); + const floatStr = row[k].toString(); + return floatStr.length < fixed.length ? floatStr : fixed; + }, + sortType: (r1, r2, col) => r1.original[col] < r2.original[col] ? -1 : 1, + }; + }), { id: "ucsc", Header: "View in UCSC", @@ -170,8 +170,7 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { , disableSortBy: true, }, - ], [onOpenTracks, tooltipsShown]); - // const data = useMemo(() => peaks, []); + ], [assembly, conditions, onOpenTracks, tooltipsShown]); // noinspection JSCheckFunctionSignatures const tableInstance = useTable( @@ -276,7 +275,7 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { onChange={e => setPageSize(Number(e.target.value))} style={{width: "120px", marginLeft: "1em"}} > - {[10, 20, 30, 40, 50].map(pageSize => ( + {PAGE_SIZES.map(pageSize => ( diff --git a/client/src/components/PeakBoxplot.js b/client/src/components/PeakBoxplot.js index ba406500..f3a3e18d 100644 --- a/client/src/components/PeakBoxplot.js +++ b/client/src/components/PeakBoxplot.js @@ -1,8 +1,8 @@ -import React, {useEffect, useRef, useState} from 'react'; - -import { ETHNICITY_COLOR } from '../constants/app' +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; function PeakBoxplot({ title, peak, /*values = defaultValues*/ }) { + const ethnicities = useState(state => state.ethnicities.list); + const [loaded, setLoaded] = useState(false); const prevPeakRef = useRef(); @@ -14,35 +14,40 @@ function PeakBoxplot({ title, peak, /*values = defaultValues*/ }) { }, [peak]); const peakImg = `${process.env.PUBLIC_URL}/api/tracks/plot/${peak?.id}`; + const peakImgStyle = useMemo(() => ({ + width: "100%", + height: "auto", + maxWidth: 700, + opacity: loaded ? 1 : 0, + transition: "opacity ease-in-out 0.3s", + }), [loaded]); + const peakImgOnLoad = useCallback(() => setLoaded(true), []); return (
{title}
{ - peak && ( - setLoaded(true)} - src={peakImg} - alt="Peak Box Plots"/> + peak ? ( + Peak Box Plots + ) : ( +
) }
-
- African-American -
-
- European-American -
+ {ethnicities.map(({id, name, plotColor}) => ( +
+ {name} +
+ ))}

diff --git a/client/src/components/pages/DatasetsPage.js b/client/src/components/pages/DatasetsPage.js index 08f9c23d..2f3e083d 100644 --- a/client/src/components/pages/DatasetsPage.js +++ b/client/src/components/pages/DatasetsPage.js @@ -1,23 +1,29 @@ -import React from "react"; +import React, {useCallback} from "react"; import {Col, Container, Row} from "reactstrap"; import {Link} from "react-router-dom"; -import {ASSEMBLY, BASE_URL} from "../../constants/app"; +import {BASE_URL} from "../../constants/app"; import {constructUCSCUrl} from "../../helpers/ucsc"; +import {useSelector} from "react-redux"; -const openTracks = () => { - const permaHubURL = `${BASE_URL}/api/ucsc/perma/hub/other-tracks`; - const ucscURL = constructUCSCUrl([ - ["db", ASSEMBLY], - ["hubClear", permaHubURL], - ]); - console.log("UCSC:", ucscURL); - window.open(ucscURL); -}; + +const lgColSize = {size: 10, offset: 1}; const DatasetsPage = () => { + const {id: assembly} = useSelector(state => state.assembly.data) ?? {}; + + const openTracks = useCallback(() => { + const permaHubURL = `${BASE_URL}/api/ucsc/perma/hub/other-tracks`; + const ucscURL = constructUCSCUrl([ + ["db", assembly], + ["hubClear", permaHubURL], + ]); + console.log("UCSC:", ucscURL); + window.open(ucscURL); + }, [assembly]); + return - +

Datasets

All the datasets that have been generated as part of this project are made available freely. Access to some diff --git a/client/src/constants/ActionTypes.js b/client/src/constants/ActionTypes.js index 3f0bd469..a709b437 100644 --- a/client/src/constants/ActionTypes.js +++ b/client/src/constants/ActionTypes.js @@ -5,6 +5,9 @@ export const SET_OVERVIEW_ASSAY = 'SET_OVERVIEW_ASSAY'; export const HANDLE_ERROR = 'HANDLE_ERROR'; export const ASSAYS = createFetchConstants('ASSAYS'); +export const ASSEMBLY = createFetchConstants('ASSEMBLY'); +export const CONDITIONS = createFetchConstants('CONDITIONS'); +export const ETHNICITIES = createFetchConstants('ETHNICITIES'); export const SAMPLES = createFetchConstants('SAMPLES'); export const CHROMS = createFetchConstants('CHROMS'); export const POSITIONS = createFetchConstants('POSITIONS'); diff --git a/client/src/constants/app.js b/client/src/constants/app.js index 56e0cb10..3a516b5e 100644 --- a/client/src/constants/app.js +++ b/client/src/constants/app.js @@ -4,15 +4,3 @@ export const BASE_URL = `${window.location.origin}${process.env.PUBLIC_URL || ''}`; export const LOGIN_PATH = "/api/auth/login"; - -export const ASSEMBLY = "hg19"; - -export const ETHNICITY_COLOR = { - AF: '#5100FF', - EU: '#FF8A00', -}; - -export const ETHNICITY_BOX_COLOR = { - AF: 'rgba(81, 0, 255, 0.6)', - EU: 'rgba(255, 138, 0, 0.6)', -}; diff --git a/client/src/helpers/conditions.js b/client/src/helpers/conditions.js deleted file mode 100644 index f9340636..00000000 --- a/client/src/helpers/conditions.js +++ /dev/null @@ -1,7 +0,0 @@ -export const CONDITION_NI = "NI"; -export const CONDITION_FLU = "Flu"; - -export const conditionName = (c) => ({ - [CONDITION_NI]: "Non-infected", - [CONDITION_FLU]: "Flu", -}[c] ?? "Unknown"); diff --git a/client/src/helpers/reducers.js b/client/src/helpers/reducers.js new file mode 100644 index 00000000..3803f0c8 --- /dev/null +++ b/client/src/helpers/reducers.js @@ -0,0 +1,37 @@ +export const makeDefaultListState = (defaultList = undefined) => ({ + isLoading: false, + isLoaded: false, + list: defaultList ?? [], +}); + +export const makeListReducer = (types, defaultState) => (state = defaultState, action) => { + switch (action.type) { + case types.REQUEST: + return {...state, isLoading: true, isLoaded: false, list: []}; + case types.RECEIVE: + return {...state, isLoading: false, isLoaded: true, list: action.payload}; + case types.ERROR: + return {...state, isLoading: false}; + default: + return state; + } +}; + +export const makeDefaultDataState = (defaultData = undefined) => ({ + isLoading: false, + isLoaded: false, + data: defaultData, +}); + +export const makeDataReducer = (types, defaultState) => (state = defaultState, action) => { + switch (action.type) { + case types.REQUEST: + return {...state, isLoading: true}; + case types.RECEIVE: + return {...state, isLoading: false, isLoaded: true, data: action.payload}; + case types.ERROR: + return {...state, isLoading: false}; + default: + return state; + } +}; diff --git a/client/src/index.js b/client/src/index.js index 6ae4c6ab..33f811c1 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -6,7 +6,7 @@ import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { createLogger } from 'redux-logger'; -import {BrowserRouter as Router} from 'react-router-dom'; +import { BrowserRouter as Router } from 'react-router-dom'; import thunkMiddleware from 'redux-thunk'; import { legacy_createStore as createStore, applyMiddleware } from 'redux'; import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extension'; @@ -14,7 +14,15 @@ import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extensio import './styles.css'; import { rootReducer } from './reducers'; import App from './components/App'; -import {fetchAssays, /* fetchChroms, */ fetchMessages, fetchUser} from './actions.js' +import { + fetchAssays, + fetchAssembly, + fetchConditions, + fetchEthnicities, + /* fetchChroms, */ + fetchMessages, + fetchUser +} from './actions.js' const initialState = {}; @@ -42,15 +50,18 @@ render( document.querySelector('#root') ); -store.dispatch(fetchUser()) -store.dispatch(fetchMessages()) // Server-side messages, e.g. auth errors +store.dispatch(fetchUser()); +store.dispatch(fetchMessages()); // Server-side messages, e.g. auth errors -store.dispatch(fetchAssays()) +store.dispatch(fetchAssays()); +store.dispatch(fetchAssembly()); +store.dispatch(fetchConditions()); +store.dispatch(fetchEthnicities()); /* Re-enable to bring back server-fetched genomic chromosomes. For now, this instead just holds rsID + gene. - David L, 2023-05-25 -store.dispatch(fetchChroms()) +store.dispatch(fetchChroms()); */ diff --git a/client/src/reducers.js b/client/src/reducers.js index 1ee0d5ff..6b0503b6 100644 --- a/client/src/reducers.js +++ b/client/src/reducers.js @@ -1,5 +1,6 @@ import { combineReducers } from 'redux' import * as k from './constants/ActionTypes' +import {makeDataReducer, makeDefaultDataState, makeDefaultListState, makeListReducer} from "./helpers/reducers"; const defaultChrom = 'rsID' @@ -119,52 +120,22 @@ function positionsReducer(state = defaultPositions, action) { } } -const defaultPeaks = { - isLoading: false, - isLoaded: false, - list: [], -} -function peaksReducer(state = defaultPeaks, action) { - switch (action.type) { - case k.PEAKS.REQUEST: { - return { ...state, isLoading: true, isLoaded: false, list: [] } - } - case k.PEAKS.RECEIVE: { - return { ...state, isLoading: false, isLoaded: true, list: action.payload } - } - case k.PEAKS.ERROR: { - return { ...state, isLoading: false } - } - default: - return state; - } -} +const defaultPeaks = makeDefaultListState(); +const peaksReducer = makeListReducer(k.PEAKS, defaultPeaks); -const defaultAssays = { - isLoading: false, - isLoaded: false, - list: ['RNA-seq', 'ATAC-seq', 'H3K4me1', 'H3K4me3', 'H3K27ac', 'H3K27me3'], -} -function assaysReducer(state = defaultAssays, action) { - switch (action.type) { - case k.ASSAYS.REQUEST: { - return { ...state, isLoading: true } - } - case k.ASSAYS.RECEIVE: { - return { - ...state, - isLoading: false, - isLoaded: true, - list: action.payload - } - } - case k.ASSAYS.ERROR: { - return { ...state, isLoading: false } - } - default: - return state; - } -} +const defaultAssays = makeDefaultListState( + ['RNA-seq', 'ATAC-seq', 'H3K4me1', 'H3K4me3', 'H3K27ac', 'H3K27me3'] +); +const assaysReducer = makeListReducer(k.ASSAYS, defaultAssays); + +const defaultConditions = makeDefaultListState(); +const conditionsReducer = makeListReducer(k.CONDITIONS, defaultConditions); + +const defaultEthnicities = makeDefaultListState(); +const ethnicitiesReducer = makeListReducer(k.ETHNICITIES, defaultEthnicities); + +const defaultAssembly = makeDefaultDataState(); +const assemblyReducer = makeDataReducer(k.ASSEMBLY, defaultAssembly); const defaultOverview = { isLoading: false, @@ -242,68 +213,25 @@ const manhattanReducer = (state = defaultManhattan, action) => { } }; -const defaultUser = { - isLoading: false, - isLoaded: false, - data: undefined, -}; -function userReducer(state = defaultUser, action) { - switch (action.type) { - case k.USER.REQUEST: { - return {...state, isLoading: true}; - } - case k.USER.RECEIVE: { - return { - ...state, - isLoading: false, - isLoaded: true, - data: action.payload, - }; - } - case k.USER.ERROR: { - return {...state, isLoading: false}; - } - default: - return state; - } -} +const defaultUser = makeDefaultDataState(); +const userReducer = makeDataReducer(k.USER, defaultUser); -const defaultMessages = { - isLoading: false, - isLoaded: false, - total: 0, - list: [], -}; -function messagesReducer(state = defaultMessages, action) { - switch (action.type) { - case k.MESSAGES.REQUEST: { - return {...state, isLoading: true}; - } - case k.MESSAGES.RECEIVE: { - return { - ...state, - isLoading: false, - isLoaded: true, - list: action.payload, - }; - } - case k.MESSAGES.ERROR: { - return {...state, isLoading: false}; - } - default: - return state; - } -} +const defaultMessages = makeDefaultListState(); +const messagesReducer = makeListReducer(k.MESSAGES, defaultMessages); export const rootReducer = combineReducers({ ui: uiReducer, - samples: samplesReducer, chroms: chromsReducer, positions: positionsReducer, - peaks: peaksReducer, + assays: assaysReducer, + assembly: assemblyReducer, + conditions: conditionsReducer, + ethnicities: ethnicitiesReducer, + samples: samplesReducer, + peaks: peaksReducer, overview: overviewReducer, manhattan: manhattanReducer, diff --git a/config.example.js b/config.example.js index 17522d5b..170ae2b1 100644 --- a/config.example.js +++ b/config.example.js @@ -36,6 +36,15 @@ module.exports = { metadata: { path: path.join(__dirname, 'data/metadata.json'), }, + + conditions: [ + {id: "NI", name: "Non-infected"}, + {id: "Flu", name: "Flu"}, + ], + ethnicities: [ + {id: "AF", name: "African-American", plotColor: "#5100FF", plotBoxColor: "rgba(81, 0, 255, 0.6)"}, + {id: "EU", name: "European-American", plotColor: "#FF8A00", plotBoxColor: "rgba(255, 138, 0, 0.6)"}, + ], }, // The application was conceived to accept multiple data sources, @@ -66,36 +75,40 @@ module.exports = { semaphoreLimit: 2, }, + assembly: { + id: 'hg19', + chromosomeSizes: { // This is for HG19; if you want HG38, you will have to change these values + "1": 249250621, + "2": 243199373, + "3": 198022430, + "4": 191154276, + "5": 180915260, + "6": 171115067, + "7": 159138663, + "8": 146364022, + "9": 141213431, + "10": 135534747, + "11": 135006516, + "12": 133851895, + "13": 115169878, + "14": 107349540, + "15": 102531392, + "16": 90354753, + "17": 81195210, + "18": 78077248, + "19": 59128983, + "20": 63025520, + "21": 48129895, + "22": 51304566, + // "X": 155270560, + // "Y": 59373566, + }, + }, + plots: { manhattan: { minPValue: 0.10, binSize: 100000, // 100 kb bins - chromosomeSizes: { // This is for HG19; if you want HG38, you will have to change these values - "1": 249250621, - "2": 243199373, - "3": 198022430, - "4": 191154276, - "5": 180915260, - "6": 171115067, - "7": 159138663, - "8": 146364022, - "9": 141213431, - "10": 135534747, - "11": 135006516, - "12": 133851895, - "13": 115169878, - "14": 107349540, - "15": 102531392, - "16": 90354753, - "17": 81195210, - "18": 78077248, - "19": 59128983, - "20": 63025520, - "21": 48129895, - "22": 51304566, - // "X": 155270560, - // "Y": 59373566, - }, }, }, diff --git a/helpers/boxplot.js b/helpers/boxplot.js index d7fee813..daec88cd 100644 --- a/helpers/boxplot.js +++ b/helpers/boxplot.js @@ -1,3 +1,8 @@ +import config from "../config"; + +const {ethnicities} = config.source; +const nEthnicities = ethnicities.length; + const scaleLinear = (...args) => import("d3-scale").then( ({scaleLinear}) => scaleLinear(...args)); @@ -11,18 +16,11 @@ const SCALE_FONT_SIZE = 12; const BAR_WIDTH = 40; const BAR_HALF_WIDTH = BAR_WIDTH / 2; +const BAR_INTERVAL_WIDTH = BAR_WIDTH / nEthnicities; // Small chunk of the bar, in x-space, for each ethnicity const POINT_RADIUS = 4; -const ETHNICITY_COLOR = { - AF: '#5100FF', - EU: '#FF8A00', -}; - -const textStyles = `font-size: ${FONT_SIZE}; font-family: sans-serif; text-anchor: middle`; /*{ - fontSize: FONT_SIZE, - textAnchor: 'middle', -};*/ +const TEXT_STYLES = `font-size: ${FONT_SIZE}; font-family: sans-serif; text-anchor: middle`; const PLOT_SIZE = 350; const PLOT_PADDING = 40; @@ -124,10 +122,10 @@ function boxPlotXAxis({data, scale, x, height, width}) { const inner = data.map((d, i) => { return ` ${boxPlotLine({position: [[scale(i), height], [scale(i), height + 5]]})} - + ${d.name} - + (n = ${d.data.n}) `; @@ -180,22 +178,22 @@ async function boxPlotBar({data, x, y, height, domain}) { */ const stats = data.stats; - const afPoints = data.points.AF || []; - const euPoints = data.points.EU || []; - - shuffle(afPoints); - shuffle(euPoints); + const points = ethnicities.map(e => { + const points = data.points[e.id] || []; + shuffle(points); + return points; + }); const padding = 5; const xMin = x - BAR_HALF_WIDTH; const xMax = x + BAR_HALF_WIDTH; - const afXRange = [xMin + padding, x - padding]; - const euXRange = [x + padding, xMax - padding]; + const ranges = ethnicities.map((e, i) => + [xMin + (i * BAR_INTERVAL_WIDTH) + padding, xMin + ((i + 1) * BAR_INTERVAL_WIDTH) - padding]); - const afScale = (await scaleLinear()).range(afXRange).domain([0, afPoints.length || 1]); - const euScale = (await scaleLinear()).range(euXRange).domain([0, euPoints.length || 1]); + const xScales = await Promise.all(ranges.map((r, i) => + scaleLinear().then(sl => sl.range(r).domain([0, points[i].length || 1])))); const yScale = (await scaleLinear()).range([height, y]).domain(domain); @@ -237,33 +235,21 @@ async function boxPlotBar({data, x, y, height, domain}) { xStop: xMax, stats, yScale, - // fill: "url(#diagonal)", }); - const afDots = afPoints.map((value, i) => + const dots = ethnicities.flatMap(({plotColor}, i) => points[i].map((value, j) => `` - ); - - const euDots = euPoints.map((value, i) => - `` - ); + )); return ` ${barPart} - ${afDots.join("")} - ${euDots.join("")} + ${dots.join("")} `; } diff --git a/helpers/defaultValues.mjs b/helpers/defaultValues.mjs new file mode 100644 index 00000000..1f06dea7 --- /dev/null +++ b/helpers/defaultValues.mjs @@ -0,0 +1,7 @@ +export const DEFAULT_CONDITIONS = [ + {id: "NA", name: "Not available"}, +]; + +export const DEFAULT_ETHNICITIES = [ + {id: "NA", name: "Not available", plotColor: "#5100FF", plotBoxColor: "rgba(81, 0, 255, 0.6)"}, +]; diff --git a/routes/assembly.mjs b/routes/assembly.mjs new file mode 100644 index 00000000..6ac5bb66 --- /dev/null +++ b/routes/assembly.mjs @@ -0,0 +1,8 @@ +import express from "express"; + +import config from "../config.js"; +import {dataHandler} from "../helpers/handlers.mjs"; + +export default express.Router().get("", (req, res) => { + dataHandler(res)(config.assembly); +}); diff --git a/routes/conditions.mjs b/routes/conditions.mjs new file mode 100644 index 00000000..a4a087d8 --- /dev/null +++ b/routes/conditions.mjs @@ -0,0 +1,9 @@ +import express from "express"; + +import config from "../config.js"; +import {DEFAULT_CONDITIONS} from "../helpers/defaultValues.mjs"; +import {dataHandler} from "../helpers/handlers.mjs"; + +export default express.Router().get("", (req, res) => { + dataHandler(res)(config.source?.conditions ?? DEFAULT_CONDITIONS); +}); diff --git a/routes/ethnicities.mjs b/routes/ethnicities.mjs new file mode 100644 index 00000000..a4d9f6fc --- /dev/null +++ b/routes/ethnicities.mjs @@ -0,0 +1,9 @@ +import express from "express"; + +import config from "../config.js"; +import {DEFAULT_ETHNICITIES} from "../helpers/defaultValues.mjs"; +import {dataHandler} from "../helpers/handlers.mjs"; + +export default express.Router().get("", (req, res) => { + dataHandler(res)(config.source?.ethnicities ?? DEFAULT_ETHNICITIES); +}); diff --git a/scripts/calculate-top-peaks.js b/scripts/calculate-top-peaks.js index e3d63206..bd787b69 100644 --- a/scripts/calculate-top-peaks.js +++ b/scripts/calculate-top-peaks.js @@ -9,7 +9,7 @@ const config = require("../config"); await db.run("TRUNCATE TABLE binned_most_significant_peaks RESTART IDENTITY CASCADE"); // Only calculate top binned peaks by assay for chromosomes whose size has been provided in config.js - const chromSizes = config.plots?.manhattan?.chromosomeSizes ?? {}; + const chromSizes = config.assembly?.chromosomeSizes ?? {}; const minPValue = config.plots?.manhattan?.minPValue ?? 0.10; const binSize = config.plots?.manhattan?.binSize ?? 100000; From 72f98afc9b1ecdb19c61a3139f6a490469c8b61e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 15:25:19 -0400 Subject: [PATCH 010/105] feat: configurable gemini sample name extractor fn --- config.example.js | 7 +++++++ models/source/metadata.js | 5 +---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/config.example.js b/config.example.js index 170ae2b1..3457681b 100644 --- a/config.example.js +++ b/config.example.js @@ -37,6 +37,13 @@ module.exports = { path: path.join(__dirname, 'data/metadata.json'), }, + /* + * The current gemini database for Aracena et al. contains names as "Epi_realName_flu_xxx". + * We need to extract "realName" to make it easier for the rest (where "realName" corresponds to + * the metadata.json "donor" property). + */ + geminiSampleNameConverter: name => name.split('_')[1], // name => name + conditions: [ {id: "NI", name: "Non-infected"}, {id: "Flu", name: "Flu"}, diff --git a/models/source/metadata.js b/models/source/metadata.js index 2d48a5df..6ec48c0e 100644 --- a/models/source/metadata.js +++ b/models/source/metadata.js @@ -12,9 +12,6 @@ module.exports = { getTracks, } -const nameToRealName = name => - name.split('_')[1] - /** * @param {Object.} samples * @param {Object} peak @@ -27,7 +24,7 @@ function getTracks(samples, peak) { const samplesByRealName = Object.fromEntries( Object.entries(samples) - .map(([key, value]) => [nameToRealName(key), value])) + .map(([key, value]) => [config.source.metadata.geminiSampleNameConverter(key), value])) const sampleNames = Object.keys(samplesByRealName) const assay = peak.assay.toLowerCase() From 52123ec235d79393d9a0170223dc53b8ef813ff3 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 15:36:18 -0400 Subject: [PATCH 011/105] chore: dynamic conditions for p-values in db chore: pre-filter peaks by p-value --- config.example.js | 3 ++ helpers/associations.mjs | 1 - models/peaks.mjs | 20 +++++-------- models/schema.sql | 7 +++-- scripts/calculate-top-peaks.js | 4 +-- scripts/import-peaks.js | 53 +++++++++++++++++++++++----------- 6 files changed, 53 insertions(+), 35 deletions(-) delete mode 100644 helpers/associations.mjs diff --git a/config.example.js b/config.example.js index 3457681b..3f919882 100644 --- a/config.example.js +++ b/config.example.js @@ -44,6 +44,9 @@ module.exports = { */ geminiSampleNameConverter: name => name.split('_')[1], // name => name + // minimum p-value for a peak must be at or below this p-value for the peak to get included. + pValueMinThreshold: 0.05, + conditions: [ {id: "NI", name: "Non-infected"}, {id: "Flu", name: "Flu"}, diff --git a/helpers/associations.mjs b/helpers/associations.mjs deleted file mode 100644 index d49c8933..00000000 --- a/helpers/associations.mjs +++ /dev/null @@ -1 +0,0 @@ -export const P_VALUE_MAXIMUM = 0.05; diff --git a/models/peaks.mjs b/models/peaks.mjs index f6d82890..d3c2bdf5 100644 --- a/models/peaks.mjs +++ b/models/peaks.mjs @@ -8,7 +8,6 @@ import db from "./db.mjs"; import cache from "../helpers/cache.mjs"; import config from "../config.js"; import Gene from "./genes.mjs"; -import {P_VALUE_MAXIMUM} from "../helpers/associations.mjs"; import {CHROM_ORDER} from "../helpers/genome.mjs"; export default { @@ -25,8 +24,7 @@ export default { const peakSelect = ` p."id" AS "id", -p."valueNI" AS "valueNI", -p."valueFlu" AS "valueFlu", +p."values" AS "values", f."nat_id" AS "feature_str", f."chrom" AS "feature_chrom", f."start" AS "feature_start", @@ -42,8 +40,7 @@ g."name_norm" AS "gene" // noinspection JSUnresolvedVariable const normalizePeak = peak => peak && ({ id: peak.id, - valueNI: peak.valueNI, - valueFlu: peak.valueFlu, + values: peak.values, assay: peak.assay, gene: peak.gene, feature: { @@ -87,7 +84,6 @@ function query(chrom, position) { LEFT JOIN genes g ON f."gene" = g."id" -- having an associated gene is optional WHERE s."chrom" = $1 AND s."position" = $2 - AND LEAST(p."valueNI", p."valueFlu") <= ${P_VALUE_MAXIMUM} ORDER BY LEAST(p."valueNI", p."valueFlu") `, [chrom, position] @@ -104,8 +100,7 @@ function queryBySNP(rsID) { JOIN snps s ON p."snp" = s."id" LEFT JOIN genes g ON f."gene" = g."id" WHERE s."nat_id" = $1 - AND LEAST(p."valueNI", p."valueFlu") <= ${P_VALUE_MAXIMUM} - ORDER BY LEAST(p."valueNI", p."valueFlu") + ORDER BY (SELECT MIN(x) FROM unnest(p."values") AS x) `, [rsID] ).then(normalizePeaks); @@ -121,8 +116,7 @@ function queryByGene(gene) { JOIN snps s ON p."snp" = s."id" JOIN genes g ON f."gene" = g."id" -- no left join here; it's no longer optional to have a gene associated WHERE g."name_norm" = $1 - AND LEAST(p."valueNI", p."valueFlu") <= ${P_VALUE_MAXIMUM} - ORDER BY LEAST(p."valueNI", p."valueFlu") + ORDER BY (SELECT MIN(x) FROM unnest(p."values") AS x) `, [Gene.normalizeName(gene)] ).then(normalizePeaks); @@ -243,7 +237,7 @@ async function autocompleteWithDetail(query) { JOIN assays a ON f."assay" = a."id" JOIN genes g ON f."gene" = g."id" JOIN features_by_${by} fb ON fb."mostSignificantFeatureID" = p."id" - WHERE ${where} AND fb."minValueMin" <= ${P_VALUE_MAXIMUM} + WHERE ${where} ORDER BY fb."minValueMin" LIMIT 50 `, @@ -259,8 +253,8 @@ function topBinnedForAssayAndChrom(assay, chrom) { return db.findAll( ` SELECT - msp."pos_bin", - LEAST(p."valueNI", p."valueFlu") AS p_val, + msp."pos_bin", + (SELECT MIN(x) FROM unnest(p."values") AS x) AS p_val, s."nat_id" AS snp_nat_id, f."nat_id" AS feature_nat_id, g."name" AS gene_name diff --git a/models/schema.sql b/models/schema.sql index 44696506..412f2df7 100644 --- a/models/schema.sql +++ b/models/schema.sql @@ -76,8 +76,11 @@ create table if not exists peaks "id" serial primary key, "snp" integer not null, "feature" integer not null, - "valueNI" real not null, -- 32 bit floats are enough - "valueFlu" real not null, -- " + + -- values as array of treatments (in alphabetical order): [, ] + "values" real[] not null, + -- "valueNI" real not null, -- p-value; 32 bit floats are enough + -- "valueFlu" real not null, -- " -- "valueMin" real not null, -- for prioritizing items in search foreign key ("snp") references snps ("id"), diff --git a/scripts/calculate-top-peaks.js b/scripts/calculate-top-peaks.js index bd787b69..47386acb 100644 --- a/scripts/calculate-top-peaks.js +++ b/scripts/calculate-top-peaks.js @@ -36,8 +36,8 @@ const config = require("../config"); AND s2."position" >= $2 AND s2."position" <= $3 GROUP BY a_id - HAVING MIN(LEAST(p2."valueFlu", p2."valueNI")) <= $4 - ) j ON f.assay = j.a_id AND LEAST(p."valueFlu", p."valueNI") = j.p_min + HAVING MIN((SELECT MIN(x) FROM unnest(p2."values") AS x)) <= $4 + ) j ON f.assay = j.a_id AND (SELECT MIN(x) FROM unnest(p2."values") AS x) = j.p_min WHERE s."chrom" = $1 AND s."position" >= $2 AND s."position" <= $3 diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index c2bce173..4f025230 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -7,6 +7,8 @@ const copyFrom = require('pg-copy-streams').from; require('dotenv').config(); +const config = require('../config'); + // const chipmentationAssays = [ // 'H3K4me1', // 'H3K4me3', @@ -90,8 +92,8 @@ console.log("Loading peaks"); "id" serial primary key, "snp" varchar(20) not null, -- no FK until we insert to handle missing SNPs "feature" integer not null, -- if null, gene FK contains feature information - "valueNI" real not null, - "valueFlu" real not null + "values" real[] not null + -- "points" real[][] ) `); @@ -107,8 +109,8 @@ console.log("Loading peaks"); const copyToPeaksTable = async () => { const tn = "peaks_temp"; await client.query(` - INSERT INTO peaks ("snp", "feature", "valueNI", "valueFlu") - SELECT snps."id", pt."feature", pt."valueNI", pt."valueFlu" + INSERT INTO peaks ("snp", "feature", "values") -- , "points") + SELECT snps."id", pt."feature", pt."values" -- , pt."points" FROM ${tn} AS pt JOIN snps ON pt."snp" = snps."nat_id" `); await client.query(`TRUNCATE TABLE ${tn}`); @@ -137,19 +139,25 @@ console.log("Loading peaks"); COPY peaks_temp ( "snp", "feature", - "valueNI", - "valueFlu" + "values" + -- "points" + -- "valueFlu" -- valueMin, ) FROM STDIN NULL AS 'null' `)); // const transformNull = v => v === null ? "null" : v; const peakStreamPush = p => { + const pValues = p.values; + + // TODO: get points + // const points = []; + pgPeakCopyStream.write(Buffer.from([ p.snp, p.feature, - p.valueNI, - p.valueFlu, + `"{${pValues.join(",")}}"`, + // `"{${points.join(",")}}"`, ].join("\t") + "\n")); totalInserted++; }; @@ -207,6 +215,12 @@ console.log("Loading peaks"); const p = normalizePeak(row); + if (Math.min(...p.values) >= config.source.pValueMinThreshold) { + // Not included due to insignificance; skip this peak + parseStream.resume(); + return; + } + // Make sure SNP will exist in snps table if (!snpSet.has(p.snpArray[0])) { snpStreamPush(p.snpArray); @@ -274,9 +288,11 @@ console.log("Loading peaks"); (SELECT "id" FROM peaks WHERE "snp" = a."snp" - AND LEAST("valueNI", "valueFlu") = a."minValueMin" + AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" LIMIT 1) AS "mostSignificantFeatureID" - FROM (SELECT "snp", MIN(LEAST("valueNI", "valueFlu")) as "minValueMin", COUNT(*) AS "nFeatures" + FROM (SELECT "snp", + MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", + COUNT(*) AS "nFeatures" FROM peaks GROUP BY "snp") AS a ` @@ -293,9 +309,11 @@ console.log("Loading peaks"); (SELECT peaks."id" FROM peaks JOIN features ON peaks.feature = features.id WHERE features."gene" = a."gene" - AND LEAST("valueNI", "valueFlu") = a."minValueMin" + AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" LIMIT 1) AS "mostSignificantFeatureID" - FROM (SELECT f."gene", MIN(LEAST("valueNI", "valueFlu")) as "minValueMin", COUNT(*) AS "nFeatures" + FROM (SELECT f."gene", + MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", + COUNT(*) AS "nFeatures" FROM peaks AS p JOIN features AS f ON p.feature = f.id WHERE f."gene" IS NOT NULL GROUP BY f."gene") AS a @@ -349,11 +367,12 @@ console.log("Loading peaks"); // peak.position = +position; // delete peak.snp; - peak.valueNI = parseFloat(peak['pvalue.NI']); - peak.valueFlu = parseFloat(peak['pvalue.Flu']); - // peak.valueMin = Math.min(peak.valueNI, peak.valueFlu); - delete peak['pvalue.NI']; - delete peak['pvalue.Flu']; + peak.values = config.source.conditions.map(c => { + const k = `pvalue.${c.id}`; + const val = parseFloat(peak[k]); + delete peak[k]; + return val; + }); peak.assay = peak.feature_type; // Pre-process the assay name: add the 'Chipmentation ' From 40e2a8722a1e2a44854c36954d5cff35015d72e7 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 16 Jun 2023 15:51:52 -0400 Subject: [PATCH 012/105] refact: rewrite App component with more memoization --- client/src/components/App.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/client/src/components/App.js b/client/src/components/App.js index d52d103c..31cf5963 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {Navigate, Outlet, Route, Routes, useLocation, useNavigate} from "react-router-dom"; import {useDispatch, useSelector} from "react-redux"; @@ -8,7 +8,6 @@ import PeakResults from './PeakResults' import HelpModal from "./HelpModal"; import TermsModal from "./TermsModal"; -import {saveUser} from "../actions"; import ContactModal from "./ContactModal"; import AboutPage from "./pages/AboutPage"; import ProtectedPageContainer from "./pages/ProtectedPageContainer"; @@ -17,6 +16,8 @@ import ExplorePage from "./pages/ExplorePage"; import DatasetsPage from "./pages/DatasetsPage"; import FAQPage from "./pages/FAQPage"; +import {saveUser} from "../actions"; + const RoutedApp = () => { const location = useLocation(); @@ -32,21 +33,22 @@ const RoutedApp = () => { const chrom = useSelector(state => state.ui.chrom); const position = useSelector(state => state.ui.position); - const toggleHelp = () => setHelpModal(!helpModal); - const toggleContact = () => setContactModal(!contactModal); - const toggleTerms = () => setTermsModal(!termsModal); + const toggleHelp = useCallback(() => setHelpModal(!helpModal), [helpModal]); + const toggleContact = useCallback(() => setContactModal(!contactModal), [contactModal]); + const toggleTerms = useCallback(() => setTermsModal(!termsModal), [termsModal]); - const navigateAbout = () => navigate("/about"); - const navigateDatasets = () => navigate("/datasets"); - const navigateOverview = () => navigate("/overview"); // TODO: remember chrom and assay - const navigateExplore = () => { + const navigateAbout = useCallback(() => navigate("/about"), [navigate]); + const navigateDatasets = useCallback(() => navigate("/datasets"), [navigate]); + // TODO: remember chrom and assay: + const navigateOverview = useCallback(() => navigate("/overview"), [navigate]); + const navigateExplore = useCallback(() => { if (location.pathname.startsWith("/explore")) return; if (chrom && position) { navigate(`/explore/locus/${chrom}/${position}`); } else { navigate("/explore"); } - } + }, [location.pathname, chrom, position, navigate]); const navigateFAQ = () => navigate("/faq"); useEffect(() => { @@ -60,17 +62,19 @@ const RoutedApp = () => { } }, [userData]); + const termsOnAgree = useCallback(institution => { + if (userData.isLoaded) { + dispatch(saveUser({consentedToTerms: true, institution})); + } + }, [userData, dispatch]); + return (

{ - if (userData.isLoaded) { - dispatch(saveUser({consentedToTerms: true, institution})); - } - }} + onAgree={termsOnAgree} /> From 4432c8d7ee15d6856c418b05b7144b4716b8f59b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 13:37:33 -0400 Subject: [PATCH 013/105] chore: add input-files subdirectory gitkeeps --- input-files/matrices/.gitkeep | 0 input-files/qtls/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 input-files/matrices/.gitkeep create mode 100644 input-files/qtls/.gitkeep diff --git a/input-files/matrices/.gitkeep b/input-files/matrices/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/input-files/qtls/.gitkeep b/input-files/qtls/.gitkeep new file mode 100644 index 00000000..e69de29b From ea89b1da6a4187723f11edb66300867c23e3d55b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 13:46:50 -0400 Subject: [PATCH 014/105] feat: configurable static tracks file location --- .gitignore | 1 + config.example.js | 16 ++++++++++++---- {models => data}/ucsc.other-tracks.txt | 0 models/ucsc.mjs | 6 ++---- 4 files changed, 15 insertions(+), 8 deletions(-) rename {models => data}/ucsc.other-tracks.txt (100%) diff --git a/.gitignore b/.gitignore index 2ae0d14a..ef38413f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /data/* !/data/.gitkeep !/data/otherData +!/data/ucsc.other-tracks.txt /input-files/*.csv !/input-files/flu-infection-gene-peaks.csv /config.js diff --git a/config.example.js b/config.example.js index 3f919882..3022e2dd 100644 --- a/config.example.js +++ b/config.example.js @@ -20,12 +20,20 @@ const dataDirname = path.join(__dirname, './data') module.exports = { paths: { - data: dataDirname, - mergedTracks: `${dataDirname}/mergedTracks`, + data: dataDirname, + + // ====== Tracks ====== + // - merged tracks file location + mergedTracks: `${dataDirname}/mergedTracks`, + + // - static (unchanging) part of UCSC track hub to show alongside dynamic merged tracks + staticTracks: `${dataDirname}/ucsc.other-tracks.txt`, + // ==================== + // In production: - tracks: `${dataDirname}/tracks`, - gemini: `${dataDirname}/gemini.db`, + tracks: `${dataDirname}/tracks`, + gemini: `${dataDirname}/gemini.db`, // In development: //tracks: belugaDirname, //gemini: path.join(belugaDirname, 'WGS_VCFs/allSamples_WGS.gemini.db'), diff --git a/models/ucsc.other-tracks.txt b/data/ucsc.other-tracks.txt similarity index 100% rename from models/ucsc.other-tracks.txt rename to data/ucsc.other-tracks.txt diff --git a/models/ucsc.mjs b/models/ucsc.mjs index eacf0cea..0fd5fe4d 100644 --- a/models/ucsc.mjs +++ b/models/ucsc.mjs @@ -4,13 +4,11 @@ import Color from "color-js"; import fs from "fs"; -import path from "path"; -import {fileURLToPath} from "url"; +import config from "../config.js"; import unindent from "../helpers/unindent.mjs"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -export const otherTracks = fs.readFileSync(path.join(__dirname, './ucsc.other-tracks.txt')).toString(); +export const otherTracks = fs.readFileSync(config.paths.staticTracks).toString(); export default { otherTracks, From 400659a4f8b27c6d629504c7490e09d8a896912c Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 14:51:46 -0400 Subject: [PATCH 015/105] feat: if ucsc static tracks not provided, show nothing instead of erroring --- models/ucsc.mjs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/models/ucsc.mjs b/models/ucsc.mjs index 0fd5fe4d..dfa4cb5c 100644 --- a/models/ucsc.mjs +++ b/models/ucsc.mjs @@ -8,7 +8,9 @@ import fs from "fs"; import config from "../config.js"; import unindent from "../helpers/unindent.mjs"; -export const otherTracks = fs.readFileSync(config.paths.staticTracks).toString(); +const {staticTracks} = config.paths; + +export const otherTracks = fs.existsSync(staticTracks) ? fs.readFileSync(staticTracks).toString() : ""; export default { otherTracks, @@ -48,7 +50,7 @@ function generateTracks(mergedTracks) { container multiWig shortLabel ${parentName} longLabel ${parentName} - type bigWig + type ${TRACK_TYPE_BIGWIG} visibility full aggregate transparentOverlay showSubtrackColorOnUi on @@ -59,11 +61,10 @@ function generateTracks(mergedTracks) { autoScale on `) - const trackType = 'bigWig' - Object.entries(merged.output).forEach(([type, output]) => { - if (output === undefined) - return + if (output === undefined) { + return; + } const trackName = `${parentName}__${type}` const shortLabel = trackName @@ -72,7 +73,7 @@ function generateTracks(mergedTracks) { trackBlocks.push(indent(4, unindent` track ${trackName} - type ${trackType} + type ${TRACK_TYPE_BIGWIG} parent ${parentName} shortLabel ${shortLabel} bigDataUrl ${output.url} @@ -95,15 +96,11 @@ function generateTracks(mergedTracks) { priority 0.${i+mergedTracks.length+1} `)) - return ( - trackBlocks.join('\n\n') - // 2021-10-05: Disable these for now at the request of Alain - // - David L - + '\n\n' - + otherTracks - ) + return trackBlocks.join('\n\n') + (otherTracks ? `\n\n${otherTracks}` : ""); } +const TRACK_TYPE_BIGWIG = "bigWig"; + // Thanks to Google Charts const COLORS = { REF: [ From 022e64dbed880f211221e7f2e8e75254283638f3 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 14:54:51 -0400 Subject: [PATCH 016/105] fix: missed handling of dynamic conditions for plot generation --- models/tracks.mjs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 11352b0c..ab0dabed 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -158,19 +158,12 @@ function merge(tracks, session) { } function plot(tracksByCondition) { - const CONDITION_NI = "NI"; - const CONDITION_FLU = "Flu"; + const data = conditions.map(c => tracksByCondition[c.id] ? getDataFromValues(tracksByCondition[c.id]) : []); + const domains = data.map(d => getDomain(d)); - const niData = tracksByCondition[CONDITION_NI] ? getDataFromValues(tracksByCondition[CONDITION_NI]) : []; - const fluData = tracksByCondition[CONDITION_FLU] ? getDataFromValues(tracksByCondition[CONDITION_FLU]) : []; - - const niDomain = getDomain(niData) - const fluDomain = getDomain(fluData) - - return Promise.all([ - boxPlot({title: "Non-infected", data: niData, domain: niDomain}), - boxPlot({title: "Flu", data: fluData, domain: fluDomain, transform: "translate(350 0)"}), - ]).then(plots => + return Promise.all( + conditions.map((c, ci) => boxPlot({title: c.name, data: data[ci], domain: domains[ci]})) + ).then(plots => ` ${plots.join("")} ` From dacf74cfbdce5680236778211049a171c2cdddb2 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 14:56:18 -0400 Subject: [PATCH 017/105] refact: configurable qtls input file path --- config.example.js | 8 +++++++- scripts/import-peaks.js | 17 ++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/config.example.js b/config.example.js index 3022e2dd..22d9dd0b 100644 --- a/config.example.js +++ b/config.example.js @@ -6,7 +6,10 @@ const path = require('path') /* This is the application data directory */ -const dataDirname = path.join(__dirname, './data') +const dataDirname = path.join(__dirname, './data'); + +/* This is the input data directory */ +const inputFilesDirname = path.join(__dirname, './input-files'); /* For development: the `tracks` data is huge, so it makes * more sense to mount the files via `sshfs` instead of @@ -30,6 +33,9 @@ module.exports = { staticTracks: `${dataDirname}/ucsc.other-tracks.txt`, // ==================== + // Template for loading QTL files + qtlsTemplate: `${inputFilesDirname}/qtls/QTLs_complete_$ASSAY.csv`, + // In production: tracks: `${dataDirname}/tracks`, diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 4f025230..2c2ec01f 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -1,7 +1,6 @@ // noinspection SqlResolve const fs = require('fs'); -const path = require('path'); const parseCSV = require('csv-parse'); const copyFrom = require('pg-copy-streams').from; @@ -9,21 +8,17 @@ require('dotenv').config(); const config = require('../config'); -// const chipmentationAssays = [ -// 'H3K4me1', -// 'H3K4me3', -// 'H3K27ac', -// 'H3K27me3', -// ]; - -const datasetPaths = [ - 'RNAseq_symbol', +// TODO: configurable +const ASSAYS = [ + 'RNAseq', 'ATACseq', 'H3K4me1', 'H3K4me3', 'H3K27ac', 'H3K27me3', -].map(d => `${path.join(__dirname, '../input-files')}/QTLs_complete_${d}.csv`); +]; + +const datasetPaths = ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); console.log("Loading peaks"); From 477cc5abf5d0ba4b24b4d2f1e3e15c4532409fd4 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 15:00:02 -0400 Subject: [PATCH 018/105] refact: common constants for genotype states (ref/het/hom) --- helpers/genome.mjs | 12 ++++++++++ models/samples.mjs | 33 +++++++++++++++----------- models/tracks.mjs | 59 ++++++++++++++++++++-------------------------- models/ucsc.mjs | 9 +++---- 4 files changed, 62 insertions(+), 51 deletions(-) diff --git a/helpers/genome.mjs b/helpers/genome.mjs index 04dca588..a4901456 100644 --- a/helpers/genome.mjs +++ b/helpers/genome.mjs @@ -10,3 +10,15 @@ export const CHROM_ORDER = [...AUTOSOMES, ...SEX_CHROMS, "M"]; export const normalizeChrom = chrom => (chrom.indexOf("chr") === -1) ? `chr${chrom}` : chrom; + +export const GENOTYPE_STATE_REF = "REF"; +export const GENOTYPE_STATE_HET = "HET"; +export const GENOTYPE_STATE_HOM = "HOM"; + +export const GENOTYPE_STATES = [GENOTYPE_STATE_REF, GENOTYPE_STATE_HET, GENOTYPE_STATE_HOM]; + +export const GENOTYPE_STATE_NAMES = { + [GENOTYPE_STATE_REF]: "Hom Ref", + [GENOTYPE_STATE_HET]: "Het", + [GENOTYPE_STATE_HOM]: "Hom Alt", +}; diff --git a/models/samples.mjs b/models/samples.mjs index c81892f3..b8d8859d 100644 --- a/models/samples.mjs +++ b/models/samples.mjs @@ -9,7 +9,13 @@ import csvParse from "csv-parse/lib/sync.js"; import config from "../config.js"; import cache from "../helpers/cache.mjs"; -import {normalizeChrom} from "../helpers/genome.mjs"; +import { + GENOTYPE_STATE_HET, + GENOTYPE_STATE_HOM, + GENOTYPE_STATE_REF, + GENOTYPE_STATES, + normalizeChrom +} from "../helpers/genome.mjs"; const exec = command => new Promise((resolve, reject) => @@ -170,7 +176,9 @@ export function normalizeSamplesMap(samples) { const donor = key.slice(4) const value = res[key] const variant = variants.has(donor) - const type = !variant ? 'REF' : value.charAt(0) !== value.charAt(2) ? 'HET' : 'HOM' + const type = !variant + ? GENOTYPE_STATE_REF + : (value.charAt(0) !== value.charAt(2) ? GENOTYPE_STATE_HET : GENOTYPE_STATE_HOM) newRes.samples[donor] = { value, variant, type } } else { newRes[key] = res[key] @@ -181,21 +189,18 @@ export function normalizeSamplesMap(samples) { } export function getCounts(total, samples) { - const counts = { - REF: 0, - HET: 0, - HOM: 0, - } + const counts = Object.fromEntries(GENOTYPE_STATES.map(g => [g, 0])); samples.forEach(sample => { const [a, b] = splitSampleValue(sample) - if (a === sample.alt && b === sample.alt) - counts.HOM += 1 - else - counts.HET += 1 - }) - counts.REF = total - counts.HET - counts.HOM + if (a === sample.alt && b === sample.alt) { + counts[GENOTYPE_STATE_HOM] += 1; + } else { + counts[GENOTYPE_STATE_HET] += 1; + } + }); + counts[GENOTYPE_STATE_REF] = total - counts[GENOTYPE_STATE_HET] - counts[GENOTYPE_STATE_HOM]; - return counts + return counts; } export function splitSampleValue(sample) { diff --git a/models/tracks.mjs b/models/tracks.mjs index ab0dabed..e489d8df 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -17,7 +17,7 @@ import valueAt from "../helpers/value-at.mjs"; import config from "../config.js"; import Samples from "./samples.mjs"; import source from "./source/index.js"; -import {normalizeChrom} from "../helpers/genome.mjs"; +import {normalizeChrom, GENOTYPE_STATES, GENOTYPE_STATE_NAMES} from "../helpers/genome.mjs"; const exists = promisify(fs.exists); @@ -100,12 +100,12 @@ function group(tracks) { function calculate(tracksByCondition) { Object.keys(tracksByCondition).forEach(condition => { const tracksByType = tracksByCondition[condition] - tracksByType.HET = derive(tracksByType.HET || []) - tracksByType.HOM = derive(tracksByType.HOM || []) - tracksByType.REF = derive(tracksByType.REF || []) - }) + GENOTYPE_STATES.forEach(g => { + tracksByType[g] = derive(tracksByType[g] ?? []); + }); + }); - return tracksByCondition + return tracksByCondition; } const MERGE_WINDOW_EXTENT = 100000; // in bases @@ -117,24 +117,22 @@ function merge(tracks, session) { const mergeTracksByType = tracksByType => Promise.all( - [ - tracksByType.REF || [], - tracksByType.HET || [], - tracksByType.HOM || [] - ].map(async tracks => { - if (tracks.length === 0) { - return undefined; - } - - const filePaths = tracks.map(prop('path')) - const maxSize = await bigWigChromosomeLength(filePaths[0], chrom) - - return mergeFiles(filePaths, { - chrom, - start: Math.max(session.peak.feature.start - MERGE_WINDOW_EXTENT, 0), - end: Math.min(session.peak.feature.end + MERGE_WINDOW_EXTENT, maxSize), + GENOTYPE_STATES + .map(g => tracksByType[g] ?? []) + .map(async tracks => { + if (tracks.length === 0) { + return undefined; + } + + const filePaths = tracks.map(prop('path')) + const maxSize = await bigWigChromosomeLength(filePaths[0], chrom) + + return mergeFiles(filePaths, { + chrom, + start: Math.max(session.peak.feature.start - MERGE_WINDOW_EXTENT, 0), + end: Math.min(session.peak.feature.end + MERGE_WINDOW_EXTENT, maxSize), + }) }) - }) ) const promisedTracks = @@ -144,11 +142,7 @@ function merge(tracks, session) { assay: session.peak.assay, condition, tracks, - output: { - REF: output[0], - HET: output[1], - HOM: output[2], - }, + output: Object.fromEntries(GENOTYPE_STATES.map((g, gi) => [g, output[gi]])), }) ) ) @@ -174,11 +168,10 @@ function plot(tracksByCondition) { // Helpers function getDataFromValues(values) { - return [ - { name: 'Hom Ref', data: values.REF || [] }, - { name: 'Het', data: values.HET || [] }, - { name: 'Hom Alt', data: values.HOM || [] } - ] + return GENOTYPE_STATES.map(s => ({ + name: GENOTYPE_STATE_NAMES[s], + data: values[s] ?? [], + })); } function mergeFiles(paths, { chrom, start, end }) { diff --git a/models/ucsc.mjs b/models/ucsc.mjs index dfa4cb5c..60df5bfb 100644 --- a/models/ucsc.mjs +++ b/models/ucsc.mjs @@ -6,6 +6,7 @@ import Color from "color-js"; import fs from "fs"; import config from "../config.js"; +import {GENOTYPE_STATE_HET, GENOTYPE_STATE_HOM, GENOTYPE_STATE_REF, GENOTYPE_STATES} from "../helpers/genome.mjs"; import unindent from "../helpers/unindent.mjs"; const {staticTracks} = config.paths; @@ -85,7 +86,7 @@ function generateTracks(mergedTracks) { }) // Add legend 'tracks' - non-data tracks that show the REF/HET/HOM colours - trackBlocks.push(...["REF", "HET", "HOM"].map((t, i) => unindent` + trackBlocks.push(...GENOTYPE_STATES.map((t, i) => unindent` track ${t} shortLabel Legend: ${t} longLabel Legend: ${t} @@ -103,15 +104,15 @@ const TRACK_TYPE_BIGWIG = "bigWig"; // Thanks to Google Charts const COLORS = { - REF: [ + [GENOTYPE_STATE_REF]: [ '#87A8E8', '#3559A1' ], - HET: [ + [GENOTYPE_STATE_HET]: [ '#FFAD33', '#B77C25' ], - HOM: [ + [GENOTYPE_STATE_HOM]: [ '#E038E0', '#910591' ], From 4509c2701dd860f8c8859acd9ae221662939e3d1 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 15:50:31 -0400 Subject: [PATCH 019/105] lint --- models/samples.mjs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/models/samples.mjs b/models/samples.mjs index b8d8859d..be746f0a 100644 --- a/models/samples.mjs +++ b/models/samples.mjs @@ -36,7 +36,7 @@ export default { }; export function query(chrom, start, end = start + 1) { - const params = `--header --show-samples --format sampledetail` + const params = `--header --show-samples --format sampledetail`; const query = escape` SELECT chrom, start, end, ref, alt, (gts).(*) @@ -45,12 +45,12 @@ export function query(chrom, start, end = start + 1) { AND chrom = ${normalizeChrom(chrom)} AND start >= ${start} AND end <= ${end} - ` + `; return gemini(query, params) - .then(res => res.stdout) - .then(parseCSV) - .then(normalizeSamples) + .then(res => res.stdout) + .then(parseCSV) + .then(normalizeSamples); } export function queryMap(chrom, start, end = start + 1) { @@ -141,7 +141,7 @@ export function normalizeSamples(samples) { samples.forEach(sample => { - sample.value = sample['gts.' + sample.name] + sample.value = sample[`gts.${sample.name}`] for (let key in sample) { if (key.startsWith('gts.')) @@ -203,25 +203,27 @@ export function getCounts(total, samples) { return counts; } -export function splitSampleValue(sample) { - let parts = sample.value.split(/\||\//) +export const splitSampleValue = (sample) => { + const parts = sample.value.split(/\||\//) // Basic validation - if (parts.length !== 2) - throw new Error(`Invalid sample value: "${sample.value}" (sample ${sample.name})`) + if (parts.length !== 2) { + throw new Error(`Invalid sample value: "${sample.value}" (sample ${sample.name})`); + } - if (!parts.every(p => /^\w$/.test(p))) - console.log('Invalid sample value:', sample.value) + if (!parts.every(p => /^\w$/.test(p))) { + console.log('Invalid sample value:', sample.value); + } - return parts + return parts; } export function parseLines({ stdout }) { - return stdout.split('\n').filter(Boolean) + return stdout.split('\n').filter(Boolean); } export function parseCSV(string) { - return csvParse(string, { delimiter: '\t', columns: true }) + return csvParse(string, { delimiter: '\t', columns: true }); } export function escape(strings, ...args) { From d6efb576384c5a9a49843105dec05ff92a32d2de Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 15:52:53 -0400 Subject: [PATCH 020/105] chore: consistent rna-seq default spelling --- scripts/import-peaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 2c2ec01f..4216eeab 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -10,7 +10,7 @@ const config = require('../config'); // TODO: configurable const ASSAYS = [ - 'RNAseq', + 'RNA-seq', 'ATACseq', 'H3K4me1', 'H3K4me3', From 06366d2282ea2367af383b39d33ccdf3f7e72266 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 16:01:04 -0400 Subject: [PATCH 021/105] chore: prep work in tracks model for precomputed values --- models/tracks.mjs | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index e489d8df..1b09ca4e 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -17,6 +17,7 @@ import valueAt from "../helpers/value-at.mjs"; import config from "../config.js"; import Samples from "./samples.mjs"; import source from "./source/index.js"; +import {DEFAULT_CONDITIONS} from "../helpers/defaultValues.mjs"; import {normalizeChrom, GENOTYPE_STATES, GENOTYPE_STATE_NAMES} from "../helpers/genome.mjs"; const exists = promisify(fs.exists); @@ -39,8 +40,18 @@ const strandToView = { const groupByEthnicity = groupBy(prop("ethnicity")); const mapToData = map(prop("data")); +const {conditions} = config.source?.conditions ?? DEFAULT_CONDITIONS; +const CONDITION_IDS = conditions.map(c => c.id); + // Methods +const _makeTrackDonorLookupArray = tracks => { + const donorsByCondition = Object.fromEntries(CONDITION_IDS.map(c => [c, []])); + tracks.forEach(t => donorsByCondition[t.condition].push(t.donor)); + Object.values(donorsByCondition).forEach(v => v.sort()); // Sort donor IDs in place + return donorsByCondition; +}; + function get(peak) { const {snp: {chrom, position}} = peak; // FIXME remove position - 1 hack (needs clean data) @@ -48,7 +59,7 @@ function get(peak) { .then(info => source.getTracks(info.samples, peak)); } -async function values(peak) { +async function values(peak, usePrecomputed = false) { const k = `values:${peak.id}`; const chrom = normalizeChrom(peak.feature.chrom); @@ -60,18 +71,33 @@ async function values(peak) { const tracks = await get(peak); + let getValueForTrack = track => valueAt(track.path, { + chrom, + start: peak.feature.start, + end: peak.feature.end, + ...config.merge + }); + + if (usePrecomputed) { + const donorsByCondition = _makeTrackDonorLookupArray(tracks); + + // Replace getter function with one which extracts the precomputed point value. + getValueForTrack = track => { + const condIdx = CONDITION_IDS.indexOf(track.condition); + if (condIdx === -1) return Promise.resolve(undefined); + const pointIdx = donorsByCondition[condIdx].indexOf(track.donor); + if (pointIdx === -1) return Promise.resolve(undefined); + return Promise.resolve(peak.points?.[condIdx]?.[pointIdx]); + }; + } + const result = (await Promise.all(tracks.filter(track => // RNA-seq results are either forward or reverse strand; we only want tracks from the direction // of the selected peak (otherwise results will appear incorrectly, and we'll have 2x the # of // values we should in some cases.) track.assay !== "RNA-Seq" || track.view === strandToView[peak.feature.strand] ).map(track => - valueAt(track.path, { - chrom, - start: peak.feature.start, - end: peak.feature.end, - ...config.merge - }).then(value => (value === undefined ? undefined : { + getValueForTrack(track).then(value => (value === undefined ? undefined : { id: track.id, donor: track.donor, assay: track.assay, From 8a886825aff876cf295267424581cefa20333150 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 16:05:11 -0400 Subject: [PATCH 022/105] feat: accept precomputed query arg for values/plot track endpoints --- routes/tracks.mjs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/routes/tracks.mjs b/routes/tracks.mjs index 51917933..448ed216 100644 --- a/routes/tracks.mjs +++ b/routes/tracks.mjs @@ -13,20 +13,22 @@ router.use((_req, res, next) => { return next(); }); +const getUsePrecomputed = ({precomputed}) => precomputed === "1"; + router.post( '/values', ensureLogIn, ensureAgreedToTerms, - ({body: peak}, res) => { + ({query, body: peak}, res) => { // We're re-purposing this endpoint as basically a way to pre-cache any desired calculations, // without actually returning any values (since those are too close to re-identifiable.) // - David L, 2022-03-02 - Tracks.values(peak) + Tracks.values(peak, getUsePrecomputed(query)) .then(Tracks.group) .then(Tracks.calculate) .then(() => dataHandler(res)(undefined)) // Return an ok message without any data - .catch(errorHandler(res)) + .catch(errorHandler(res)); }); const svgToPng = data => @@ -38,7 +40,7 @@ router.get( '/plot/:peakID', ensureLogIn, ensureAgreedToTerms, - ({params}, res) => { + ({params, query}, res) => { // We go through a lot of headache to generate plots on the server side // (despite it being a nice modern front end) not because we're naive but // because we're trying to make the site more secure against @@ -55,7 +57,7 @@ router.get( }); } - Tracks.values(peak) + Tracks.values(peak, getUsePrecomputed(query)) .then(Tracks.group) .then(Tracks.calculate) .then(Tracks.plot) From 2613fe5f0bff8afbefbc961cd52c29ca6ea2277f Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 16:05:54 -0400 Subject: [PATCH 023/105] fix: use different cache key for precomputed track values --- models/tracks.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 1b09ca4e..c3425e66 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -60,7 +60,7 @@ function get(peak) { } async function values(peak, usePrecomputed = false) { - const k = `values:${peak.id}`; + const k = `values:${peak.id}${usePrecomputed ? ':pre' : ''}`; const chrom = normalizeChrom(peak.feature.chrom); await cache.open(); From 2a06590e1a1b861627e88d4be3f9d9c8750e3071 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 16:16:42 -0400 Subject: [PATCH 024/105] refact: use plot size constants everywhere; add constant for track value cache key --- models/tracks.mjs | 4 +++- routes/tracks.mjs | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index c3425e66..e57ca066 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -43,6 +43,8 @@ const mapToData = map(prop("data")); const {conditions} = config.source?.conditions ?? DEFAULT_CONDITIONS; const CONDITION_IDS = conditions.map(c => c.id); +const TRACK_VALUES_CACHE_EXPIRY = 60 * 60 * 24 * 180; // 180 days + // Methods const _makeTrackDonorLookupArray = tracks => { @@ -110,7 +112,7 @@ async function values(peak, usePrecomputed = false) { })) ))).filter(v => v !== undefined); - await cache.setJSON(k, result, 60 * 60 * 24 * 180); + await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); return result; } diff --git a/routes/tracks.mjs b/routes/tracks.mjs index 448ed216..b5a6ef98 100644 --- a/routes/tracks.mjs +++ b/routes/tracks.mjs @@ -5,6 +5,10 @@ import {ensureAgreedToTerms, ensureLogIn} from "../helpers/auth.mjs"; import {dataHandler, errorHandler, pngHandler} from "../helpers/handlers.mjs"; import Tracks from "../models/tracks.mjs"; import Peaks from "../models/peaks.mjs"; +import PLOT_SIZE from "../helpers/boxplot.js"; + +const SCALE_FACTOR = 2; +const PNG_DPI = 300; const router = express.Router(); @@ -32,8 +36,8 @@ router.post( }); const svgToPng = data => - sharp(Buffer.from(data), {density: 300}) - .resize(700 * 2, 350 * 2) + sharp(Buffer.from(data), {density: PNG_DPI}) + .resize(PLOT_SIZE * 2 * SCALE_FACTOR, PLOT_SIZE * SCALE_FACTOR) .toBuffer(); router.get( @@ -68,7 +72,7 @@ router.get( // Display error in PNG form svgToPng( - ` + ` Error while plotting: From 2d2fcf009c6c7e73d00a451014db5fc52c02af8b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 16:17:11 -0400 Subject: [PATCH 025/105] chore: add config path for point matrix file path template --- config.example.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config.example.js b/config.example.js index 22d9dd0b..66423f95 100644 --- a/config.example.js +++ b/config.example.js @@ -36,6 +36,11 @@ module.exports = { // Template for loading QTL files qtlsTemplate: `${inputFilesDirname}/qtls/QTLs_complete_$ASSAY.csv`, + // Template for loading pre-computed points for box plots + // Format: TSV with: + // - column headers of sample IDs ([ETHNICITY][NUMBER]_[CONDITION]) + // - row headers of features + pointTemplate: `${inputFilesDirname}/matrices/$ASSAY_batch.age.corrected_PCsreg.txt`, // In production: tracks: `${dataDirname}/tracks`, From 569f2bd84e258f167f5cdef571939d25e3f1a150 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 19 Jun 2023 16:32:18 -0400 Subject: [PATCH 026/105] chore: add points 2d array to schema for precomputed vals (opt) --- models/schema.sql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/models/schema.sql b/models/schema.sql index 412f2df7..fd360b66 100644 --- a/models/schema.sql +++ b/models/schema.sql @@ -83,6 +83,13 @@ create table if not exists peaks -- "valueFlu" real not null, -- " -- "valueMin" real not null, -- for prioritizing items in search + -- optional: + -- pre-computed, (presumably batch-corrected etc.) array of treatment-arrays of points for the peak to render in + -- the box plot, instead of pulling live from the bigWigs. treatments and samples MUST be in alphabetical order of + -- their IDs, eg [Flu: [, ..., ], NI: [...]] + -- if the array is NULL, points SHOULD be pulled from bigWigs instead + "points" real[][] default null, + foreign key ("snp") references snps ("id"), foreign key ("feature") references features ("id") ); From 3badc5e8480c611cb4f50be8ef4091c6cc2e9d04 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 10:33:50 -0400 Subject: [PATCH 027/105] chore!: change default config to use env vars for some directories/paths --- config.example.js | 31 ++++++++++++++++--------------- readme.md | 8 ++++++++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/config.example.js b/config.example.js index 66423f95..89116c53 100644 --- a/config.example.js +++ b/config.example.js @@ -2,7 +2,9 @@ * config.js */ -const path = require('path') +const path = require('path'); + +require('dotenv').config(); /* This is the application data directory */ @@ -11,27 +13,26 @@ const dataDirname = path.join(__dirname, './data'); /* This is the input data directory */ const inputFilesDirname = path.join(__dirname, './input-files'); +/* This is the storage volume path for Gemini genotypes and tracks (i.e., very large files / raw data) */ +const tracksDirname = process.env.VARWIG_TRACKS_DIR ?? '/flu-infection-data'; + /* For development: the `tracks` data is huge, so it makes * more sense to mount the files via `sshfs` instead of * copying them all. * You'd mount them with something like: * sshfs beluga.calculcanada.ca:~/projects/rrg-bourqueg-ad/C3G/projects/DavidB_varwig \ * ~/mnt/beluga-varwig-data - * Then you base directory would be: + * Then you base directory would be something like: + * VARWIG_TRACKS_DIR='/home/romgrk/mnt/beluga-varwig-data' + * VARWIG_GEMINI_DB='/home/romgrk/mnt/beluga-varwig-data/WGS_VCFs/allSamples_WGS.gemini.db' */ -// const belugaDirname = '/home/romgrk/mnt/beluga-varwig-data' module.exports = { paths: { data: dataDirname, - // ====== Tracks ====== - // - merged tracks file location - mergedTracks: `${dataDirname}/mergedTracks`, - - // - static (unchanging) part of UCSC track hub to show alongside dynamic merged tracks + // Static (unchanging) part of UCSC track hub to show alongside dynamic merged tracks staticTracks: `${dataDirname}/ucsc.other-tracks.txt`, - // ==================== // Template for loading QTL files qtlsTemplate: `${inputFilesDirname}/qtls/QTLs_complete_$ASSAY.csv`, @@ -42,12 +43,12 @@ module.exports = { // - row headers of features pointTemplate: `${inputFilesDirname}/matrices/$ASSAY_batch.age.corrected_PCsreg.txt`, - // In production: - tracks: `${dataDirname}/tracks`, - gemini: `${dataDirname}/gemini.db`, - // In development: - //tracks: belugaDirname, - //gemini: path.join(belugaDirname, 'WGS_VCFs/allSamples_WGS.gemini.db'), + // Merged tracks file location + mergedTracks: process.env.VARWIG_MERGED_TRACKS_DIR ?? path.join(dataDirname, 'mergedTracks'), + + // Locations of huge sensitive files + tracks: tracksDirname, + gemini: process.env.VARWIG_GEMINI_DB ?? path.join(tracksDirname, 'allSamples_WGS.gemini.db'), }, source: { diff --git a/readme.md b/readme.md index ada4a306..6f9c71fe 100644 --- a/readme.md +++ b/readme.md @@ -141,6 +141,7 @@ into a `.env` file and loaded at service start time). Here is an example, with secrets redacted, for a setup via Auth0: ```bash +# Auth configuration VARWIG_AUTH_SCOPE="openid profile" VARWIG_CLIENT_ID=some_client VARWIG_CLIENT_SECRET=some_secret @@ -149,7 +150,14 @@ VARWIG_ISSUER=https://dev-###.us.auth0.com/ VARWIG_AUTH_URL=https://dev-###.us.auth0.com/authorize VARWIG_TOKEN_URL=https://dev-###.us.auth0.com/oauth/token VARWIG_USERINFO_URL=https://dev-###.us.auth0.com/userinfo +# Other Varwig configuration VARWIG_BASE_URL=https://flu-infection.vhost38.genap.ca +# Database configuration +VARWIG_PG_CONNECTION=postgres://davidlougheed@localhost:5432/flu_infection_db +# Directories +VARWIG_MERGED_TRACKS_DIR=/flu-infection-data/mergedTracks +VARWIG_TRACKS_DIR=/flu-infection-data +VARWIG_GEMINI_DB=/flu-infection-data/allSamples_WGS.gemini.db ``` Note that trailing slashes are very important here; for example, a missing trailing slash for `VARWIG_ISSUER` will From 6385c594bbb3e059418e4d17a9cb86ca9715cbf7 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 10:34:05 -0400 Subject: [PATCH 028/105] docs: specify when notes are aracena-et-al specific --- readme.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 6f9c71fe..c0e17392 100644 --- a/readme.md +++ b/readme.md @@ -98,7 +98,7 @@ npm run install npm run build ``` -#### Development +#### Development (Aracena *et al.*-specific) To enable remote `gemini` execution: @@ -169,12 +169,11 @@ In production with CILogon, the auth scopes would be configured as follows: VARWIG_AUTH_SCOPE="openid email org.cilogon.userinfo" ``` +##### Note on current deployment for Aracena *et al.* + We use `pm2` to run multiple processes of the application at a time to handle more simultaneous requests. The `PM2_HOME` folder is set to `/home/dlougheed/.pm2` currently (sorry). -**Note that all code should be written with the assumption that multiple processes can run at a time.** -Thus, Redis/Postgres should generally be used for any cached/persistent data. - ## Architecture This is a standard express backend + react frontend application. Frontend files @@ -186,3 +185,6 @@ The [models/](./models) folder contains the functions to retrieve the actual dat depending on where it is. Some of it is in Postgres databases (genes, peaks, sessions); the tracks come from the `tracks/mergedTracks` folders configured previously, the variants (aka samples) data comes from `gemini`, and the UCSC track hubs are generated on the fly. + +**Note that all code should be written with the assumption that multiple processes can run at a time.** +Thus, Redis/Postgres should generally be used for any cached/persistent data. From cb90c014e4cbfc7eaddde0c79edd48fe55358927 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 10:51:33 -0400 Subject: [PATCH 029/105] feat: precomputed points import in import-peaks script --- scripts/import-peaks.js | 63 ++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 4216eeab..8f8a7417 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -8,6 +8,8 @@ require('dotenv').config(); const config = require('../config'); +const stripQuotes = str => str.replace("\"", "").trim(); + // TODO: configurable const ASSAYS = [ 'RNA-seq', @@ -20,6 +22,31 @@ const ASSAYS = [ const datasetPaths = ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); +const loadingPrecomputedPoints = !!config.paths.pointTemplate; +const precomputedPoints = {}; + +if (loadingPrecomputedPoints) { + console.log("Pre-loading precomputed point matrices"); + + ASSAYS.forEach(assay => { + const fc = fs.readFileSync(config.paths.pointTemplate.replace(/\$ASSAY/g, assay)) + .toString() + .split("\n"); + + const sortedSampleNamesAndIndices = fc[0] + .split("\t") + .map(s => stripQuotes(s)) + .filter(s => s !== "") + .map((s, si) => [s, si]) + .sort((a, b) => a[0].localeCompare(b[0])); + + precomputedPoints[assay] = Object.fromEntries(fc.slice(1).map(featureRow => [ + stripQuotes(featureRow[0]), + sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(featureRow[si + 1])), + ])); + }) +} + console.log("Loading peaks"); // -------------------------------------------------------------------------- @@ -87,8 +114,8 @@ console.log("Loading peaks"); "id" serial primary key, "snp" varchar(20) not null, -- no FK until we insert to handle missing SNPs "feature" integer not null, -- if null, gene FK contains feature information - "values" real[] not null - -- "points" real[][] + "values" real[] not null, + "points" real[][] ) `); @@ -104,8 +131,8 @@ console.log("Loading peaks"); const copyToPeaksTable = async () => { const tn = "peaks_temp"; await client.query(` - INSERT INTO peaks ("snp", "feature", "values") -- , "points") - SELECT snps."id", pt."feature", pt."values" -- , pt."points" + INSERT INTO peaks ("snp", "feature", "values", "points") + SELECT snps."id", pt."feature", pt."values", pt."points" FROM ${tn} AS pt JOIN snps ON pt."snp" = snps."nat_id" `); await client.query(`TRUNCATE TABLE ${tn}`); @@ -135,24 +162,29 @@ console.log("Loading peaks"); "snp", "feature", "values" - -- "points" - -- "valueFlu" - -- valueMin, + "points" ) FROM STDIN NULL AS 'null' `)); // const transformNull = v => v === null ? "null" : v; - const peakStreamPush = p => { - const pValues = p.values; - - // TODO: get points - // const points = []; + /* + * p: { + * assay, + * feature, + * featureStr, + * snp, + * snpArray, + * values, + * } + */ + const peakStreamPush = p => { + const points = loadingPrecomputedPoints ? precomputedPoints[p.featureStr] : null; pgPeakCopyStream.write(Buffer.from([ p.snp, p.feature, - `"{${pValues.join(",")}}"`, - // `"{${points.join(",")}}"`, + `"{${p.values.join(",")}}"`, + points !== null ? `"{${points.join(",")}}"` : "null", ].join("\t") + "\n")); totalInserted++; }; @@ -357,6 +389,9 @@ console.log("Loading peaks"); ] peak.snp = snpNaturalID; + // Save the string representation of the feature for later - we need it to look up precomputed points + peak.featureStr = peak.feature; + // const [chrom, position] = peak.snp.split('_'); // peak.chrom = chrom; // peak.position = +position; From 3477a3692801c7e0bf2a86717fcecae8a719aa79 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 11:02:14 -0400 Subject: [PATCH 030/105] chore: optionally load qtls location template from env var --- config.example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.example.js b/config.example.js index 89116c53..7c4c196f 100644 --- a/config.example.js +++ b/config.example.js @@ -35,7 +35,7 @@ module.exports = { staticTracks: `${dataDirname}/ucsc.other-tracks.txt`, // Template for loading QTL files - qtlsTemplate: `${inputFilesDirname}/qtls/QTLs_complete_$ASSAY.csv`, + qtlsTemplate: process.env.VARWIG_QTLS_TEMPLATE ?? `${inputFilesDirname}/qtls/QTLs_complete_$ASSAY.csv`, // Template for loading pre-computed points for box plots // Format: TSV with: From 58c50286706918ec25643b2fd945e9bea871f266 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 11:10:39 -0400 Subject: [PATCH 031/105] chore: add inputFilesDirname to config & use for gene files --- config.example.js | 2 ++ scripts/import-genes.js | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config.example.js b/config.example.js index 7c4c196f..bf96ace3 100644 --- a/config.example.js +++ b/config.example.js @@ -28,6 +28,8 @@ const tracksDirname = process.env.VARWIG_TRACKS_DIR ?? '/flu-infection-data'; */ module.exports = { + inputFilesDirname, + paths: { data: dataDirname, diff --git a/scripts/import-genes.js b/scripts/import-genes.js index 5808f106..1654a383 100644 --- a/scripts/import-genes.js +++ b/scripts/import-genes.js @@ -4,8 +4,10 @@ const parseCSVSync = require("csv-parse/lib/sync"); require('dotenv').config(); -const genesPath = path.join(__dirname, '../input-files/flu-infection-genes.txt'); -const genesFeaturesPath = path.join(__dirname, '../input-files/flu-infection-gene-peaks.csv'); +import config from "../config"; + +const genesPath = path.join(config.inputFilesDirname, 'flu-infection-genes.txt'); +const genesFeaturesPath = path.join(config.inputFilesDirname, 'flu-infection-gene-peaks.csv'); (async () => { const db = await import("../models/db.mjs"); From 5e37a2474bac688552f49015335d833f89c83935 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 11:11:05 -0400 Subject: [PATCH 032/105] fix: filter out undefined rows in feature matrix for precomputed points --- scripts/import-peaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 8f8a7417..3f67f84b 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -40,7 +40,7 @@ if (loadingPrecomputedPoints) { .map((s, si) => [s, si]) .sort((a, b) => a[0].localeCompare(b[0])); - precomputedPoints[assay] = Object.fromEntries(fc.slice(1).map(featureRow => [ + precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x).map(featureRow => [ stripQuotes(featureRow[0]), sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(featureRow[si + 1])), ])); From 3595924082c66fc50154954c1159710c4eff508a Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 11:12:01 -0400 Subject: [PATCH 033/105] fix: loading precomputed points error in script --- scripts/import-peaks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 3f67f84b..1d9b46c7 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -179,12 +179,12 @@ console.log("Loading peaks"); * } */ const peakStreamPush = p => { - const points = loadingPrecomputedPoints ? precomputedPoints[p.featureStr] : null; + const points = loadingPrecomputedPoints ? precomputedPoints[p.featureStr] : undefined; pgPeakCopyStream.write(Buffer.from([ p.snp, p.feature, `"{${p.values.join(",")}}"`, - points !== null ? `"{${points.join(",")}}"` : "null", + points !== undefined ? `"{${points.join(",")}}"` : "null", ].join("\t") + "\n")); totalInserted++; }; From 826acfed5ee185e59853a59e7790cf315736083e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 11:14:07 -0400 Subject: [PATCH 034/105] fix: sql error in import-peaks --- scripts/import-peaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 1d9b46c7..0ab2e8aa 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -161,7 +161,7 @@ console.log("Loading peaks"); COPY peaks_temp ( "snp", "feature", - "values" + "values", "points" ) FROM STDIN NULL AS 'null' `)); From f935865debd9059a967f75fd4a66bf5cae300f42 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 11:16:48 -0400 Subject: [PATCH 035/105] fix: sql error again? --- scripts/import-peaks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 0ab2e8aa..0f518c93 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -183,8 +183,8 @@ console.log("Loading peaks"); pgPeakCopyStream.write(Buffer.from([ p.snp, p.feature, - `"{${p.values.join(",")}}"`, - points !== undefined ? `"{${points.join(",")}}"` : "null", + `{${p.values.join(",")}}`, + points !== undefined ? `{${points.join(",")}}` : "null", ].join("\t") + "\n")); totalInserted++; }; From c0e1b65bac5688f0cfc7b0a4ad50e1ae154f5aa0 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:19:07 -0400 Subject: [PATCH 036/105] chore: one-off script for fixing symbols in rna matrix --- one-offs/rewrite_rnaseq_matrix_symbols.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 one-offs/rewrite_rnaseq_matrix_symbols.py diff --git a/one-offs/rewrite_rnaseq_matrix_symbols.py b/one-offs/rewrite_rnaseq_matrix_symbols.py new file mode 100644 index 00000000..ae6125a5 --- /dev/null +++ b/one-offs/rewrite_rnaseq_matrix_symbols.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import csv + +symbol_lookup = {} + +with open("/flu-infection-data/qtls/QTLs_complete_RNAseq_non_symbol.csv", "r") as nsf, \ + open("/flu-infection-data/qtls/QTLs_complete_RNA-seq.csv", "r") as sf: + nsr = csv.DictReader(nsf) + sr = csv.DictReader(sf) + + for nrow, srow in zip(nsr, sr): + symbol_lookup[nrow["feature"]] = srow["feature"] + +with open("/opt/varwig2/input-files/matrices/RNA-seq_batch.age.corrected_PCsreg.txt", "r") as rna_f, \ + open("/opt/varwig2/input-files/matrices/RNA-seq_batch.age.corrected_PCsreg_symbol.txt", "w") as rna_w: + rna_w.write(rna_f.read() + "\n") # copy header + for row in rna_f: + if not row.strip(): + continue + rd = row.strip().split("\t") + new_symbol = symbol_lookup.get(rd[0].strip("\"")) + if new_symbol is None: + print(f"could not find symbol for {rd[0]}") + continue + rna_w.write("\t".join((new_symbol, *rd[1:])) + "\n") From 90e0b83615de64b9c202701c107cc07f2c8df740 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:26:44 -0400 Subject: [PATCH 037/105] fix: issue with one-off --- one-offs/rewrite_rnaseq_matrix_symbols.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/one-offs/rewrite_rnaseq_matrix_symbols.py b/one-offs/rewrite_rnaseq_matrix_symbols.py index ae6125a5..4b448609 100644 --- a/one-offs/rewrite_rnaseq_matrix_symbols.py +++ b/one-offs/rewrite_rnaseq_matrix_symbols.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import csv +import tqdm symbol_lookup = {} @@ -9,13 +10,13 @@ nsr = csv.DictReader(nsf) sr = csv.DictReader(sf) - for nrow, srow in zip(nsr, sr): + for nrow, srow in tqdm(zip(nsr, sr)): symbol_lookup[nrow["feature"]] = srow["feature"] with open("/opt/varwig2/input-files/matrices/RNA-seq_batch.age.corrected_PCsreg.txt", "r") as rna_f, \ open("/opt/varwig2/input-files/matrices/RNA-seq_batch.age.corrected_PCsreg_symbol.txt", "w") as rna_w: - rna_w.write(rna_f.read() + "\n") # copy header - for row in rna_f: + rna_w.write(next(rna_f) + "\n") # copy header + for row in tqdm(rna_f): if not row.strip(): continue rd = row.strip().split("\t") @@ -23,4 +24,4 @@ if new_symbol is None: print(f"could not find symbol for {rd[0]}") continue - rna_w.write("\t".join((new_symbol, *rd[1:])) + "\n") + rna_w.write("\t".join((f"\"{new_symbol}\"", *rd[1:])) + "\n") From 9e8caf39488dfedfa4f87b1edc92b693ab3c444e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:27:41 -0400 Subject: [PATCH 038/105] oops --- one-offs/rewrite_rnaseq_matrix_symbols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/one-offs/rewrite_rnaseq_matrix_symbols.py b/one-offs/rewrite_rnaseq_matrix_symbols.py index 4b448609..78ef2806 100644 --- a/one-offs/rewrite_rnaseq_matrix_symbols.py +++ b/one-offs/rewrite_rnaseq_matrix_symbols.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import csv -import tqdm +from tqdm import tqdm symbol_lookup = {} From 2c589351ac48699cb9ac4999fcce023a5d5ff49b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:31:35 -0400 Subject: [PATCH 039/105] fix: preserve old symbols in rnaseq one-off if no new one found --- one-offs/rewrite_rnaseq_matrix_symbols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/one-offs/rewrite_rnaseq_matrix_symbols.py b/one-offs/rewrite_rnaseq_matrix_symbols.py index 78ef2806..fb155c7a 100644 --- a/one-offs/rewrite_rnaseq_matrix_symbols.py +++ b/one-offs/rewrite_rnaseq_matrix_symbols.py @@ -22,6 +22,6 @@ rd = row.strip().split("\t") new_symbol = symbol_lookup.get(rd[0].strip("\"")) if new_symbol is None: - print(f"could not find symbol for {rd[0]}") + rna_w.write("\t".join(rd) + "\n") continue rna_w.write("\t".join((f"\"{new_symbol}\"", *rd[1:])) + "\n") From 59ee98881bfd99aea8523e2c5a96d0288776e9a4 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:34:42 -0400 Subject: [PATCH 040/105] fix: one-off --- one-offs/rewrite_rnaseq_matrix_symbols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/one-offs/rewrite_rnaseq_matrix_symbols.py b/one-offs/rewrite_rnaseq_matrix_symbols.py index fb155c7a..28d5f6f0 100644 --- a/one-offs/rewrite_rnaseq_matrix_symbols.py +++ b/one-offs/rewrite_rnaseq_matrix_symbols.py @@ -15,7 +15,7 @@ with open("/opt/varwig2/input-files/matrices/RNA-seq_batch.age.corrected_PCsreg.txt", "r") as rna_f, \ open("/opt/varwig2/input-files/matrices/RNA-seq_batch.age.corrected_PCsreg_symbol.txt", "w") as rna_w: - rna_w.write(next(rna_f) + "\n") # copy header + rna_w.write(next(rna_f)) # copy header for row in tqdm(rna_f): if not row.strip(): continue From 8306e702a9afecf9187defa32f56dd11800ab3c9 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:49:53 -0400 Subject: [PATCH 041/105] fix: importing precomputed points --- scripts/import-peaks.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 0f518c93..ee467622 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -40,10 +40,14 @@ if (loadingPrecomputedPoints) { .map((s, si) => [s, si]) .sort((a, b) => a[0].localeCompare(b[0])); - precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x).map(featureRow => [ - stripQuotes(featureRow[0]), - sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(featureRow[si + 1])), - ])); + precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x) + .map(featureRow => { + const rd = featureRow.split("\t"); + return [ + stripQuotes(rd[0]), + sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(rd[si + 1])), + ]; + })); }) } From 583ed3894f6a2ae317aeee3327619ef2eac09380 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:52:24 -0400 Subject: [PATCH 042/105] fix(scripts): import-peaks strip quote --- scripts/import-peaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index ee467622..608a54ea 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -8,7 +8,7 @@ require('dotenv').config(); const config = require('../config'); -const stripQuotes = str => str.replace("\"", "").trim(); +const stripQuotes = str => str.replace(/"/g, "").trim(); // TODO: configurable const ASSAYS = [ From f935aede51486b4ceed3969dceaec8c88d46290c Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 14:58:18 -0400 Subject: [PATCH 043/105] fix(scripts): import-peaks points --- scripts/import-peaks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 608a54ea..ba781f82 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -48,7 +48,7 @@ if (loadingPrecomputedPoints) { sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(rd[si + 1])), ]; })); - }) + }); } console.log("Loading peaks"); @@ -183,7 +183,7 @@ console.log("Loading peaks"); * } */ const peakStreamPush = p => { - const points = loadingPrecomputedPoints ? precomputedPoints[p.featureStr] : undefined; + const points = loadingPrecomputedPoints ? precomputedPoints[p.assay][p.featureStr] : undefined; pgPeakCopyStream.write(Buffer.from([ p.snp, p.feature, From 555e4f426c5875858cbb55ee14724df92237ee7d Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 15:24:47 -0400 Subject: [PATCH 044/105] refact!: move points to features --- models/peaks.mjs | 2 + models/schema.sql | 14 +++---- models/tracks.mjs | 2 +- scripts/_common.js | 53 +++++++++++++++++++++++++ scripts/import-genes.js | 17 ++++++--- scripts/import-peaks.js | 85 ++++++++++++----------------------------- 6 files changed, 100 insertions(+), 73 deletions(-) create mode 100644 scripts/_common.js diff --git a/models/peaks.mjs b/models/peaks.mjs index d3c2bdf5..d4a57ca5 100644 --- a/models/peaks.mjs +++ b/models/peaks.mjs @@ -30,6 +30,7 @@ f."chrom" AS "feature_chrom", f."start" AS "feature_start", f."end" AS "feature_end", f."strand" AS "feature_strand", +f."points" AS "feature_points", s."nat_id" AS "snp_id", s."chrom" AS "snp_chrom", s."position" AS "snp_position", @@ -49,6 +50,7 @@ const normalizePeak = peak => peak && ({ start: peak.feature_start, end: peak.feature_end, strand: peak.feature_strand, + points: peak.feature_points, }, snp: { id: peak.snp_id, diff --git a/models/schema.sql b/models/schema.sql index fd360b66..b180f0de 100644 --- a/models/schema.sql +++ b/models/schema.sql @@ -56,6 +56,13 @@ create table if not exists features "assay" smallint not null, -- put assay next to strand to keep tuple smaller "gene" integer, + -- optional: + -- pre-computed, (presumably batch-corrected etc.) array of treatment-arrays of points for the peak to render in + -- the box plot, instead of pulling live from the bigWigs. treatments and samples MUST be in alphabetical order of + -- their IDs, eg [Flu: [, ..., ], NI: [...]] + -- if the array is NULL, points SHOULD be pulled from bigWigs instead + "points" real[][] default null, + foreign key ("assay") references assays ("id"), foreign key ("gene") references genes ("id") ); @@ -83,13 +90,6 @@ create table if not exists peaks -- "valueFlu" real not null, -- " -- "valueMin" real not null, -- for prioritizing items in search - -- optional: - -- pre-computed, (presumably batch-corrected etc.) array of treatment-arrays of points for the peak to render in - -- the box plot, instead of pulling live from the bigWigs. treatments and samples MUST be in alphabetical order of - -- their IDs, eg [Flu: [, ..., ], NI: [...]] - -- if the array is NULL, points SHOULD be pulled from bigWigs instead - "points" real[][] default null, - foreign key ("snp") references snps ("id"), foreign key ("feature") references features ("id") ); diff --git a/models/tracks.mjs b/models/tracks.mjs index e57ca066..de7884cc 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -89,7 +89,7 @@ async function values(peak, usePrecomputed = false) { if (condIdx === -1) return Promise.resolve(undefined); const pointIdx = donorsByCondition[condIdx].indexOf(track.donor); if (pointIdx === -1) return Promise.resolve(undefined); - return Promise.resolve(peak.points?.[condIdx]?.[pointIdx]); + return Promise.resolve(peak.feature.points?.[condIdx]?.[pointIdx]); }; } diff --git a/scripts/_common.js b/scripts/_common.js new file mode 100644 index 00000000..8de3eaa8 --- /dev/null +++ b/scripts/_common.js @@ -0,0 +1,53 @@ +import fs from "fs"; + +require('dotenv').config(); + +import config from "../config"; + +const stripQuotes = str => str.replace(/"/g, "").trim(); + +// TODO: configurable +const ASSAYS = [ + 'RNA-seq', + 'ATACseq', + 'H3K4me1', + 'H3K4me3', + 'H3K27ac', + 'H3K27me3', +]; + +const loadingPrecomputedPoints = !!config.paths.pointTemplate; +const precomputedPoints = {}; + +if (loadingPrecomputedPoints) { + console.log("Pre-loading precomputed point matrices"); + + ASSAYS.forEach(assay => { + const fc = fs.readFileSync(config.paths.pointTemplate.replace(/\$ASSAY/g, assay)) + .toString() + .split("\n"); + + const sortedSampleNamesAndIndices = fc[0] + .split("\t") + .map(s => stripQuotes(s)) + .filter(s => s !== "") + .map((s, si) => [s, si]) + .sort((a, b) => a[0].localeCompare(b[0])); + + precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x) + .map(featureRow => { + const rd = featureRow.split("\t"); + return [ + stripQuotes(rd[0]), + sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(rd[si + 1])), + ]; + })); + }); +} + +module.exports = { + stripQuotes, + ASSAYS, + loadingPrecomputedPoints, + precomputedPoints, +}; diff --git a/scripts/import-genes.js b/scripts/import-genes.js index 1654a383..19f6d670 100644 --- a/scripts/import-genes.js +++ b/scripts/import-genes.js @@ -4,18 +4,23 @@ const parseCSVSync = require("csv-parse/lib/sync"); require('dotenv').config(); -import config from "../config"; +const config = require("../config"); +const common = require("./_common"); + +const ASSAY_NAME_RNASEQ = "RNA-seq"; const genesPath = path.join(config.inputFilesDirname, 'flu-infection-genes.txt'); const genesFeaturesPath = path.join(config.inputFilesDirname, 'flu-infection-gene-peaks.csv'); +const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; + (async () => { const db = await import("../models/db.mjs"); const Gene = await import('../models/genes.mjs'); const assaysByName = Object.fromEntries((await db.findAll("SELECT * FROM assays")).map(r => [r.name, r.id])); - const rnaSeq = assaysByName["RNA-seq"]; + const rnaSeq = assaysByName[ASSAY_NAME_RNASEQ]; const parseGene = line => { const fields = line.trim().split('\t'); @@ -45,7 +50,8 @@ const genesFeaturesPath = path.join(config.inputFilesDirname, 'flu-infection-gen const start = +fields[2]; const end = +fields[3]; const strand = fields[4]; - const gene = genesByNormName[Gene.normalizeName(fields[0])]; + const geneNameNorm = Gene.normalizeName(fields[0]); + const gene = genesByNormName[geneNameNorm]; if (!gene) return []; return [[ `${chrom}_${start}_${end}_${strand}:${rnaSeq}`, @@ -55,13 +61,14 @@ const genesFeaturesPath = path.join(config.inputFilesDirname, 'flu-infection-gen strand, // strand rnaSeq, gene, + rnaSeqPrecomputed[fields[0]] ?? rnaSeqPrecomputed[geneNameNorm], // Pre-computed points ]]; }; await db.insertMany( ` - INSERT INTO features ("nat_id", "chrom", "start", "end", "strand", "assay", "gene") - VALUES ($1, $2, $3, $4, $5, $6, $7) + INSERT INTO features ("nat_id", "chrom", "start", "end", "strand", "assay", "gene", "points") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT DO NOTHING `, geneLines.flatMap(parseGeneFeature), diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index ba781f82..6d02a22f 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -7,49 +7,9 @@ const copyFrom = require('pg-copy-streams').from; require('dotenv').config(); const config = require('../config'); +const common = require('./_common'); -const stripQuotes = str => str.replace(/"/g, "").trim(); - -// TODO: configurable -const ASSAYS = [ - 'RNA-seq', - 'ATACseq', - 'H3K4me1', - 'H3K4me3', - 'H3K27ac', - 'H3K27me3', -]; - -const datasetPaths = ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); - -const loadingPrecomputedPoints = !!config.paths.pointTemplate; -const precomputedPoints = {}; - -if (loadingPrecomputedPoints) { - console.log("Pre-loading precomputed point matrices"); - - ASSAYS.forEach(assay => { - const fc = fs.readFileSync(config.paths.pointTemplate.replace(/\$ASSAY/g, assay)) - .toString() - .split("\n"); - - const sortedSampleNamesAndIndices = fc[0] - .split("\t") - .map(s => stripQuotes(s)) - .filter(s => s !== "") - .map((s, si) => [s, si]) - .sort((a, b) => a[0].localeCompare(b[0])); - - precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x) - .map(featureRow => { - const rd = featureRow.split("\t"); - return [ - stripQuotes(rd[0]), - sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(rd[si + 1])), - ]; - })); - }); -} +const datasetPaths = common.ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); console.log("Loading peaks"); @@ -83,21 +43,34 @@ console.log("Loading peaks"); ); process.stdout.write(" done.\n"); - const getFeatureIDOrCreate = async (feature, assay) => { - const naturalKey = `${feature}:${assay}`; + const getFeatureIDOrCreate = async (feature, assayStr, assayId) => { + const naturalKey = `${feature}:${assayId}`; if (featureCache.hasOwnProperty(naturalKey)) { return featureCache[naturalKey]; } + const points = common.loadingPrecomputedPoints + ? (common.precomputedPoints[assayStr][feature] ?? null) + : null; + const fs = feature.split("_"); // TODO: this will break with chrUn const res = await db.insert( ` - INSERT INTO features ("nat_id", "assay", "chrom", "start", "end" ${fs.length > 3 ? ', "strand"' : ''} ) - VALUES ($1, $2, $3, $4, $5 ${fs.length > 3 ? ', $6' : ''} ) + INSERT INTO features + ("nat_id", "assay", "chrom", "start", "end", "points" ${fs.length > 3 ? ', "strand"' : ''} ) + VALUES ($1, $2, $3, $4, $5, $6 ${fs.length > 3 ? ', $7' : ''} ) RETURNING id - `, [naturalKey, assay, fs[0], parseInt(fs[1]), parseInt(fs[2]), ...(fs.length > 3 ? [fs[3]] : [])]); + `, [ + naturalKey, + assayId, + fs[0], + parseInt(fs[1]), + parseInt(fs[2]), + points, + ...(fs.length > 3 ? [fs[3]] : []), + ]); featureCache[naturalKey] = res.rows[0].id; return featureCache[naturalKey]; }; @@ -118,8 +91,7 @@ console.log("Loading peaks"); "id" serial primary key, "snp" varchar(20) not null, -- no FK until we insert to handle missing SNPs "feature" integer not null, -- if null, gene FK contains feature information - "values" real[] not null, - "points" real[][] + "values" real[] not null ) `); @@ -135,8 +107,8 @@ console.log("Loading peaks"); const copyToPeaksTable = async () => { const tn = "peaks_temp"; await client.query(` - INSERT INTO peaks ("snp", "feature", "values", "points") - SELECT snps."id", pt."feature", pt."values", pt."points" + INSERT INTO peaks ("snp", "feature", "values") + SELECT snps."id", pt."feature", pt."values" FROM ${tn} AS pt JOIN snps ON pt."snp" = snps."nat_id" `); await client.query(`TRUNCATE TABLE ${tn}`); @@ -165,8 +137,7 @@ console.log("Loading peaks"); COPY peaks_temp ( "snp", "feature", - "values", - "points" + "values" ) FROM STDIN NULL AS 'null' `)); @@ -176,19 +147,16 @@ console.log("Loading peaks"); * p: { * assay, * feature, - * featureStr, * snp, * snpArray, * values, * } */ const peakStreamPush = p => { - const points = loadingPrecomputedPoints ? precomputedPoints[p.assay][p.featureStr] : undefined; pgPeakCopyStream.write(Buffer.from([ p.snp, p.feature, `{${p.values.join(",")}}`, - points !== undefined ? `{${points.join(",")}}` : "null", ].join("\t") + "\n")); totalInserted++; }; @@ -263,7 +231,7 @@ console.log("Loading peaks"); // Get the already-created (by import-genes.js) feature if it exists, // or make a new one. Pre-created features are associated with genes if // specified in the flu-infection-gene-peaks.csv file. - getFeatureIDOrCreate(p.feature.slice(3), assays[p.assay].id).then(fID => { + getFeatureIDOrCreate(p.feature.slice(3), p.assay, assays[p.assay].id).then(fID => { p.feature = fID; peakStreamPush(p); parseStream.resume(); @@ -393,9 +361,6 @@ console.log("Loading peaks"); ] peak.snp = snpNaturalID; - // Save the string representation of the feature for later - we need it to look up precomputed points - peak.featureStr = peak.feature; - // const [chrom, position] = peak.snp.split('_'); // peak.chrom = chrom; // peak.position = +position; From 55e7e941eca9cd4efbcbd64b80b4345053feec4d Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 15:28:03 -0400 Subject: [PATCH 045/105] fix: import in scripts common --- scripts/_common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/_common.js b/scripts/_common.js index 8de3eaa8..6a190534 100644 --- a/scripts/_common.js +++ b/scripts/_common.js @@ -1,4 +1,4 @@ -import fs from "fs"; +const fs = require('fs'); require('dotenv').config(); From 6d7155f50d7a62372cfb6e8708368ca70fe44b46 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 15:28:36 -0400 Subject: [PATCH 046/105] oops --- scripts/_common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/_common.js b/scripts/_common.js index 6a190534..a59d41d7 100644 --- a/scripts/_common.js +++ b/scripts/_common.js @@ -2,7 +2,7 @@ const fs = require('fs'); require('dotenv').config(); -import config from "../config"; +const config = require("../config"); const stripQuotes = str => str.replace(/"/g, "").trim(); From ac98c0269b82c72b7a465d7ed7ebb10ade8ac720 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 20 Jun 2023 15:38:02 -0400 Subject: [PATCH 047/105] fix: consistent atac-seq assay name --- scripts/_common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/_common.js b/scripts/_common.js index a59d41d7..0e62bc4e 100644 --- a/scripts/_common.js +++ b/scripts/_common.js @@ -9,7 +9,7 @@ const stripQuotes = str => str.replace(/"/g, "").trim(); // TODO: configurable const ASSAYS = [ 'RNA-seq', - 'ATACseq', + 'ATAC-seq', 'H3K4me1', 'H3K4me3', 'H3K27ac', From c555189bb71bd48cd95e24e3d67900661e6e6145 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 10:28:36 -0400 Subject: [PATCH 048/105] feat(client): usePrecomputed ui/plot state + checkbox --- client/src/actions.js | 39 +++++++++++++------------- client/src/components/PeakAssay.js | 42 +++++++++++++++++++--------- client/src/components/PeakBoxplot.js | 25 ++++++++++------- client/src/constants/ActionTypes.js | 37 ++++++++++++------------ client/src/reducers.js | 5 ++++ 5 files changed, 88 insertions(+), 60 deletions(-) diff --git a/client/src/actions.js b/client/src/actions.js index 307c35b4..73e726ae 100644 --- a/client/src/actions.js +++ b/client/src/actions.js @@ -6,25 +6,26 @@ import * as k from './constants/ActionTypes.js' import {BASE_URL} from "./constants/app"; import {constructUCSCUrl} from "./helpers/ucsc"; -export const setChrom = createAction(k.SET_CHROM); -export const setPosition = createAction(k.SET_POSITION); -export const setOverviewChrom = createAction(k.SET_OVERVIEW_CHROM); -export const setOverviewAssay = createAction(k.SET_OVERVIEW_ASSAY); -export const handleError = createAction(k.HANDLE_ERROR); - -export const assays = createFetchActions(k.ASSAYS); -export const samples = createFetchActions(k.SAMPLES); -export const chroms = createFetchActions(k.CHROMS); -export const positions = createFetchActions(k.POSITIONS); -export const values = createFetchActions(k.VALUES); -export const peaks = createFetchActions(k.PEAKS); -export const assembly = createFetchActions(k.ASSEMBLY); -export const conditions = createFetchActions(k.CONDITIONS); -export const ethnicities = createFetchActions(k.ETHNICITIES); -export const overviewConfig = createFetchActions(k.OVERVIEW_CONFIG); -export const manhattanData = createFetchActions(k.MANHATTAN_DATA); -export const user = createFetchActions(k.USER); -export const messages = createFetchActions(k.MESSAGES); +export const setChrom = createAction(k.SET_CHROM); +export const setPosition = createAction(k.SET_POSITION); +export const setOverviewChrom = createAction(k.SET_OVERVIEW_CHROM); +export const setOverviewAssay = createAction(k.SET_OVERVIEW_ASSAY); +export const setUsePrecomputed = createAction(k.SET_USE_PRECOMPUTED); +export const handleError = createAction(k.HANDLE_ERROR); + +export const assays = createFetchActions(k.ASSAYS); +export const samples = createFetchActions(k.SAMPLES); +export const chroms = createFetchActions(k.CHROMS); +export const positions = createFetchActions(k.POSITIONS); +export const values = createFetchActions(k.VALUES); +export const peaks = createFetchActions(k.PEAKS); +export const assembly = createFetchActions(k.ASSEMBLY); +export const conditions = createFetchActions(k.CONDITIONS); +export const ethnicities = createFetchActions(k.ETHNICITIES); +export const overviewConfig = createFetchActions(k.OVERVIEW_CONFIG); +export const manhattanData = createFetchActions(k.MANHATTAN_DATA); +export const user = createFetchActions(k.USER); +export const messages = createFetchActions(k.MESSAGES); export const fetchAssays = createFetchFunction(api.fetchAssays, assays); // export const fetchChroms = createFetchFunction(api.fetchChroms, chroms); diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 2f085940..680f157e 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -1,20 +1,21 @@ -import {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {useDispatch, useSelector} from 'react-redux'; import { Button, + ButtonGroup, Container, Col, + Input, + Label, Row, Table, - ButtonGroup, - Input, Tooltip, } from 'reactstrap' import {useTable, usePagination, useSortBy} from "react-table"; import Icon from "./Icon"; import PeakBoxplot from "./PeakBoxplot"; -import {cacheValues, mergeTracks} from "../actions"; +import {cacheValues, mergeTracks, setUsePrecomputed} from "../actions"; const PAGE_SIZES = [10, 20, 30, 40, 50]; @@ -23,6 +24,11 @@ const PAGE_SIZES = [10, 20, 30, 40, 50]; const PeakAssay = ({peaks}) => { const dispatch = useDispatch(); + const usePrecomputed = useSelector(state => state.ui.usePrecomputed); + const setPrecomputed = useCallback( + event => dispatch(setUsePrecomputed(event.currentTarget.checked)), + [dispatch]); + const [selectedPeak, setSelectedPeak] = useState(undefined); useEffect(() => { @@ -32,8 +38,8 @@ const PeakAssay = ({peaks}) => { setSelectedPeak(p ? p.id : undefined); }, [peaks]) - const onChangeFeature = p => setSelectedPeak(p.id); - const onOpenTracks = p => dispatch(mergeTracks(p)); + const onChangeFeature = useCallback(p => setSelectedPeak(p.id), []); + const onOpenTracks = useCallback(p => dispatch(mergeTracks(p)), [dispatch]); const selectedPeakData = peaks.find(p => p.id === selectedPeak); @@ -60,7 +66,7 @@ const PeakAssay = ({peaks}) => { return ( - + { onOpenTracks={onOpenTracks} /> - + + + + { state: { pageIndex, pageSize }, } = tableInstance; + const onGotoPage = useCallback(e => { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + gotoPage(page) + }, [gotoPage]); + + const onSelectPage = useCallback(e => setPageSize(Number(e.target.value)), []); + return <>
{ type="number" disabled={pageOptions.length === 1} defaultValue={pageIndex + 1} - onChange={e => { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - gotoPage(page) - }} + onChange={onGotoPage} style={{ width: "100px", display: "inline-block" }} /> setPageSize(Number(e.target.value))} + onChange={onSelectPage} style={{width: "120px", marginLeft: "1em"}} > {PAGE_SIZES.map(pageSize => ( diff --git a/client/src/components/PeakBoxplot.js b/client/src/components/PeakBoxplot.js index f3a3e18d..f8234c0d 100644 --- a/client/src/components/PeakBoxplot.js +++ b/client/src/components/PeakBoxplot.js @@ -1,19 +1,23 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import {useSelector} from "react-redux"; function PeakBoxplot({ title, peak, /*values = defaultValues*/ }) { + const usePrecomputed = useSelector(state => state.ui.usePrecomputed); + const ethnicities = useState(state => state.ethnicities.list); const [loaded, setLoaded] = useState(false); - const prevPeakRef = useRef(); + const prevPeakImgRef = useRef(""); + + const peakImg = `${process.env.PUBLIC_URL}/api/tracks/plot/${peak?.id}?precomputed=${Number(usePrecomputed)}`; useEffect(() => { - if (prevPeakRef.current !== peak?.id) { + if (prevPeakImgRef.current !== peakImg) { setLoaded(false); - prevPeakRef.current = peak?.id; + prevPeakImgRef.current = peakImg; } - }, [peak]); + }, [peakImg]); - const peakImg = `${process.env.PUBLIC_URL}/api/tracks/plot/${peak?.id}`; const peakImgStyle = useMemo(() => ({ width: "100%", height: "auto", @@ -50,11 +54,12 @@ function PeakBoxplot({ title, peak, /*values = defaultValues*/ }) { ))}
-

- Box plots are generated from normalised signals (read count per base pair per 10 million reads), without any - batch correction, whereas p-values are calculated from age-regressed, batch-corrected - signal values. The p-values thus may not precisely match the distributions visible in the box plots. -

+ {!usePrecomputed &&

+ When not using precomputed values, box plots are generated from normalised signals (read count per base pair + per 10 million reads), without any batch correction, whereas p-values are calculated from the + age-regressed, batch-corrected signal values. The p-values thus may not precisely match the + distributions visible in the box plots. +

}
) diff --git a/client/src/constants/ActionTypes.js b/client/src/constants/ActionTypes.js index a709b437..ae6365cb 100644 --- a/client/src/constants/ActionTypes.js +++ b/client/src/constants/ActionTypes.js @@ -1,22 +1,23 @@ -export const SET_CHROM = 'SET_CHROM'; -export const SET_POSITION = 'SET_POSITION'; -export const SET_OVERVIEW_CHROM = 'SET_OVERVIEW_CHROM'; -export const SET_OVERVIEW_ASSAY = 'SET_OVERVIEW_ASSAY'; -export const HANDLE_ERROR = 'HANDLE_ERROR'; +export const SET_CHROM = 'SET_CHROM'; +export const SET_POSITION = 'SET_POSITION'; +export const SET_OVERVIEW_CHROM = 'SET_OVERVIEW_CHROM'; +export const SET_OVERVIEW_ASSAY = 'SET_OVERVIEW_ASSAY'; +export const SET_USE_PRECOMPUTED = 'SET_USE_PRECOMPUTED'; +export const HANDLE_ERROR = 'HANDLE_ERROR'; -export const ASSAYS = createFetchConstants('ASSAYS'); -export const ASSEMBLY = createFetchConstants('ASSEMBLY'); -export const CONDITIONS = createFetchConstants('CONDITIONS'); -export const ETHNICITIES = createFetchConstants('ETHNICITIES'); -export const SAMPLES = createFetchConstants('SAMPLES'); -export const CHROMS = createFetchConstants('CHROMS'); -export const POSITIONS = createFetchConstants('POSITIONS'); -export const VALUES = createFetchConstants('VALUES'); -export const PEAKS = createFetchConstants('PEAKS'); -export const OVERVIEW_CONFIG = createFetchConstants('OVERVIEW_CONFIG'); -export const MANHATTAN_DATA = createFetchConstants('MANHATTAN_DATA'); -export const USER = createFetchConstants('USER'); -export const MESSAGES = createFetchConstants('MESSAGES'); +export const ASSAYS = createFetchConstants('ASSAYS'); +export const ASSEMBLY = createFetchConstants('ASSEMBLY'); +export const CONDITIONS = createFetchConstants('CONDITIONS'); +export const ETHNICITIES = createFetchConstants('ETHNICITIES'); +export const SAMPLES = createFetchConstants('SAMPLES'); +export const CHROMS = createFetchConstants('CHROMS'); +export const POSITIONS = createFetchConstants('POSITIONS'); +export const VALUES = createFetchConstants('VALUES'); +export const PEAKS = createFetchConstants('PEAKS'); +export const OVERVIEW_CONFIG = createFetchConstants('OVERVIEW_CONFIG'); +export const MANHATTAN_DATA = createFetchConstants('MANHATTAN_DATA'); +export const USER = createFetchConstants('USER'); +export const MESSAGES = createFetchConstants('MESSAGES'); function createFetchConstants(namespace) { diff --git a/client/src/reducers.js b/client/src/reducers.js index 6b0503b6..0580e159 100644 --- a/client/src/reducers.js +++ b/client/src/reducers.js @@ -13,6 +13,8 @@ const defaultUI = { chrom: '', assay: '', }, + + usePrecomputed: true, }; function uiReducer(state = defaultUI, action) { @@ -29,6 +31,9 @@ function uiReducer(state = defaultUI, action) { case k.SET_OVERVIEW_ASSAY: { return {...state, overview: {...state.overview, assay: action.payload}}; } + case k.SET_USE_PRECOMPUTED: { + return {...state, usePrecomputed: action.payload}; + } default: return state; } From 2f2da96f3485ee12dd91421ef0e23378e0077175 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 13:09:49 -0400 Subject: [PATCH 049/105] fix: server box plot module export/import/es6 module --- helpers/{boxplot.js => boxplot.mjs} | 14 +++----------- models/tracks.mjs | 2 +- routes/tracks.mjs | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) rename helpers/{boxplot.js => boxplot.mjs} (98%) diff --git a/helpers/boxplot.js b/helpers/boxplot.mjs similarity index 98% rename from helpers/boxplot.js rename to helpers/boxplot.mjs index daec88cd..9efde80b 100644 --- a/helpers/boxplot.js +++ b/helpers/boxplot.mjs @@ -22,7 +22,7 @@ const POINT_RADIUS = 4; const TEXT_STYLES = `font-size: ${FONT_SIZE}; font-family: sans-serif; text-anchor: middle`; -const PLOT_SIZE = 350; +export const PLOT_SIZE = 350; const PLOT_PADDING = 40; const PLOT_HORIZ_PADDING = 25; @@ -253,7 +253,7 @@ async function boxPlotBar({data, x, y, height, domain}) { `; } -async function boxPlot({title, data, domain, transform}) { +export async function boxPlot({title, data, domain, transform}) { if (!data) { return ""; } @@ -298,7 +298,7 @@ async function boxPlot({title, data, domain, transform}) { // Helpers -function getDomain(categories) { +export function getDomain(categories) { if (!categories?.length) { return undefined; } @@ -382,11 +382,3 @@ function shuffle(array) { return array; } - -// --- - -module.exports = { - PLOT_SIZE, - getDomain, - boxPlot, -}; diff --git a/models/tracks.mjs b/models/tracks.mjs index de7884cc..0b908453 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -11,7 +11,7 @@ import {groupBy, map, path as prop} from "rambda"; import bigWigMerge from "../helpers/bigwig-merge.js"; import bigWigChromosomeLength from "../helpers/bigwig-chromosome-length.js"; -import {boxPlot, getDomain, PLOT_SIZE} from "../helpers/boxplot.js"; +import {boxPlot, getDomain, PLOT_SIZE} from "../helpers/boxplot.mjs"; import cache from "../helpers/cache.mjs"; import valueAt from "../helpers/value-at.mjs"; import config from "../config.js"; diff --git a/routes/tracks.mjs b/routes/tracks.mjs index b5a6ef98..f7c705b2 100644 --- a/routes/tracks.mjs +++ b/routes/tracks.mjs @@ -5,7 +5,7 @@ import {ensureAgreedToTerms, ensureLogIn} from "../helpers/auth.mjs"; import {dataHandler, errorHandler, pngHandler} from "../helpers/handlers.mjs"; import Tracks from "../models/tracks.mjs"; import Peaks from "../models/peaks.mjs"; -import PLOT_SIZE from "../helpers/boxplot.js"; +import {PLOT_SIZE} from "../helpers/boxplot.mjs"; const SCALE_FACTOR = 2; const PNG_DPI = 300; From 646eee5bdf696f213c013b15a652a67e088df049 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 13:10:23 -0400 Subject: [PATCH 050/105] fix: boxplot config import --- helpers/boxplot.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/boxplot.mjs b/helpers/boxplot.mjs index 9efde80b..ccffba66 100644 --- a/helpers/boxplot.mjs +++ b/helpers/boxplot.mjs @@ -1,4 +1,4 @@ -import config from "../config"; +import config from "../config.js"; const {ethnicities} = config.source; const nEthnicities = ethnicities.length; From ebeae299e5bc5b90725a691a73c45ea80db4269a Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 13:12:16 -0400 Subject: [PATCH 051/105] fix: conditions loading in track model --- models/tracks.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 0b908453..e0c74fe1 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -40,7 +40,7 @@ const strandToView = { const groupByEthnicity = groupBy(prop("ethnicity")); const mapToData = map(prop("data")); -const {conditions} = config.source?.conditions ?? DEFAULT_CONDITIONS; +const conditions = config.source?.conditions ?? DEFAULT_CONDITIONS; const CONDITION_IDS = conditions.map(c => c.id); const TRACK_VALUES_CACHE_EXPIRY = 60 * 60 * 24 * 180; // 180 days From ebd8bfec54661dee885f4fd4df18921942331009 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 13:16:31 -0400 Subject: [PATCH 052/105] chore: bump version to 0.13.0 --- client/package-lock.json | 4 ++-- client/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 7fdbc4d7..db36a63d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "flu-infection-client", - "version": "0.12.2", + "version": "0.13.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "flu-infection-client", - "version": "0.12.2", + "version": "0.13.0", "dependencies": { "@redux-devtools/extension": "^3.2.5", "axios": "^1.3.6", diff --git a/client/package.json b/client/package.json index bbe4892a..51131207 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "flu-infection-client", - "version": "0.12.2", + "version": "0.13.0", "private": true, "dependencies": { "@redux-devtools/extension": "^3.2.5", diff --git a/package-lock.json b/package-lock.json index 2c8d66bf..e7507293 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "flu-infection", - "version": "0.12.2", + "version": "0.13.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "flu-infection", - "version": "0.12.2", + "version": "0.13.0", "hasInstallScript": true, "dependencies": { "@gmod/bbi": "^1.0.35", diff --git a/package.json b/package.json index dfbaaf5f..458ad89a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flu-infection", - "version": "0.12.2", + "version": "0.13.0", "private": true, "scripts": { "postinstall": "cd client && npm install", From f0c294c4da87e4f17e3123b65167067ee998546e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 13:23:29 -0400 Subject: [PATCH 053/105] chore: update dependencies --- client/package-lock.json | 80 +++++++++--------- client/package.json | 6 +- package-lock.json | 169 ++++++++++++++++++++++----------------- package.json | 10 +-- 4 files changed, 142 insertions(+), 123 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index db36a63d..9b055227 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,7 +9,7 @@ "version": "0.13.0", "dependencies": { "@redux-devtools/extension": "^3.2.5", - "axios": "^1.3.6", + "axios": "^1.4.0", "bootstrap": "^4.6.2", "bootstrap-icons": "^1.10.5", "classname": "0.0.0", @@ -21,8 +21,8 @@ "rambda": "^7.5.0", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-redux": "^8.0.5", - "react-router-dom": "^6.10.0", + "react-redux": "^8.1.1", + "react-router-dom": "^6.13.0", "react-table": "^7.8.0", "reactstrap": "^8.10.1", "redux": "^4.2.1", @@ -3100,9 +3100,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz", - "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz", + "integrity": "sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==", "engines": { "node": ">=14" } @@ -4625,9 +4625,9 @@ } }, "node_modules/axios": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz", - "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -14334,9 +14334,9 @@ } }, "node_modules/react-redux": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", - "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", + "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", "dependencies": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", @@ -14351,7 +14351,7 @@ "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0", "react-native": ">=0.59", - "redux": "^4" + "redux": "^4 || ^5.0.0-beta.0" }, "peerDependenciesMeta": { "@types/react": { @@ -14386,11 +14386,11 @@ } }, "node_modules/react-router": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz", - "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.13.0.tgz", + "integrity": "sha512-Si6KnfEnJw7gUQkNa70dlpI1bul46FuSxX5t5WwlUBxE25DAz2BjVkwaK8Y2s242bQrZPXCpmwLPtIO5pv4tXg==", "dependencies": { - "@remix-run/router": "1.5.0" + "@remix-run/router": "1.6.3" }, "engines": { "node": ">=14" @@ -14400,12 +14400,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz", - "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.13.0.tgz", + "integrity": "sha512-6Nqoqd7fgwxxVGdbiMHTpDHCYPq62d7Wk1Of7B82vH7ZPwwsRaIa22zRZKPPg413R5REVNiyuQPKDG1bubcOFA==", "dependencies": { - "@remix-run/router": "1.5.0", - "react-router": "6.10.0" + "@remix-run/router": "1.6.3", + "react-router": "6.13.0" }, "engines": { "node": ">=14" @@ -19653,9 +19653,9 @@ } }, "@remix-run/router": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz", - "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==" + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz", + "integrity": "sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==" }, "@rollup/plugin-babel": { "version": "5.3.1", @@ -20823,9 +20823,9 @@ "dev": true }, "axios": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz", - "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -27935,9 +27935,9 @@ } }, "react-redux": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", - "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", + "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", "requires": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", @@ -27961,20 +27961,20 @@ "dev": true }, "react-router": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz", - "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.13.0.tgz", + "integrity": "sha512-Si6KnfEnJw7gUQkNa70dlpI1bul46FuSxX5t5WwlUBxE25DAz2BjVkwaK8Y2s242bQrZPXCpmwLPtIO5pv4tXg==", "requires": { - "@remix-run/router": "1.5.0" + "@remix-run/router": "1.6.3" } }, "react-router-dom": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz", - "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.13.0.tgz", + "integrity": "sha512-6Nqoqd7fgwxxVGdbiMHTpDHCYPq62d7Wk1Of7B82vH7ZPwwsRaIa22zRZKPPg413R5REVNiyuQPKDG1bubcOFA==", "requires": { - "@remix-run/router": "1.5.0", - "react-router": "6.10.0" + "@remix-run/router": "1.6.3", + "react-router": "6.13.0" } }, "react-scripts": { diff --git a/client/package.json b/client/package.json index 51131207..d7f06d21 100644 --- a/client/package.json +++ b/client/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@redux-devtools/extension": "^3.2.5", - "axios": "^1.3.6", + "axios": "^1.4.0", "bootstrap": "^4.6.2", "bootstrap-icons": "^1.10.5", "classname": "0.0.0", @@ -16,8 +16,8 @@ "rambda": "^7.5.0", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-redux": "^8.0.5", - "react-router-dom": "^6.10.0", + "react-redux": "^8.1.1", + "react-router-dom": "^6.13.0", "react-table": "^7.8.0", "reactstrap": "^8.10.1", "redux": "^4.2.1", diff --git a/package-lock.json b/package-lock.json index e7507293..5a2a378e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "cuid": "^2.1.8", "d3-scale": "^4.0.2", "debug": "~4.3.4", - "dotenv": "^16.0.3", + "dotenv": "^16.3.1", "express": "^4.18.2", "express-session": "^1.17.3", "lodash": "^4.17.21", @@ -30,13 +30,13 @@ "mysql": "^2.18.1", "passport": "^0.6.0", "passport-openidconnect": "^0.1.1", - "pg": "^8.10.0", + "pg": "^8.11.0", "pg-copy-streams": "^6.0.5", "pug": "~3.0.2", - "rambda": "^7.3.0", - "redis": "~4.6.5", + "rambda": "^7.5.0", + "redis": "~4.6.7", "serve-favicon": "~2.5.0", - "sharp": "^0.31.2" + "sharp": "^0.32.1" }, "devDependencies": { "nodemon": "^2.0.22", @@ -123,9 +123,9 @@ } }, "node_modules/@redis/client": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.6.tgz", - "integrity": "sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.8.tgz", + "integrity": "sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw==", "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -152,9 +152,9 @@ } }, "node_modules/@redis/search": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", - "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", + "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -861,11 +861,14 @@ "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, "node_modules/ee-first": { @@ -1721,9 +1724,9 @@ } }, "node_modules/node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" }, "node_modules/nodemon": { "version": "2.0.22", @@ -1899,13 +1902,13 @@ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "node_modules/pg": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.10.0.tgz", - "integrity": "sha512-ke7o7qSTMb47iwzOSaZMfeR7xToFdkE71ifIipOAAaLIM0DYzfOAXlgFFmYUIE2BcJtvnVlGCID84ZzCegE8CQ==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.0.tgz", + "integrity": "sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA==", "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", + "pg-connection-string": "^2.6.0", "pg-pool": "^3.6.0", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", @@ -1914,6 +1917,9 @@ "engines": { "node": ">= 8.0.0" }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.0" + }, "peerDependencies": { "pg-native": ">=3.0.1" }, @@ -1923,10 +1929,16 @@ } } }, + "node_modules/pg-cloudflare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.0.tgz", + "integrity": "sha512-tGM8/s6frwuAIyRcJ6nWcIvd3+3NmUKIs6OjviIm1HPPFEt5MzQDOTBQyhPWg/m0kCl95M6gA1JaIXtS8KovOA==", + "optional": true + }, "node_modules/pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.0.tgz", + "integrity": "sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg==" }, "node_modules/pg-copy-streams": { "version": "6.0.5", @@ -2310,9 +2322,9 @@ } }, "node_modules/rambda": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.3.0.tgz", - "integrity": "sha512-RFVofZYaG2TaVcxjnM0ejdVWf/59rFq1f57OGnjP3GT/bthzFw0GVr5rkP9PKbVlEuF/Y7bOVPLfiiYfxq/EWQ==" + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", + "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==" }, "node_modules/random-bytes": { "version": "1.0.0", @@ -2385,15 +2397,15 @@ } }, "node_modules/redis": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.5.tgz", - "integrity": "sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.7.tgz", + "integrity": "sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw==", "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.6", + "@redis/client": "1.5.8", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.2", + "@redis/search": "1.1.3", "@redis/time-series": "1.0.4" } }, @@ -2536,16 +2548,16 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sharp": { - "version": "0.31.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.2.tgz", - "integrity": "sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.1.tgz", + "integrity": "sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==", "hasInstallScript": true, "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", + "node-addon-api": "^6.1.0", "prebuild-install": "^7.1.1", - "semver": "^7.3.8", + "semver": "^7.5.0", "simple-get": "^4.0.1", "tar-fs": "^2.1.1", "tunnel-agent": "^0.6.0" @@ -2558,9 +2570,9 @@ } }, "node_modules/sharp/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -3009,9 +3021,9 @@ "requires": {} }, "@redis/client": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.6.tgz", - "integrity": "sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.8.tgz", + "integrity": "sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw==", "requires": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -3031,9 +3043,9 @@ "requires": {} }, "@redis/search": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", - "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", + "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", "requires": {} }, "@redis/time-series": { @@ -3547,9 +3559,9 @@ "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" }, "ee-first": { "version": "1.1.1", @@ -4194,9 +4206,9 @@ } }, "node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" }, "nodemon": { "version": "2.0.22", @@ -4329,23 +4341,30 @@ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "pg": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.10.0.tgz", - "integrity": "sha512-ke7o7qSTMb47iwzOSaZMfeR7xToFdkE71ifIipOAAaLIM0DYzfOAXlgFFmYUIE2BcJtvnVlGCID84ZzCegE8CQ==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.0.tgz", + "integrity": "sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA==", "requires": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", + "pg-cloudflare": "^1.1.0", + "pg-connection-string": "^2.6.0", "pg-pool": "^3.6.0", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", "pgpass": "1.x" } }, + "pg-cloudflare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.0.tgz", + "integrity": "sha512-tGM8/s6frwuAIyRcJ6nWcIvd3+3NmUKIs6OjviIm1HPPFEt5MzQDOTBQyhPWg/m0kCl95M6gA1JaIXtS8KovOA==", + "optional": true + }, "pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.0.tgz", + "integrity": "sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg==" }, "pg-copy-streams": { "version": "6.0.5", @@ -4681,9 +4700,9 @@ "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==" }, "rambda": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.3.0.tgz", - "integrity": "sha512-RFVofZYaG2TaVcxjnM0ejdVWf/59rFq1f57OGnjP3GT/bthzFw0GVr5rkP9PKbVlEuF/Y7bOVPLfiiYfxq/EWQ==" + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", + "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==" }, "random-bytes": { "version": "1.0.0", @@ -4741,15 +4760,15 @@ } }, "redis": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.5.tgz", - "integrity": "sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.7.tgz", + "integrity": "sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw==", "requires": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.6", + "@redis/client": "1.5.8", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.2", + "@redis/search": "1.1.3", "@redis/time-series": "1.0.4" } }, @@ -4877,24 +4896,24 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sharp": { - "version": "0.31.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.2.tgz", - "integrity": "sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.1.tgz", + "integrity": "sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==", "requires": { "color": "^4.2.3", "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", + "node-addon-api": "^6.1.0", "prebuild-install": "^7.1.1", - "semver": "^7.3.8", + "semver": "^7.5.0", "simple-get": "^4.0.1", "tar-fs": "^2.1.1", "tunnel-agent": "^0.6.0" }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "requires": { "lru-cache": "^6.0.0" } diff --git a/package.json b/package.json index 458ad89a..0702bef3 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "cuid": "^2.1.8", "d3-scale": "^4.0.2", "debug": "~4.3.4", - "dotenv": "^16.0.3", + "dotenv": "^16.3.1", "express": "^4.18.2", "express-session": "^1.17.3", "lodash": "^4.17.21", @@ -31,13 +31,13 @@ "mysql": "^2.18.1", "passport": "^0.6.0", "passport-openidconnect": "^0.1.1", - "pg": "^8.10.0", + "pg": "^8.11.0", "pg-copy-streams": "^6.0.5", "pug": "~3.0.2", - "rambda": "^7.3.0", - "redis": "~4.6.5", + "rambda": "^7.5.0", + "redis": "~4.6.7", "serve-favicon": "~2.5.0", - "sharp": "^0.31.2" + "sharp": "^0.32.1" }, "devDependencies": { "nodemon": "^2.0.22", From 8c82612eda1fcec1996d0fa2a5d411221bec99a2 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 15:18:06 -0400 Subject: [PATCH 054/105] docs: notes on building metadata file from scratch + more info on tracks --- readme.md | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index c0e17392..db1ad09f 100644 --- a/readme.md +++ b/readme.md @@ -52,7 +52,36 @@ The different data sources to generate/prepare are: peak position is. Eg, the peak can be at `chr1:1000`, but the feature is at the range `chr1:3500-3600` - - **Metadata:** This is the track's metadata + - **Metadata:** This is the track's metadata. This can either be provided as an + XLSX file with the headers: + - `file.path` + - `ethnicity` + - `condition` + - `institution.short_name` + - `sample_name` + - `donor` + - `track.view` + - `track.track_type` + - `assembly.name` + - `assay.name` + - `assay_category.name` + + or a JSON file containing a list of objects with (similar) keys: + - `path` + - `ethnicity` + - `condition` + - `short_name` + - `sample_name` + - `donor` + - `view` + - `type` + - `assembly` + - `assay` + - `assay_id` + - `assay_category` + - `assay_category_id` + + Information on track metadata file: - **Generate with:** `node ./scripts/metadata-to-json.js` - **Input:** `./input-files/flu-infection.xlsx` - **Data:** `./data/metadata.json` @@ -63,19 +92,20 @@ The different data sources to generate/prepare are: chromosome/assay pairs, binned by SNP position. - **Generate with:** `node ./scripts/calculate-top-peaks.js` - - **Tracks:** There are the bigWig files that contain the signal data. - - **Generate with:** You will need to either copy the files, or - in development mount them with `sshfs` to have access to them. - See comment inside `config.example.js` for more details. - - Config: `config.paths.tracks` (directory) - - Notes: A metadata item (from step Metadata above) `.path` field + - **Tracks:** There are pre-generated bigWig files that contain the signal data + to use for merging and displaying in the browser. The paths should correspond to + - **Config:** `VARWIG_TRACKS_DIR` environment variable, specifying the directory + - **Notes:** A metadata item (from step Metadata above) `.path` field points to a path inside the `config.paths.tracks` directory, eg: `metadata.path = 'RNAseq/AF02_Flu.forward.bw'` `filepath = path.join(config.paths.tracks, metadata.path)` + - **EpiVar-specific notes:** You will need to either copy the files, or + in development mount them with `sshfs` to have access to them. - **Merged tracks:** The directory to store the merged tracks. - **Generate with:** `mkdir -p ./data/mergedTracks` - - **Config:** `config.paths.mergedTracks` (directory) + - This location can be changed with the environment variable `VARWIG_MERGED_TRACKS_DIR` + - **Config:** `VARWIG_MERGED_TRACKS_DIR` environment variable or `config.paths.mergedTracks` (directory) - **Notes:** Make sure there is enough space for those tracks. - **Gemini database:** This contains variants' data. @@ -90,7 +120,7 @@ The different data sources to generate/prepare are: ### Application -Once the data is read, you can install & build the application as follows: +Once the data is ready, you can install & build the application as follows: ```sh npm run install From a2b00ed8ff69a8b3d30fa8aa43f6586ad2fa81be Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 15:25:53 -0400 Subject: [PATCH 055/105] docs: wording tweaks + notes on peaks --- readme.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index db1ad09f..158feb8a 100644 --- a/readme.md +++ b/readme.md @@ -45,9 +45,22 @@ The different data sources to generate/prepare are: normalized by replacing all non-digits/non-letters with `-`, and that is the unique `name_norm` column used for genes. - - **Peaks:** list of peaks names mapped to their characteristics. + - **Peaks:** list of peaks names mapped to their characteristics. These files + have the following headers: + - `rsID`: The rsID of the SNP + - `snp`: The SNP in the SNP-peak association; formatted like `chr#_######` + (UCSC-formatted chromosome name, underscore, position) + - `feature`: The feature name - either `chr#_startpos_endpos` or `GENE_NAME` + - `pvalue.*` where `*` is the ID of the condition (by default, `*` = `NI` then `Flu`) + - These are floating point numbers + - `feature_type`: The assay the peak is from - e.g., `RNA-seq` + + Information on the QTL/peak list files: - **Import with:** `node ./scripts/import-peaks.js` - - **Input:** `./input-files/QTLS_complete_*.csv` + - **Input:** `./input-files/qtls/QTLS_complete_*.csv` + - **Config:** Use the `VARWIG_QTLS_TEMPLATE` environment variable to configure + where QTL lists are loaded from. The `$ASSAY` string is replaced with each + assay in turn. *Defaults to:* `./input-files/qtls/QTLs_complete_$ASSAY.csv` - **Notes:** The peak's associated feature is usually different from where the peak position is. Eg, the peak can be at `chr1:1000`, but the feature is at the range `chr1:3500-3600` @@ -81,16 +94,17 @@ The different data sources to generate/prepare are: - `assay_category` - `assay_category_id` - Information on track metadata file: + Information on the track metadata file: - **Generate with:** `node ./scripts/metadata-to-json.js` - **Input:** `./input-files/flu-infection.xlsx` - - **Data:** `./data/metadata.json` + - **Output:** `./data/metadata.json` - **Config:** `config.source.metadata.path` (filepath) - **Notes:** This is really just an XLSX to JSON transformation. - **Binned top peaks for assays:** Used to generate Manhattan plots for chromosome/assay pairs, binned by SNP position. - **Generate with:** `node ./scripts/calculate-top-peaks.js` + - **Notes:** This will populate a table in the Postgres database. - **Tracks:** There are pre-generated bigWig files that contain the signal data to use for merging and displaying in the browser. The paths should correspond to From 58f95964aa67fe64545fb1507a68f163b9e70994 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 15:26:47 -0400 Subject: [PATCH 056/105] docs: note peaks are csvs --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 158feb8a..c7d04280 100644 --- a/readme.md +++ b/readme.md @@ -46,7 +46,7 @@ The different data sources to generate/prepare are: unique `name_norm` column used for genes. - **Peaks:** list of peaks names mapped to their characteristics. These files - have the following headers: + are CSVs which have the following headers: - `rsID`: The rsID of the SNP - `snp`: The SNP in the SNP-peak association; formatted like `chr#_######` (UCSC-formatted chromosome name, underscore, position) From 9bc7d5d191848019f5acd2553cbf1e7d0cd43130 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Jun 2023 15:32:17 -0400 Subject: [PATCH 057/105] docs: conditions and ethnicities config --- readme.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/readme.md b/readme.md index c7d04280..b90c1d31 100644 --- a/readme.md +++ b/readme.md @@ -36,6 +36,27 @@ exists with `mkdir -p ./data`. The different data sources to generate/prepare are: + - **Condition and ethnicity configuration:** Set up conditions/treatments and + sample ethnicities for the portal in `config.js` in the + `config.source.conditions` and `config.source.ethnicities` arrays: + - **Format:** *(example)* + ```javascript + module.exports = { + // ... + source: { + // ... + conditions: [ + {id: "NI", name: "Non-infected"}, + {id: "Flu", name: "Flu"}, + ], + ethnicities: [ + {id: "AF", name: "African-American", plotColor: "#5100FF", plotBoxColor: "rgba(81, 0, 255, 0.6)"}, + {id: "EU", name: "European-American", plotColor: "#FF8A00", plotBoxColor: "rgba(255, 138, 0, 0.6)"}, + ], + }, + // ... + }; + ``` - **Genes:** list of gene names mapped to their characteristics. - **Import with:** `node ./scripts/import-genes.js` - **Input:** `./input-files/flu-infection-genes.txt` From 66ebb8bc78f584f4b5f00a8f08521e1e4ac996ff Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 08:46:46 -0400 Subject: [PATCH 058/105] fix(client): bad deconstruct of ui state --- client/src/components/PeakResults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/PeakResults.js b/client/src/components/PeakResults.js index 777d2d6a..a0868b12 100644 --- a/client/src/components/PeakResults.js +++ b/client/src/components/PeakResults.js @@ -26,7 +26,7 @@ const PeakResults = () => { const isEmpty = useSelector(state => state.peaks.isLoaded && state.peaks.list.length === 0); const peaks = useSelector(state => state.peaks.list || []); - const {ui: {chrom: uiChrom, position: uiPosition}} = useSelector(state => state.ui); + const {chrom: uiChrom, position: uiPosition} = useSelector(state => state.ui); const peaksByAssay = groupAndSortPeaks(peaks); const assaysWithFeatures = Object.keys(peaksByAssay); From f21a8e01c9dd554d39dcb7822aeae441a924c621 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 08:49:16 -0400 Subject: [PATCH 059/105] fix(client): value access in peak table --- client/src/components/PeakAssay.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 680f157e..e78c3e04 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -159,15 +159,14 @@ const PeaksTable = ({peaks, selectedPeak, onChangeFeature, onOpenTracks}) => { }, disableSortBy: true, }, - ...conditions.map(({id, name}) => { - const k = `value${id}`; + ...conditions.map(({id, name}, idx) => { // noinspection JSUnusedGlobalSymbols return { - id: k, + id: `value${id}`, Header: p Value ({name}), accessor: row => { - const fixed = row[k].toPrecision(5); - const floatStr = row[k].toString(); + const fixed = row.values[idx].toPrecision(5); + const floatStr = row.values[idx].toString(); return floatStr.length < fixed.length ? floatStr : fixed; }, sortType: (r1, r2, col) => r1.original[col] < r2.original[col] ? -1 : 1, From b231b2a9da12dd7f15aa9d4c4311b3f2e0c7b265 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 08:52:44 -0400 Subject: [PATCH 060/105] fix(client): hook typo --- client/src/components/PeakBoxplot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/PeakBoxplot.js b/client/src/components/PeakBoxplot.js index f8234c0d..2e338a89 100644 --- a/client/src/components/PeakBoxplot.js +++ b/client/src/components/PeakBoxplot.js @@ -4,7 +4,7 @@ import {useSelector} from "react-redux"; function PeakBoxplot({ title, peak, /*values = defaultValues*/ }) { const usePrecomputed = useSelector(state => state.ui.usePrecomputed); - const ethnicities = useState(state => state.ethnicities.list); + const ethnicities = useSelector(state => state.ethnicities.list); const [loaded, setLoaded] = useState(false); const prevPeakImgRef = useRef(""); From 52416d01198b2861f7b6ae61c95b973a474c8d6e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 08:55:32 -0400 Subject: [PATCH 061/105] fix: geminiSampleNameConverter access --- models/source/metadata.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/source/metadata.js b/models/source/metadata.js index 6ec48c0e..cfda363f 100644 --- a/models/source/metadata.js +++ b/models/source/metadata.js @@ -24,7 +24,7 @@ function getTracks(samples, peak) { const samplesByRealName = Object.fromEntries( Object.entries(samples) - .map(([key, value]) => [config.source.metadata.geminiSampleNameConverter(key), value])) + .map(([key, value]) => [config.source.geminiSampleNameConverter(key), value])) const sampleNames = Object.keys(samplesByRealName) const assay = peak.assay.toLowerCase() From 5dbd884ce78d086eca25d4f36dcbbf0ebc412953 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:01:16 -0400 Subject: [PATCH 062/105] fix: precomputed values fetch --- models/tracks.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index e0c74fe1..be92c449 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -87,7 +87,7 @@ async function values(peak, usePrecomputed = false) { getValueForTrack = track => { const condIdx = CONDITION_IDS.indexOf(track.condition); if (condIdx === -1) return Promise.resolve(undefined); - const pointIdx = donorsByCondition[condIdx].indexOf(track.donor); + const pointIdx = donorsByCondition[track.condition].indexOf(track.donor); if (pointIdx === -1) return Promise.resolve(undefined); return Promise.resolve(peak.feature.points?.[condIdx]?.[pointIdx]); }; From 99ea62946883f484cc18bdcebcba401efedca8bb Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:09:46 -0400 Subject: [PATCH 063/105] fix: plot positioning / transform / width handling --- helpers/boxplot.mjs | 10 ++++++---- models/tracks.mjs | 13 ++++++++++--- routes/tracks.mjs | 6 +++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/helpers/boxplot.mjs b/helpers/boxplot.mjs index ccffba66..e60d502f 100644 --- a/helpers/boxplot.mjs +++ b/helpers/boxplot.mjs @@ -1,6 +1,7 @@ import config from "../config.js"; -const {ethnicities} = config.source; +const {conditions, ethnicities} = config.source; +const nConditions = conditions.length; const nEthnicities = ethnicities.length; const scaleLinear = (...args) => import("d3-scale").then( @@ -22,15 +23,16 @@ const POINT_RADIUS = 4; const TEXT_STYLES = `font-size: ${FONT_SIZE}; font-family: sans-serif; text-anchor: middle`; -export const PLOT_SIZE = 350; +export const PLOT_WIDTH = 700; +export const PLOT_HEIGHT = 350; const PLOT_PADDING = 40; const PLOT_HORIZ_PADDING = 25; const plotDimensions = { x: PLOT_PADDING + PLOT_HORIZ_PADDING, y: PLOT_PADDING, - width: PLOT_SIZE - PLOT_PADDING, - height: PLOT_SIZE - PLOT_PADDING, + width: (PLOT_WIDTH / nConditions) - PLOT_PADDING, + height: PLOT_HEIGHT - PLOT_PADDING, }; // Drawing diff --git a/models/tracks.mjs b/models/tracks.mjs index be92c449..63229796 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -11,7 +11,7 @@ import {groupBy, map, path as prop} from "rambda"; import bigWigMerge from "../helpers/bigwig-merge.js"; import bigWigChromosomeLength from "../helpers/bigwig-chromosome-length.js"; -import {boxPlot, getDomain, PLOT_SIZE} from "../helpers/boxplot.mjs"; +import {boxPlot, getDomain, PLOT_HEIGHT, PLOT_WIDTH} from "../helpers/boxplot.mjs"; import cache from "../helpers/cache.mjs"; import valueAt from "../helpers/value-at.mjs"; import config from "../config.js"; @@ -83,6 +83,8 @@ async function values(peak, usePrecomputed = false) { if (usePrecomputed) { const donorsByCondition = _makeTrackDonorLookupArray(tracks); + console.log(peak.feature); + // Replace getter function with one which extracts the precomputed point value. getValueForTrack = track => { const condIdx = CONDITION_IDS.indexOf(track.condition); @@ -184,9 +186,14 @@ function plot(tracksByCondition) { const domains = data.map(d => getDomain(d)); return Promise.all( - conditions.map((c, ci) => boxPlot({title: c.name, data: data[ci], domain: domains[ci]})) + conditions.map((c, ci) => boxPlot({ + title: c.name, + data: data[ci], + domain: domains[ci], + transform: `translate(${((PLOT_WIDTH / conditions.length) * ci).toFixed(0)} 0)` + })) ).then(plots => - ` + ` ${plots.join("")} ` ); diff --git a/routes/tracks.mjs b/routes/tracks.mjs index f7c705b2..7b7e0b90 100644 --- a/routes/tracks.mjs +++ b/routes/tracks.mjs @@ -5,7 +5,7 @@ import {ensureAgreedToTerms, ensureLogIn} from "../helpers/auth.mjs"; import {dataHandler, errorHandler, pngHandler} from "../helpers/handlers.mjs"; import Tracks from "../models/tracks.mjs"; import Peaks from "../models/peaks.mjs"; -import {PLOT_SIZE} from "../helpers/boxplot.mjs"; +import {PLOT_HEIGHT, PLOT_WIDTH} from "../helpers/boxplot.mjs"; const SCALE_FACTOR = 2; const PNG_DPI = 300; @@ -37,7 +37,7 @@ router.post( const svgToPng = data => sharp(Buffer.from(data), {density: PNG_DPI}) - .resize(PLOT_SIZE * 2 * SCALE_FACTOR, PLOT_SIZE * SCALE_FACTOR) + .resize(PLOT_WIDTH * SCALE_FACTOR, PLOT_HEIGHT * SCALE_FACTOR) .toBuffer(); router.get( @@ -72,7 +72,7 @@ router.get( // Display error in PNG form svgToPng( - ` + ` Error while plotting: From cadce64b703dedca81d2081f4a21adaff915cde0 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:11:43 -0400 Subject: [PATCH 064/105] debug --- models/tracks.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 63229796..4b35c27e 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -83,10 +83,10 @@ async function values(peak, usePrecomputed = false) { if (usePrecomputed) { const donorsByCondition = _makeTrackDonorLookupArray(tracks); - console.log(peak.feature); // Replace getter function with one which extracts the precomputed point value. getValueForTrack = track => { + console.log(peak.feature.points); const condIdx = CONDITION_IDS.indexOf(track.condition); if (condIdx === -1) return Promise.resolve(undefined); const pointIdx = donorsByCondition[track.condition].indexOf(track.donor); From 0ca1ac25ebea344d96b482d94434ae0d87cb9762 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:12:58 -0400 Subject: [PATCH 065/105] contd --- models/tracks.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 4b35c27e..72b8730e 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -83,10 +83,9 @@ async function values(peak, usePrecomputed = false) { if (usePrecomputed) { const donorsByCondition = _makeTrackDonorLookupArray(tracks); - // Replace getter function with one which extracts the precomputed point value. getValueForTrack = track => { - console.log(peak.feature.points); + console.log("points", peak.feature.points); const condIdx = CONDITION_IDS.indexOf(track.condition); if (condIdx === -1) return Promise.resolve(undefined); const pointIdx = donorsByCondition[track.condition].indexOf(track.donor); From 9c9c0360abc2274f5284009e20d19713468908f8 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:15:32 -0400 Subject: [PATCH 066/105] debug: temp disable cache --- models/tracks.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 72b8730e..31d10ba3 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -69,7 +69,7 @@ async function values(peak, usePrecomputed = false) { // noinspection JSCheckFunctionSignatures const cv = await cache.getJSON(k); - if (cv) return cv; + // if (cv) return cv; const tracks = await get(peak); @@ -113,7 +113,7 @@ async function values(peak, usePrecomputed = false) { })) ))).filter(v => v !== undefined); - await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); + // await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); return result; } From 05a67025a1c845f9e5ee2102d60c3bfa0fc6a468 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:19:54 -0400 Subject: [PATCH 067/105] chore(client): temp-disable precomputed checkbox --- client/src/components/PeakAssay.js | 2 +- client/src/reducers.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index e78c3e04..2f0bc51d 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -76,7 +76,7 @@ const PeakAssay = ({peaks}) => { diff --git a/client/src/reducers.js b/client/src/reducers.js index 0580e159..8289aea0 100644 --- a/client/src/reducers.js +++ b/client/src/reducers.js @@ -14,7 +14,7 @@ const defaultUI = { assay: '', }, - usePrecomputed: true, + usePrecomputed: false, }; function uiReducer(state = defaultUI, action) { From 5a0820eae1717b9072a478fc1782ada5ceb0687a Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:32:56 -0400 Subject: [PATCH 068/105] restore caching --- models/tracks.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 31d10ba3..72b8730e 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -69,7 +69,7 @@ async function values(peak, usePrecomputed = false) { // noinspection JSCheckFunctionSignatures const cv = await cache.getJSON(k); - // if (cv) return cv; + if (cv) return cv; const tracks = await get(peak); @@ -113,7 +113,7 @@ async function values(peak, usePrecomputed = false) { })) ))).filter(v => v !== undefined); - // await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); + await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); return result; } From eb7e29fe9f8dae5865a5d98a744113380c4e251b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 09:50:07 -0400 Subject: [PATCH 069/105] fix: precomputed points as flat array with donor_condition keys --- config.example.js | 2 +- models/schema.sql | 2 +- models/tracks.mjs | 25 +++++++++++-------------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/config.example.js b/config.example.js index bf96ace3..39f7a0bd 100644 --- a/config.example.js +++ b/config.example.js @@ -41,7 +41,7 @@ module.exports = { // Template for loading pre-computed points for box plots // Format: TSV with: - // - column headers of sample IDs ([ETHNICITY][NUMBER]_[CONDITION]) + // - column headers of sample IDs ([DONOR]_[CONDITION]) // - row headers of features pointTemplate: `${inputFilesDirname}/matrices/$ASSAY_batch.age.corrected_PCsreg.txt`, diff --git a/models/schema.sql b/models/schema.sql index b180f0de..6d8d5d0c 100644 --- a/models/schema.sql +++ b/models/schema.sql @@ -61,7 +61,7 @@ create table if not exists features -- the box plot, instead of pulling live from the bigWigs. treatments and samples MUST be in alphabetical order of -- their IDs, eg [Flu: [, ..., ], NI: [...]] -- if the array is NULL, points SHOULD be pulled from bigWigs instead - "points" real[][] default null, + "points" real[] default null, foreign key ("assay") references assays ("id"), foreign key ("gene") references genes ("id") diff --git a/models/tracks.mjs b/models/tracks.mjs index 72b8730e..b4a8cf47 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -41,18 +41,19 @@ const groupByEthnicity = groupBy(prop("ethnicity")); const mapToData = map(prop("data")); const conditions = config.source?.conditions ?? DEFAULT_CONDITIONS; -const CONDITION_IDS = conditions.map(c => c.id); const TRACK_VALUES_CACHE_EXPIRY = 60 * 60 * 24 * 180; // 180 days -// Methods +const tracks = JSON.parse(fs.readFileSync(config.source.metadata.path).toString()); -const _makeTrackDonorLookupArray = tracks => { - const donorsByCondition = Object.fromEntries(CONDITION_IDS.map(c => [c, []])); - tracks.forEach(t => donorsByCondition[t.condition].push(t.donor)); - Object.values(donorsByCondition).forEach(v => v.sort()); // Sort donor IDs in place - return donorsByCondition; -}; +const donorLookupSet = new Set(); +tracks.forEach(t => { + donorLookupSet.add(`${t.donor}_${t.condition}`); +}); + +const donorLookup = [...donorLookupSet].sort(); + +// Methods function get(peak) { const {snp: {chrom, position}} = peak; @@ -81,16 +82,12 @@ async function values(peak, usePrecomputed = false) { }); if (usePrecomputed) { - const donorsByCondition = _makeTrackDonorLookupArray(tracks); - // Replace getter function with one which extracts the precomputed point value. getValueForTrack = track => { console.log("points", peak.feature.points); - const condIdx = CONDITION_IDS.indexOf(track.condition); - if (condIdx === -1) return Promise.resolve(undefined); - const pointIdx = donorsByCondition[track.condition].indexOf(track.donor); + const pointIdx = donorLookup.indexOf(`${track.donor}_${track.condition}`); if (pointIdx === -1) return Promise.resolve(undefined); - return Promise.resolve(peak.feature.points?.[condIdx]?.[pointIdx]); + return Promise.resolve(peak.feature.points?.[pointIdx]); }; } From 7fce57e7378b41a9cba1faadda5163c3571a6c36 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:01:24 -0400 Subject: [PATCH 070/105] chore: temp hide precomputed checkbox --- client/src/components/PeakAssay.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 2f0bc51d..e158700b 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -75,10 +75,10 @@ const PeakAssay = ({peaks}) => { /> - + {/**/} Date: Fri, 23 Jun 2023 10:02:06 -0400 Subject: [PATCH 071/105] ^^ --- client/src/components/PeakAssay.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index e158700b..cf2265e2 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -74,12 +74,12 @@ const PeakAssay = ({peaks}) => { onOpenTracks={onOpenTracks} /> - - {/**/} - + {/**/} + {/* */} + {/**/} Date: Fri, 23 Jun 2023 10:04:17 -0400 Subject: [PATCH 072/105] chore: explicit sort fn for donor lookup --- models/tracks.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index b4a8cf47..928a2932 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -51,7 +51,7 @@ tracks.forEach(t => { donorLookupSet.add(`${t.donor}_${t.condition}`); }); -const donorLookup = [...donorLookupSet].sort(); +const donorLookup = [...donorLookupSet].sort((a, b) => a.localeCompare(b)); // Methods From 36e8096995062429b42596ec8b1e007b38a855b3 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:04:32 -0400 Subject: [PATCH 073/105] fix: consistent feature names for precomputed point lookup --- scripts/_common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/_common.js b/scripts/_common.js index 0e62bc4e..48fb2ac4 100644 --- a/scripts/_common.js +++ b/scripts/_common.js @@ -38,7 +38,7 @@ if (loadingPrecomputedPoints) { .map(featureRow => { const rd = featureRow.split("\t"); return [ - stripQuotes(rd[0]), + stripQuotes(rd[0]).replace(/^chr/, ""), sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(rd[si + 1])), ]; })); From 55698990a70c43fce706c682e6a63c55cb87b0b0 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:04:58 -0400 Subject: [PATCH 074/105] chore: parse metadata tracks as json in models.source.metadata --- models/source/metadata.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/source/metadata.js b/models/source/metadata.js index cfda363f..307fd885 100644 --- a/models/source/metadata.js +++ b/models/source/metadata.js @@ -2,11 +2,12 @@ * metadata.js */ +const fs = require('fs') const path = require('path') const clone = require('lodash.clonedeep') const config = require('../../config') -const metadata = require(config.source.metadata.path) +const metadata = JSON.parse(fs.readFileSync(config.source.metadata.path).toString()); module.exports = { getTracks, From 05477201b8a471cff75c01778f0bd13b199d61f7 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:16:03 -0400 Subject: [PATCH 075/105] fix(scripts): ref to valueFlu/valueNI in calculate-top-peaks --- scripts/calculate-top-peaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/calculate-top-peaks.js b/scripts/calculate-top-peaks.js index 47386acb..8a761ca3 100644 --- a/scripts/calculate-top-peaks.js +++ b/scripts/calculate-top-peaks.js @@ -28,7 +28,7 @@ const config = require("../config"); JOIN features f ON p."feature" = f."id" JOIN snps s ON p."snp" = s."id" JOIN ( - SELECT f2."assay" AS a_id, MIN(LEAST(p2."valueFlu", p2."valueNI")) AS p_min + SELECT f2."assay" AS a_id, (SELECT MIN(x) FROM unnest(p2."values") AS x) AS p_min FROM peaks p2 JOIN snps s2 on p2."snp" = s2."id" JOIN features f2 on f2."id" = p2."feature" From f9520f844405dae81cc43563ab3debe698b5ea0c Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:18:57 -0400 Subject: [PATCH 076/105] fix(scripts): calculate-top-peaks nested query --- scripts/calculate-top-peaks.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/calculate-top-peaks.js b/scripts/calculate-top-peaks.js index 8a761ca3..f16441fe 100644 --- a/scripts/calculate-top-peaks.js +++ b/scripts/calculate-top-peaks.js @@ -28,15 +28,16 @@ const config = require("../config"); JOIN features f ON p."feature" = f."id" JOIN snps s ON p."snp" = s."id" JOIN ( - SELECT f2."assay" AS a_id, (SELECT MIN(x) FROM unnest(p2."values") AS x) AS p_min - FROM peaks p2 - JOIN snps s2 on p2."snp" = s2."id" - JOIN features f2 on f2."id" = p2."feature" - WHERE s2."chrom" = $1 - AND s2."position" >= $2 - AND s2."position" <= $3 + SELECT p3."assay" AS a_id, p3."p_min" as p_min + FROM (SELECT f2."assay" AS assay, (SELECT MIN(x) FROM unnest(p2."values") AS x) AS p_min + FROM peaks p2 + JOIN snps s2 on p2."snp" = s2."id" + JOIN features f2 on f2."id" = p2."feature" + WHERE s2."chrom" = $1 + AND s2."position" >= $2 + AND s2."position" <= $3) p3 GROUP BY a_id - HAVING MIN((SELECT MIN(x) FROM unnest(p2."values") AS x)) <= $4 + HAVING MIN(p3."p_min") <= $4 ) j ON f.assay = j.a_id AND (SELECT MIN(x) FROM unnest(p2."values") AS x) = j.p_min WHERE s."chrom" = $1 AND s."position" >= $2 From f7b51d6685a96de6ba7ab2d187ebec69299e8c0b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:21:10 -0400 Subject: [PATCH 077/105] ^^ --- scripts/calculate-top-peaks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/calculate-top-peaks.js b/scripts/calculate-top-peaks.js index f16441fe..f6c20f8e 100644 --- a/scripts/calculate-top-peaks.js +++ b/scripts/calculate-top-peaks.js @@ -28,8 +28,8 @@ const config = require("../config"); JOIN features f ON p."feature" = f."id" JOIN snps s ON p."snp" = s."id" JOIN ( - SELECT p3."assay" AS a_id, p3."p_min" as p_min - FROM (SELECT f2."assay" AS assay, (SELECT MIN(x) FROM unnest(p2."values") AS x) AS p_min + SELECT p3."assay" AS a_id, MIN(p3."peak_p_min") as p_min + FROM (SELECT f2."assay" AS assay, (SELECT MIN(x) FROM unnest(p2."values") AS x) AS peak_p_min FROM peaks p2 JOIN snps s2 on p2."snp" = s2."id" JOIN features f2 on f2."id" = p2."feature" @@ -37,7 +37,7 @@ const config = require("../config"); AND s2."position" >= $2 AND s2."position" <= $3) p3 GROUP BY a_id - HAVING MIN(p3."p_min") <= $4 + HAVING MIN(p3."peak_p_min") <= $4 ) j ON f.assay = j.a_id AND (SELECT MIN(x) FROM unnest(p2."values") AS x) = j.p_min WHERE s."chrom" = $1 AND s."position" >= $2 From fc2ea302484ee3c154b0fa84d932ec143a426c6e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:22:01 -0400 Subject: [PATCH 078/105] ^^ --- scripts/calculate-top-peaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/calculate-top-peaks.js b/scripts/calculate-top-peaks.js index f6c20f8e..1a8c2257 100644 --- a/scripts/calculate-top-peaks.js +++ b/scripts/calculate-top-peaks.js @@ -38,7 +38,7 @@ const config = require("../config"); AND s2."position" <= $3) p3 GROUP BY a_id HAVING MIN(p3."peak_p_min") <= $4 - ) j ON f.assay = j.a_id AND (SELECT MIN(x) FROM unnest(p2."values") AS x) = j.p_min + ) j ON f.assay = j.a_id AND (SELECT MIN(x) FROM unnest(p."values") AS x) = j.p_min WHERE s."chrom" = $1 AND s."position" >= $2 AND s."position" <= $3 From 84d290f79b04beb97d0c8d473893e6bfa152df5d Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:32:07 -0400 Subject: [PATCH 079/105] chore(scripts): split calculate-peak-groups into separate script --- readme.md | 7 ++-- scripts/calculate-peak-groups.js | 65 ++++++++++++++++++++++++++++++++ scripts/import-peaks.js | 63 ------------------------------- 3 files changed, 69 insertions(+), 66 deletions(-) create mode 100644 scripts/calculate-peak-groups.js diff --git a/readme.md b/readme.md index b90c1d31..6a01d644 100644 --- a/readme.md +++ b/readme.md @@ -77,14 +77,15 @@ The different data sources to generate/prepare are: - `feature_type`: The assay the peak is from - e.g., `RNA-seq` Information on the QTL/peak list files: - - **Import with:** `node ./scripts/import-peaks.js` + - **Import with:** `node ./scripts/import-peaks.js` followed by `node ./scripts/calculate-peak-groups.js` - **Input:** `./input-files/qtls/QTLS_complete_*.csv` - **Config:** Use the `VARWIG_QTLS_TEMPLATE` environment variable to configure where QTL lists are loaded from. The `$ASSAY` string is replaced with each assay in turn. *Defaults to:* `./input-files/qtls/QTLs_complete_$ASSAY.csv` - **Notes:** The peak's associated feature is usually different from where the - peak position is. Eg, the peak can be at `chr1:1000`, but the feature is - at the range `chr1:3500-3600` + peak position is; e.g., the peak SNP can be at `chr1:1000`, but the feature is + at the range `chr1:3500-3600`. The second script calculates peak groups by SNP + and gene for auto-complete. - **Metadata:** This is the track's metadata. This can either be provided as an XLSX file with the headers: diff --git a/scripts/calculate-peak-groups.js b/scripts/calculate-peak-groups.js new file mode 100644 index 00000000..154917b5 --- /dev/null +++ b/scripts/calculate-peak-groups.js @@ -0,0 +1,65 @@ +(async () => { + const db = await import("../models/db.mjs"); + + console.log('Pre-processing peak groups by SNP') + // If there is more than one most significant peak, just choose the first one + await db.run( + ` + INSERT INTO features_by_snp ("snp", "minValueMin", "nFeatures", "mostSignificantFeatureID") + SELECT "snp", + "minValueMin", + "nFeatures", + (SELECT "id" + FROM peaks + WHERE "snp" = a."snp" + AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" + LIMIT 1) AS "mostSignificantFeatureID" + FROM (SELECT "snp", + MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", + COUNT(*) AS "nFeatures" + FROM peaks + GROUP BY "snp") AS a + ` + ); + + console.log('Pre-processing peak groups by gene'); + // If there is more than one most significant peak, just choose the first one + await db.run( + ` + INSERT INTO features_by_gene ("gene", "minValueMin", "nFeatures", "mostSignificantFeatureID") + SELECT a."gene", + a."minValueMin", + a."nFeatures", + (SELECT peaks."id" + FROM peaks JOIN features ON peaks.feature = features.id + WHERE features."gene" = a."gene" + AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" + LIMIT 1) AS "mostSignificantFeatureID" + FROM (SELECT f."gene", + MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", + COUNT(*) AS "nFeatures" + FROM peaks AS p JOIN features AS f ON p.feature = f.id + WHERE f."gene" IS NOT NULL + GROUP BY f."gene") AS a + ` + ); + + // console.log('Pre-processing feature groups by position'); + // await db.run( + // ` + // INSERT INTO features_by_position (chrom, position, minValueMin, nFeatures, mostSignificantFeatureID) + // SELECT chrom, + // position, + // minValueMin, + // nFeatures, + // (SELECT id + // FROM peaks + // WHERE chrom = a.chrom + // AND position = a.position + // AND valueMin = a.minValueMin) AS mostSignificantFeatureID + // FROM (SELECT chrom, position, MIN(valueMin) as minValueMin, COUNT(*) AS nFeatures + // FROM peaks + // GROUP BY chrom, position) AS a + // ` + // ); +})(); diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 6d02a22f..86e5c440 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -276,69 +276,6 @@ console.log("Loading peaks"); // ------------------------------------------------------------------------ - console.log('Pre-processing peak groups by SNP') - // If there is more than one most significant peak, just choose the first one - await db.run( - ` - INSERT INTO features_by_snp ("snp", "minValueMin", "nFeatures", "mostSignificantFeatureID") - SELECT "snp", - "minValueMin", - "nFeatures", - (SELECT "id" - FROM peaks - WHERE "snp" = a."snp" - AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" - LIMIT 1) AS "mostSignificantFeatureID" - FROM (SELECT "snp", - MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", - COUNT(*) AS "nFeatures" - FROM peaks - GROUP BY "snp") AS a - ` - ); - - console.log('Pre-processing peak groups by gene'); - // If there is more than one most significant peak, just choose the first one - await db.run( - ` - INSERT INTO features_by_gene ("gene", "minValueMin", "nFeatures", "mostSignificantFeatureID") - SELECT a."gene", - a."minValueMin", - a."nFeatures", - (SELECT peaks."id" - FROM peaks JOIN features ON peaks.feature = features.id - WHERE features."gene" = a."gene" - AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" - LIMIT 1) AS "mostSignificantFeatureID" - FROM (SELECT f."gene", - MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", - COUNT(*) AS "nFeatures" - FROM peaks AS p JOIN features AS f ON p.feature = f.id - WHERE f."gene" IS NOT NULL - GROUP BY f."gene") AS a - ` - ); - - // console.log('Pre-processing feature groups by position'); - // await db.run( - // ` - // INSERT INTO features_by_position (chrom, position, minValueMin, nFeatures, mostSignificantFeatureID) - // SELECT chrom, - // position, - // minValueMin, - // nFeatures, - // (SELECT id - // FROM peaks - // WHERE chrom = a.chrom - // AND position = a.position - // AND valueMin = a.minValueMin) AS mostSignificantFeatureID - // FROM (SELECT chrom, position, MIN(valueMin) as minValueMin, COUNT(*) AS nFeatures - // FROM peaks - // GROUP BY chrom, position) AS a - // ` - // ); - - // "rsID", "snp", "feature", "pvalue.NI", "pvalue.Flu", "feature_type" // "rs13266435", "chr8_21739832", "chr8_21739251_21740780", 1.164469e-11, 6.856576e-13, "ATAC-seq" From 10ad6d33772abba43c63dc967b4dc69f880c98e4 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:32:49 -0400 Subject: [PATCH 080/105] fix(scripts): missing dotenv import --- scripts/calculate-peak-groups.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/calculate-peak-groups.js b/scripts/calculate-peak-groups.js index 154917b5..255d10cf 100644 --- a/scripts/calculate-peak-groups.js +++ b/scripts/calculate-peak-groups.js @@ -1,3 +1,5 @@ +require('dotenv').config(); + (async () => { const db = await import("../models/db.mjs"); From 5cdd071322fe8b6da680914be54ddc0882f603ee Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:37:58 -0400 Subject: [PATCH 081/105] chore: add runWithTransaction db method --- models/db.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/models/db.mjs b/models/db.mjs index 3c028f3f..c9b5175f 100644 --- a/models/db.mjs +++ b/models/db.mjs @@ -35,7 +35,7 @@ export const findOne = async (query, params) => { export const findAll = async (query, params) => (await pool.query(query, params)).rows; -export const insert = async (query, row) => { +export const runWithTransaction = async (...args) => { // Use same client for whole transaction const client = await pool.connect() @@ -43,7 +43,7 @@ export const insert = async (query, row) => { try { await client.query("BEGIN"); - res = await client.query(query, row); + res = await client.query(...args); await client.query("COMMIT"); } catch (e) { await client.query("ROLLBACK"); @@ -53,7 +53,9 @@ export const insert = async (query, row) => { } return res; -}; +} + +export const insert = (query, row) => runWithTransaction(query, row); // for argument typing export const insertMany = async (query, rows) => { // Use same client for whole transaction From 92776f36dc5c2d88fb7fe51bf73eba8fb06f4e3b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:38:32 -0400 Subject: [PATCH 082/105] fix: gene pre-processing to find top peaks --- scripts/calculate-peak-groups.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/calculate-peak-groups.js b/scripts/calculate-peak-groups.js index 255d10cf..5d1e83a5 100644 --- a/scripts/calculate-peak-groups.js +++ b/scripts/calculate-peak-groups.js @@ -5,7 +5,7 @@ require('dotenv').config(); console.log('Pre-processing peak groups by SNP') // If there is more than one most significant peak, just choose the first one - await db.run( + await db.runWithTransaction( ` INSERT INTO features_by_snp ("snp", "minValueMin", "nFeatures", "mostSignificantFeatureID") SELECT "snp", @@ -17,16 +17,16 @@ require('dotenv').config(); AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" LIMIT 1) AS "mostSignificantFeatureID" FROM (SELECT "snp", - MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", + MIN((SELECT MIN(x) FROM unnest(p."values") AS x)) as "minValueMin", COUNT(*) AS "nFeatures" - FROM peaks + FROM peaks AS p GROUP BY "snp") AS a ` ); console.log('Pre-processing peak groups by gene'); // If there is more than one most significant peak, just choose the first one - await db.run( + await db.runWithTransaction( ` INSERT INTO features_by_gene ("gene", "minValueMin", "nFeatures", "mostSignificantFeatureID") SELECT a."gene", @@ -38,7 +38,7 @@ require('dotenv').config(); AND (SELECT MIN(x) FROM unnest(peaks."values") AS x) = a."minValueMin" LIMIT 1) AS "mostSignificantFeatureID" FROM (SELECT f."gene", - MIN((SELECT MIN(x) FROM unnest(peaks."values") AS x)) as "minValueMin", + MIN((SELECT MIN(x) FROM unnest(p."values") AS x)) as "minValueMin", COUNT(*) AS "nFeatures" FROM peaks AS p JOIN features AS f ON p.feature = f.id WHERE f."gene" IS NOT NULL From 52dc740aa94076ab9c7fff54e3a198ab86dda330 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 10:39:44 -0400 Subject: [PATCH 083/105] fix(scripts): clear peak groups table on re run --- scripts/calculate-peak-groups.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/calculate-peak-groups.js b/scripts/calculate-peak-groups.js index 5d1e83a5..43a91b76 100644 --- a/scripts/calculate-peak-groups.js +++ b/scripts/calculate-peak-groups.js @@ -3,6 +3,10 @@ require('dotenv').config(); (async () => { const db = await import("../models/db.mjs"); + // Clear relevant tables of existing data + await db.run("TRUNCATE TABLE features_by_snp RESTART IDENTITY CASCADE"); + await db.run("TRUNCATE TABLE features_by_gene RESTART IDENTITY CASCADE"); + console.log('Pre-processing peak groups by SNP') // If there is more than one most significant peak, just choose the first one await db.runWithTransaction( From 2e3de7e40dbe74dd9fb8ca4285b31dcb2d7e45ba Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 11:00:06 -0400 Subject: [PATCH 084/105] fix: pull chromosome sizes from assembly not manhattan config --- client/src/components/pages/OverviewPage.js | 4 +++- routes/overview.mjs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/components/pages/OverviewPage.js b/client/src/components/pages/OverviewPage.js index 4ef30916..40c51079 100644 --- a/client/src/components/pages/OverviewPage.js +++ b/client/src/components/pages/OverviewPage.js @@ -32,6 +32,8 @@ const OverviewPage = () => { const {width} = useWindowDimensions(); + const {chromosomeSizes} = useSelector(state => state.assembly.data); + const { isLoading: configIsLoading, isLoaded: configIsLoaded, @@ -39,7 +41,7 @@ const OverviewPage = () => { } = useSelector(state => state.overview); const binSizeKb = ((config.binSize ?? 0) / 1000).toFixed(0); - const chroms = useMemo(() => Object.keys(config.chromosomeSizes ?? {}), [config]); + const chroms = useMemo(() => Object.keys(chromosomeSizes ?? {}), [chromosomeSizes]); const { isLoading: assaysIsLoading, isLoaded: assaysIsLoaded, diff --git a/routes/overview.mjs b/routes/overview.mjs index fe511073..940b1b17 100644 --- a/routes/overview.mjs +++ b/routes/overview.mjs @@ -11,12 +11,11 @@ import Peaks from "../models/peaks.mjs"; const router = express.Router(); router.get("/config", (_req, res) => { - const {chromosomeSizes, minPValue, binSize} = config?.plots?.manhattan ?? {}; + const {minPValue, binSize} = config?.plots?.manhattan ?? {}; dataHandler(res)({ binSize, minPValue, - chromosomeSizes, }); }); From 795ff75a442a760465240d807e36c9a5e91dba07 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Fri, 23 Jun 2023 11:01:45 -0400 Subject: [PATCH 085/105] fix(client): overview page crash --- client/src/components/pages/OverviewPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/pages/OverviewPage.js b/client/src/components/pages/OverviewPage.js index 40c51079..5ed0a9d6 100644 --- a/client/src/components/pages/OverviewPage.js +++ b/client/src/components/pages/OverviewPage.js @@ -32,7 +32,7 @@ const OverviewPage = () => { const {width} = useWindowDimensions(); - const {chromosomeSizes} = useSelector(state => state.assembly.data); + const {chromosomeSizes} = useSelector(state => state.assembly.data) ?? {}; const { isLoading: configIsLoading, From 96fc034162133a185e6c9e9b0410415368cef20e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 26 Jun 2023 09:54:23 -0400 Subject: [PATCH 086/105] docs: notes on genes and gene-features files --- readme.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 6a01d644..fc884dfe 100644 --- a/readme.md +++ b/readme.md @@ -59,7 +59,16 @@ The different data sources to generate/prepare are: ``` - **Genes:** list of gene names mapped to their characteristics. - **Import with:** `node ./scripts/import-genes.js` - - **Input:** `./input-files/flu-infection-genes.txt` + - **Input:** `./input-files/flu-infection-genes.txt` and + `./input-files/flu-infection-gene-peaks.csv` + - **Format:** + - `flu-infection-genes.txt`: TSV file with *no header row*. Columns are: + gene name, chromosome with `chr` prefix, start coordinate, end coordinate, + strand (`+` or `-`). + - `flu-infection-gene-peaks.csv`: CSV *with header row*: + `"symbol","peak_ids","feature_type"` where `symbol` is gene name, `peak_ids` + is a feature string (e.g., `chr1_9998_11177`), and `feature_type` is the name + of the assay. - **Notes:** The `name` column contains their name as provided in the input file, however there are inconsistencies in the notation of names, where sometimes the `.` and `-` will diverge from one source to another. Therefore, names are @@ -77,7 +86,8 @@ The different data sources to generate/prepare are: - `feature_type`: The assay the peak is from - e.g., `RNA-seq` Information on the QTL/peak list files: - - **Import with:** `node ./scripts/import-peaks.js` followed by `node ./scripts/calculate-peak-groups.js` + - **Import with:** `node ./scripts/import-peaks.js` followed by + `node ./scripts/calculate-peak-groups.js` - **Input:** `./input-files/qtls/QTLS_complete_*.csv` - **Config:** Use the `VARWIG_QTLS_TEMPLATE` environment variable to configure where QTL lists are loaded from. The `$ASSAY` string is replaced with each From 29978691436cb9b99ac4b0d6f9296d5b6f6c8753 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Mon, 26 Jun 2023 09:54:44 -0400 Subject: [PATCH 087/105] fix(scripts): import points with features from gene-peaks file --- scripts/import-genes.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/import-genes.js b/scripts/import-genes.js index 19f6d670..6837dd20 100644 --- a/scripts/import-genes.js +++ b/scripts/import-genes.js @@ -87,9 +87,12 @@ const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; const assayFeatures = geneAssayFeatures.flatMap(row => { const featureStr = row.peak_ids.slice(3); const feature = row.peak_ids.slice(3).split("_"); + const assayID = assaysByName[row.feature_type]; + const assayPoints = common.precomputedPoints[row.feature_type]; - const gene = genesByNormName[Gene.normalizeName(row.symbol)]; + const geneNameNorm = Gene.normalizeName(row.symbol); + const gene = genesByNormName[geneNameNorm]; if (!gene) return []; return [[ `${featureStr}:${assayID}`, @@ -98,13 +101,14 @@ const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; +feature.at(-1), assayID, gene, + assayPoints[row.symbol] ?? assayPoints[geneNameNorm], // Pre-computed points ]]; }); await db.insertMany( ` - INSERT INTO features ("nat_id", "chrom", "start", "end", "assay", "gene") - VALUES ($1, $2, $3, $4, $5, $6) + INSERT INTO features ("nat_id", "chrom", "start", "end", "assay", "gene", "points") + VALUES ($1, $2, $3, $4, $5, $6, $7) `, assayFeatures, ); From 2e2165e495c7525a3ee2f2cace04514006578436 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 4 Jul 2023 15:22:42 -0400 Subject: [PATCH 088/105] fix: loading precomputed points for features in gene-peaks file --- scripts/import-genes.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/import-genes.js b/scripts/import-genes.js index 6837dd20..54476050 100644 --- a/scripts/import-genes.js +++ b/scripts/import-genes.js @@ -86,13 +86,12 @@ const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; const assayFeatures = geneAssayFeatures.flatMap(row => { const featureStr = row.peak_ids.slice(3); - const feature = row.peak_ids.slice(3).split("_"); + const feature = featureStr.split("_"); const assayID = assaysByName[row.feature_type]; const assayPoints = common.precomputedPoints[row.feature_type]; - const geneNameNorm = Gene.normalizeName(row.symbol); - const gene = genesByNormName[geneNameNorm]; + const gene = genesByNormName[Gene.normalizeName(row.symbol)]; if (!gene) return []; return [[ `${featureStr}:${assayID}`, @@ -101,7 +100,7 @@ const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; +feature.at(-1), assayID, gene, - assayPoints[row.symbol] ?? assayPoints[geneNameNorm], // Pre-computed points + assayPoints[featureStr], // Pre-computed points ]]; }); From 5a2bddc82d942b50f4562fd2b593893e26ff605d Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 5 Jul 2023 11:48:12 -0400 Subject: [PATCH 089/105] fix: consistent donor lookup array between plot + ingest --- helpers/donors.mjs | 11 +++++++++ models/tracks.mjs | 10 +------- scripts/_common.js | 53 ---------------------------------------- scripts/_common.mjs | 54 +++++++++++++++++++++++++++++++++++++++++ scripts/import-genes.js | 6 ++--- scripts/import-peaks.js | 8 +++--- 6 files changed, 73 insertions(+), 69 deletions(-) create mode 100644 helpers/donors.mjs delete mode 100644 scripts/_common.js create mode 100644 scripts/_common.mjs diff --git a/helpers/donors.mjs b/helpers/donors.mjs new file mode 100644 index 00000000..9731c1db --- /dev/null +++ b/helpers/donors.mjs @@ -0,0 +1,11 @@ +import fs from "fs"; +import config from "../config.js"; + +const tracks = JSON.parse(fs.readFileSync(config.source.metadata.path).toString()); + +const donorLookupSet = new Set(); +tracks.forEach(t => { + donorLookupSet.add(`${t.donor}_${t.condition}`); +}); + +export const donorLookup = [...donorLookupSet].sort((a, b) => a.localeCompare(b)); diff --git a/models/tracks.mjs b/models/tracks.mjs index 928a2932..799185c8 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -18,6 +18,7 @@ import config from "../config.js"; import Samples from "./samples.mjs"; import source from "./source/index.js"; import {DEFAULT_CONDITIONS} from "../helpers/defaultValues.mjs"; +import {donorLookup} from "../helpers/donors.mjs"; import {normalizeChrom, GENOTYPE_STATES, GENOTYPE_STATE_NAMES} from "../helpers/genome.mjs"; const exists = promisify(fs.exists); @@ -44,15 +45,6 @@ const conditions = config.source?.conditions ?? DEFAULT_CONDITIONS; const TRACK_VALUES_CACHE_EXPIRY = 60 * 60 * 24 * 180; // 180 days -const tracks = JSON.parse(fs.readFileSync(config.source.metadata.path).toString()); - -const donorLookupSet = new Set(); -tracks.forEach(t => { - donorLookupSet.add(`${t.donor}_${t.condition}`); -}); - -const donorLookup = [...donorLookupSet].sort((a, b) => a.localeCompare(b)); - // Methods function get(peak) { diff --git a/scripts/_common.js b/scripts/_common.js deleted file mode 100644 index 48fb2ac4..00000000 --- a/scripts/_common.js +++ /dev/null @@ -1,53 +0,0 @@ -const fs = require('fs'); - -require('dotenv').config(); - -const config = require("../config"); - -const stripQuotes = str => str.replace(/"/g, "").trim(); - -// TODO: configurable -const ASSAYS = [ - 'RNA-seq', - 'ATAC-seq', - 'H3K4me1', - 'H3K4me3', - 'H3K27ac', - 'H3K27me3', -]; - -const loadingPrecomputedPoints = !!config.paths.pointTemplate; -const precomputedPoints = {}; - -if (loadingPrecomputedPoints) { - console.log("Pre-loading precomputed point matrices"); - - ASSAYS.forEach(assay => { - const fc = fs.readFileSync(config.paths.pointTemplate.replace(/\$ASSAY/g, assay)) - .toString() - .split("\n"); - - const sortedSampleNamesAndIndices = fc[0] - .split("\t") - .map(s => stripQuotes(s)) - .filter(s => s !== "") - .map((s, si) => [s, si]) - .sort((a, b) => a[0].localeCompare(b[0])); - - precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x) - .map(featureRow => { - const rd = featureRow.split("\t"); - return [ - stripQuotes(rd[0]).replace(/^chr/, ""), - sortedSampleNamesAndIndices.map(([_, si]) => parseFloat(rd[si + 1])), - ]; - })); - }); -} - -module.exports = { - stripQuotes, - ASSAYS, - loadingPrecomputedPoints, - precomputedPoints, -}; diff --git a/scripts/_common.mjs b/scripts/_common.mjs new file mode 100644 index 00000000..25fbbd40 --- /dev/null +++ b/scripts/_common.mjs @@ -0,0 +1,54 @@ +import fs from "fs"; + +import dotenv from "dotenv"; +dotenv.config(); + +import config from "../config.js"; +import {donorLookup} from "../helpers/donors.mjs"; + +export const stripQuotes = str => str.replace(/"/g, "").trim(); + +// TODO: configurable +export const ASSAYS = [ + 'RNA-seq', + 'ATAC-seq', + 'H3K4me1', + 'H3K4me3', + 'H3K27ac', + 'H3K27me3', +]; + +export const loadingPrecomputedPoints = !!config.paths.pointTemplate; +export const precomputedPoints = {}; + +if (loadingPrecomputedPoints) { + console.log("Pre-loading precomputed point matrices"); + + ASSAYS.forEach(assay => { + const fc = fs.readFileSync(config.paths.pointTemplate.replace(/\$ASSAY/g, assay)) + .toString() + .split("\n"); + + const sampleNamesAndIndices = Object.fromEntries( + fc[0] + .split("\t") + .map(s => stripQuotes(s)) + .filter(s => s !== "") + .map((s, si) => [s, si]) + ); + + precomputedPoints[assay] = Object.fromEntries(fc.slice(1).filter(x => !!x) + .map(featureRow => { + const rd = featureRow.split("\t"); + return [ + stripQuotes(rd[0]).replace(/^chr/, ""), + donorLookup.map(sn => { + const idx = sampleNamesAndIndices[sn]; + return idx === undefined + ? null + : parseFloat(rd[idx + 1]); // Add one due to funny off-by-one in sample name header + }), + ]; + })); + }); +} diff --git a/scripts/import-genes.js b/scripts/import-genes.js index 54476050..841d8658 100644 --- a/scripts/import-genes.js +++ b/scripts/import-genes.js @@ -5,14 +5,14 @@ const parseCSVSync = require("csv-parse/lib/sync"); require('dotenv').config(); const config = require("../config"); -const common = require("./_common"); +const {precomputedPoints} = require("./_common.mjs"); const ASSAY_NAME_RNASEQ = "RNA-seq"; const genesPath = path.join(config.inputFilesDirname, 'flu-infection-genes.txt'); const genesFeaturesPath = path.join(config.inputFilesDirname, 'flu-infection-gene-peaks.csv'); -const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; +const rnaSeqPrecomputed = precomputedPoints[ASSAY_NAME_RNASEQ]; (async () => { const db = await import("../models/db.mjs"); @@ -89,7 +89,7 @@ const rnaSeqPrecomputed = common.precomputedPoints[ASSAY_NAME_RNASEQ]; const feature = featureStr.split("_"); const assayID = assaysByName[row.feature_type]; - const assayPoints = common.precomputedPoints[row.feature_type]; + const assayPoints = precomputedPoints[row.feature_type]; const gene = genesByNormName[Gene.normalizeName(row.symbol)]; if (!gene) return []; diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 86e5c440..58b773a0 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -7,9 +7,9 @@ const copyFrom = require('pg-copy-streams').from; require('dotenv').config(); const config = require('../config'); -const common = require('./_common'); +const {ASSAYS, loadingPrecomputedPoints, precomputedPoints} = require('./_common.mjs'); -const datasetPaths = common.ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); +const datasetPaths = ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); console.log("Loading peaks"); @@ -50,8 +50,8 @@ console.log("Loading peaks"); return featureCache[naturalKey]; } - const points = common.loadingPrecomputedPoints - ? (common.precomputedPoints[assayStr][feature] ?? null) + const points = loadingPrecomputedPoints + ? (precomputedPoints[assayStr][feature] ?? null) : null; const fs = feature.split("_"); From 55c9405b397e1578608c2e01378ff40e28fe4bc4 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 5 Jul 2023 11:48:26 -0400 Subject: [PATCH 090/105] docs: require metadata ingest before points ingest --- readme.md | 72 +++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/readme.md b/readme.md index fc884dfe..80ab7945 100644 --- a/readme.md +++ b/readme.md @@ -57,6 +57,42 @@ The different data sources to generate/prepare are: // ... }; ``` + - **Metadata:** This is the track's metadata. This can either be provided as an + XLSX file with the headers: + - `file.path` + - `ethnicity` + - `condition` + - `institution.short_name` + - `sample_name` + - `donor` + - `track.view` + - `track.track_type` + - `assembly.name` + - `assay.name` + - `assay_category.name` + + or a JSON file containing a list of objects with (similar) keys: + - `path` + - `ethnicity` + - `condition` + - `short_name` + - `sample_name` + - `donor` + - `view` + - `type` + - `assembly` + - `assay` + - `assay_id` + - `assay_category` + - `assay_category_id` + + Information on the track metadata file: + - **Generate with:** `node ./scripts/metadata-to-json.js` + - **Input:** `./input-files/flu-infection.xlsx` + - **Output:** `./data/metadata.json` + - **Config:** `config.source.metadata.path` (filepath) + - **Notes:** This is really just an XLSX to JSON transformation. + - **Genes:** list of gene names mapped to their characteristics. - **Import with:** `node ./scripts/import-genes.js` - **Input:** `./input-files/flu-infection-genes.txt` and @@ -97,42 +133,6 @@ The different data sources to generate/prepare are: at the range `chr1:3500-3600`. The second script calculates peak groups by SNP and gene for auto-complete. - - **Metadata:** This is the track's metadata. This can either be provided as an - XLSX file with the headers: - - `file.path` - - `ethnicity` - - `condition` - - `institution.short_name` - - `sample_name` - - `donor` - - `track.view` - - `track.track_type` - - `assembly.name` - - `assay.name` - - `assay_category.name` - - or a JSON file containing a list of objects with (similar) keys: - - `path` - - `ethnicity` - - `condition` - - `short_name` - - `sample_name` - - `donor` - - `view` - - `type` - - `assembly` - - `assay` - - `assay_id` - - `assay_category` - - `assay_category_id` - - Information on the track metadata file: - - **Generate with:** `node ./scripts/metadata-to-json.js` - - **Input:** `./input-files/flu-infection.xlsx` - - **Output:** `./data/metadata.json` - - **Config:** `config.source.metadata.path` (filepath) - - **Notes:** This is really just an XLSX to JSON transformation. - - **Binned top peaks for assays:** Used to generate Manhattan plots for chromosome/assay pairs, binned by SNP position. - **Generate with:** `node ./scripts/calculate-top-peaks.js` From f2e0e1a46dc75c5cdba5a63c2534fc7298539d70 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 5 Jul 2023 11:57:05 -0400 Subject: [PATCH 091/105] fix: script imports --- scripts/import-genes.js | 6 ++++-- scripts/import-peaks.js | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/import-genes.js b/scripts/import-genes.js index 841d8658..be6aa3e2 100644 --- a/scripts/import-genes.js +++ b/scripts/import-genes.js @@ -5,19 +5,21 @@ const parseCSVSync = require("csv-parse/lib/sync"); require('dotenv').config(); const config = require("../config"); -const {precomputedPoints} = require("./_common.mjs"); const ASSAY_NAME_RNASEQ = "RNA-seq"; const genesPath = path.join(config.inputFilesDirname, 'flu-infection-genes.txt'); const genesFeaturesPath = path.join(config.inputFilesDirname, 'flu-infection-gene-peaks.csv'); -const rnaSeqPrecomputed = precomputedPoints[ASSAY_NAME_RNASEQ]; (async () => { + const {precomputedPoints} = await import("./_common.mjs"); + const db = await import("../models/db.mjs"); const Gene = await import('../models/genes.mjs'); + const rnaSeqPrecomputed = precomputedPoints[ASSAY_NAME_RNASEQ]; + const assaysByName = Object.fromEntries((await db.findAll("SELECT * FROM assays")).map(r => [r.name, r.id])); const rnaSeq = assaysByName[ASSAY_NAME_RNASEQ]; diff --git a/scripts/import-peaks.js b/scripts/import-peaks.js index 58b773a0..05e61ca8 100644 --- a/scripts/import-peaks.js +++ b/scripts/import-peaks.js @@ -7,18 +7,19 @@ const copyFrom = require('pg-copy-streams').from; require('dotenv').config(); const config = require('../config'); -const {ASSAYS, loadingPrecomputedPoints, precomputedPoints} = require('./_common.mjs'); - -const datasetPaths = ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); console.log("Loading peaks"); // -------------------------------------------------------------------------- (async () => { + const {ASSAYS, loadingPrecomputedPoints, precomputedPoints} = await import('./_common.mjs'); + const db = await import("../models/db.mjs"); const Gene = await import('../models/genes.mjs'); + const datasetPaths = ASSAYS.map(assay => config.paths.qtlsTemplate.replace(/\$ASSAY/g, assay)); + // Clear relevant tables of existing data await db.run("TRUNCATE TABLE snps RESTART IDENTITY CASCADE"); await db.run("TRUNCATE TABLE peaks RESTART IDENTITY CASCADE"); From b86bc13832e5be61098d426e08ebdc59f095de0e Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 5 Jul 2023 14:17:53 -0400 Subject: [PATCH 092/105] fix: null handling for precomputed points --- models/tracks.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 799185c8..89547fc0 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -79,7 +79,8 @@ async function values(peak, usePrecomputed = false) { console.log("points", peak.feature.points); const pointIdx = donorLookup.indexOf(`${track.donor}_${track.condition}`); if (pointIdx === -1) return Promise.resolve(undefined); - return Promise.resolve(peak.feature.points?.[pointIdx]); + // Return the point value, turning SQL NULLs into undefined if the value isn't present for this sample + return Promise.resolve(peak.feature.points?.[pointIdx] ?? undefined); }; } From 2adcf0e057a9fcae4c091086b2160daffa0c7f9c Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:10:34 -0400 Subject: [PATCH 093/105] debug logging --- models/tracks.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/tracks.mjs b/models/tracks.mjs index 89547fc0..17fc003a 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -103,6 +103,8 @@ async function values(peak, usePrecomputed = false) { })) ))).filter(v => v !== undefined); + console.log("result", usePrecomputed, result) + await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); return result; From 34f66a577b35b889cf075a1b7b1a1f64b5d5a784 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:12:51 -0400 Subject: [PATCH 094/105] chore(client): re-enable checkbox for precomputed --- client/src/components/PeakAssay.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index cf2265e2..2f0bc51d 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -74,12 +74,12 @@ const PeakAssay = ({peaks}) => { onOpenTracks={onOpenTracks} /> - {/**/} - {/* */} - {/**/} + + + Date: Thu, 6 Jul 2023 09:13:38 -0400 Subject: [PATCH 095/105] lint: clean up debug logging --- models/tracks.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/models/tracks.mjs b/models/tracks.mjs index 17fc003a..ded8fcaa 100644 --- a/models/tracks.mjs +++ b/models/tracks.mjs @@ -76,7 +76,6 @@ async function values(peak, usePrecomputed = false) { if (usePrecomputed) { // Replace getter function with one which extracts the precomputed point value. getValueForTrack = track => { - console.log("points", peak.feature.points); const pointIdx = donorLookup.indexOf(`${track.donor}_${track.condition}`); if (pointIdx === -1) return Promise.resolve(undefined); // Return the point value, turning SQL NULLs into undefined if the value isn't present for this sample @@ -103,8 +102,6 @@ async function values(peak, usePrecomputed = false) { })) ))).filter(v => v !== undefined); - console.log("result", usePrecomputed, result) - await cache.setJSON(k, result, TRACK_VALUES_CACHE_EXPIRY); return result; From 3cdbfc635a0bc089240542eeaa90c3f7cfc676a2 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:14:18 -0400 Subject: [PATCH 096/105] chore(client): re-enable precomputed checkbox --- client/src/components/PeakAssay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 2f0bc51d..e78c3e04 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -76,7 +76,7 @@ const PeakAssay = ({peaks}) => { From ad40023dd90f57789b03ea0733cdff85e15a7a32 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:16:45 -0400 Subject: [PATCH 097/105] chore(client): use precomputed points by default --- client/src/reducers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/reducers.js b/client/src/reducers.js index 8289aea0..0580e159 100644 --- a/client/src/reducers.js +++ b/client/src/reducers.js @@ -14,7 +14,7 @@ const defaultUI = { assay: '', }, - usePrecomputed: false, + usePrecomputed: true, }; function uiReducer(state = defaultUI, action) { From d5cd0f9495956d0f887e3fe4730b2fc03112cfff Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:28:25 -0400 Subject: [PATCH 098/105] fix(client): properly include precomputed setting in value cache posts --- client/src/api.js | 3 ++- client/src/components/PeakAssay.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/api.js b/client/src/api.js index f005b6c3..520103e3 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -49,13 +49,14 @@ export const fetchManhattanData = ({chrom, assay}) => get(`/overview/assays/${as * @property {string} feature.chrom * @property {number} feature.start * @property {number} feature.end + * @property {boolean} usePrecomputed */ /** * @param {ValuesOptions} params */ export function cacheValues(params) { - return post('/tracks/values', params) + return post(`/tracks/values?precomputed=${params.usePrecomputed ? '1' : '0'}`, params) } export function createSession(params) { diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index e78c3e04..339b2f17 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -48,7 +48,7 @@ const PeakAssay = ({peaks}) => { .filter(p => !exclude.includes(p.id)) .slice(0, 10) .forEach(p => { - dispatch(cacheValues(p, {id: p.id})); + dispatch(cacheValues({...p, usePrecomputed}, {id: p.id})); }), [peaks, dispatch]); From e7678f53eb9c454115b24b016834f281902abf32 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:30:44 -0400 Subject: [PATCH 099/105] docs: update wording around link to current instance --- readme.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 80ab7945..7e89d503 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,11 @@ # The EpiVar Browser -A web application to search for variants and merge bigWig tracks; available at -[https://flu-infection.vhost38.genap.ca/](https://flu-infection.vhost38.genap.ca/). +A web application to search for variants and merge bigWig tracks. A production +instance with data from Aracena *et al.* +([2022 preprint](https://www.biorxiv.org/content/10.1101/2022.05.10.491413v1)) +is available at +[https://computationalgenomics.ca/tools/epivar](https://computationalgenomics.ca/tools/epivar). -If linking publicly, **use the permalink**: -[https://computationalgenomics.ca/tools/epivar](https://computationalgenomics.ca/tools/epivar). ## Installation From d3c9d94f91f3bb1294d249e278d593083076b171 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:34:51 -0400 Subject: [PATCH 100/105] fix(client): missing usePrecomputed pass to cacheValues --- client/src/components/PeakAssay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 339b2f17..08f254b9 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -55,7 +55,7 @@ const PeakAssay = ({peaks}) => { // Fetch some peaks at the start for performance useEffect(() => { if (selectedPeakData) { - dispatch(cacheValues(selectedPeakData, {id: selectedPeak})); + dispatch(cacheValues({...selectedPeakData, usePrecomputed}, {id: selectedPeak})); // Give some time for the first one to get priority setTimeout(() => fetchSome([selectedPeak]), 100); } else { From 725fc012415b5997d9f518fc3393828449819235 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:46:21 -0400 Subject: [PATCH 101/105] chore(client): try debugging pre-cache --- client/src/components/PeakAssay.js | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 08f254b9..0472f8fe 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -43,25 +43,25 @@ const PeakAssay = ({peaks}) => { const selectedPeakData = peaks.find(p => p.id === selectedPeak); - const fetchSome = useCallback((exclude = []) => - peaks - .filter(p => !exclude.includes(p.id)) - .slice(0, 10) - .forEach(p => { - dispatch(cacheValues({...p, usePrecomputed}, {id: p.id})); - }), - [peaks, dispatch]); + // const fetchSome = useCallback((exclude = []) => + // peaks + // .filter(p => !exclude.includes(p.id)) + // .slice(0, 10) + // .forEach(p => { + // dispatch(cacheValues({...p, usePrecomputed}, {id: p.id})); + // }), + // [peaks, dispatch]); // Fetch some peaks at the start for performance - useEffect(() => { - if (selectedPeakData) { - dispatch(cacheValues({...selectedPeakData, usePrecomputed}, {id: selectedPeak})); - // Give some time for the first one to get priority - setTimeout(() => fetchSome([selectedPeak]), 100); - } else { - fetchSome(); - } - }, [selectedPeakData, fetchSome]); + // useEffect(() => { + // if (selectedPeakData) { + // dispatch(cacheValues({...selectedPeakData, usePrecomputed}, {id: selectedPeak})); + // // Give some time for the first one to get priority + // setTimeout(() => fetchSome([selectedPeak]), 100); + // } else { + // fetchSome(); + // } + // }, [selectedPeakData, fetchSome]); return ( From 172322db0b55e0a038fc011a3350706fc43ea772 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:48:32 -0400 Subject: [PATCH 102/105] perf: disable pre-caching requests --- client/src/actions.js | 2 +- client/src/api.js | 22 +++++++++++----------- client/src/components/PeakAssay.js | 22 +--------------------- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/client/src/actions.js b/client/src/actions.js index 73e726ae..ecee0c40 100644 --- a/client/src/actions.js +++ b/client/src/actions.js @@ -30,7 +30,7 @@ export const messages = createFetchActions(k.MESSAGES); export const fetchAssays = createFetchFunction(api.fetchAssays, assays); // export const fetchChroms = createFetchFunction(api.fetchChroms, chroms); export const fetchPositions = createFetchFunction(api.fetchPositions, positions); -export const cacheValues = createFetchFunction(api.cacheValues, values); +// export const cacheValues = createFetchFunction(api.cacheValues, values); export const fetchPeaks = createFetchFunction(api.fetchPeaks, peaks); export const fetchAssembly = createFetchFunction(api.fetchAssembly, assembly); export const fetchConditions = createFetchFunction(api.fetchConditions, conditions); diff --git a/client/src/api.js b/client/src/api.js index 520103e3..7ab1500f 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -18,14 +18,14 @@ export function fetchPeaks(params) { return get('/peaks/query', params) } -export function fetchChroms() { - return get('/autocomplete/chroms') - .then(chroms => { - const parse = string => +string.slice(3) - chroms.sort((a, b) => parse(a) - parse(b)) - return chroms - }) -} +// export function fetchChroms() { +// return get('/autocomplete/chroms') +// .then(chroms => { +// const parse = string => +string.slice(3) +// chroms.sort((a, b) => parse(a) - parse(b)) +// return chroms +// }) +// } export function fetchPositions(params, cancelToken) { return get('/autocomplete/positions', params, {cancelToken}) @@ -55,9 +55,9 @@ export const fetchManhattanData = ({chrom, assay}) => get(`/overview/assays/${as /** * @param {ValuesOptions} params */ -export function cacheValues(params) { - return post(`/tracks/values?precomputed=${params.usePrecomputed ? '1' : '0'}`, params) -} +// export function cacheValues(params) { +// return post(`/tracks/values?precomputed=${params.usePrecomputed ? '1' : '0'}`, params) +// } export function createSession(params) { return post('/sessions/create', params) diff --git a/client/src/components/PeakAssay.js b/client/src/components/PeakAssay.js index 0472f8fe..a73c0e91 100644 --- a/client/src/components/PeakAssay.js +++ b/client/src/components/PeakAssay.js @@ -15,7 +15,7 @@ import {useTable, usePagination, useSortBy} from "react-table"; import Icon from "./Icon"; import PeakBoxplot from "./PeakBoxplot"; -import {cacheValues, mergeTracks, setUsePrecomputed} from "../actions"; +import {mergeTracks, setUsePrecomputed} from "../actions"; const PAGE_SIZES = [10, 20, 30, 40, 50]; @@ -43,26 +43,6 @@ const PeakAssay = ({peaks}) => { const selectedPeakData = peaks.find(p => p.id === selectedPeak); - // const fetchSome = useCallback((exclude = []) => - // peaks - // .filter(p => !exclude.includes(p.id)) - // .slice(0, 10) - // .forEach(p => { - // dispatch(cacheValues({...p, usePrecomputed}, {id: p.id})); - // }), - // [peaks, dispatch]); - - // Fetch some peaks at the start for performance - // useEffect(() => { - // if (selectedPeakData) { - // dispatch(cacheValues({...selectedPeakData, usePrecomputed}, {id: selectedPeak})); - // // Give some time for the first one to get priority - // setTimeout(() => fetchSome([selectedPeak]), 100); - // } else { - // fetchSome(); - // } - // }, [selectedPeakData, fetchSome]); - return ( From 9991e915c999e7f1e051ebb2a9be1bc2ed79d77b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:55:17 -0400 Subject: [PATCH 103/105] chore: update server dependencies --- package-lock.json | 40 ++++++++++++++++++++-------------------- package.json | 4 ++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd7cd8ec..d50f9e5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,10 +30,10 @@ "mysql": "^2.18.1", "passport": "^0.6.0", "passport-openidconnect": "^0.1.1", - "pg": "^8.11.0", + "pg": "^8.11.1", "pg-copy-streams": "^6.0.5", "pug": "~3.0.2", - "rambda": "^7.5.0", + "rambda": "^8.1.0", "redis": "~4.6.7", "serve-favicon": "~2.5.0", "sharp": "^0.32.1" @@ -1902,14 +1902,14 @@ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "node_modules/pg": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.0.tgz", - "integrity": "sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA==", + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.1.tgz", + "integrity": "sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==", "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.0", - "pg-pool": "^3.6.0", + "pg-connection-string": "^2.6.1", + "pg-pool": "^3.6.1", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", "pgpass": "1.x" @@ -1918,7 +1918,7 @@ "node": ">= 8.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.1.0" + "pg-cloudflare": "^1.1.1" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -1930,15 +1930,15 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.0.tgz", - "integrity": "sha512-tGM8/s6frwuAIyRcJ6nWcIvd3+3NmUKIs6OjviIm1HPPFEt5MzQDOTBQyhPWg/m0kCl95M6gA1JaIXtS8KovOA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.0.tgz", - "integrity": "sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" }, "node_modules/pg-copy-streams": { "version": "6.0.5", @@ -1957,9 +1957,9 @@ } }, "node_modules/pg-pool": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.0.tgz", - "integrity": "sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", "peerDependencies": { "pg": ">=8.0" } @@ -2322,9 +2322,9 @@ } }, "node_modules/rambda": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", - "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-8.1.0.tgz", + "integrity": "sha512-S6+0BH0N7PQVmYT6kE37DL5hxmnD6fBh3rfYOZSNVFpLjL6NQRYvHBo27WR4derYOnrOIhoXIzXNsTBZU3oZLg==" }, "node_modules/random-bytes": { "version": "1.0.0", diff --git a/package.json b/package.json index 0702bef3..8f587580 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,10 @@ "mysql": "^2.18.1", "passport": "^0.6.0", "passport-openidconnect": "^0.1.1", - "pg": "^8.11.0", + "pg": "^8.11.1", "pg-copy-streams": "^6.0.5", "pug": "~3.0.2", - "rambda": "^7.5.0", + "rambda": "^8.1.0", "redis": "~4.6.7", "serve-favicon": "~2.5.0", "sharp": "^0.32.1" From 5a688bec822e0e8cef668d054d30c8285a4b34fa Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 09:56:02 -0400 Subject: [PATCH 104/105] chore(client): update client dependencies --- client/package-lock.json | 34 +++++++++++++++++----------------- client/package.json | 4 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index f63d613e..060d40a3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -18,11 +18,11 @@ "d3-scale": "^4.0.2", "font-awesome": "^4.7.0", "memoize-one": "^6.0.0", - "rambda": "^7.5.0", + "rambda": "^8.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.1", - "react-router-dom": "^6.13.0", + "react-router-dom": "^6.14.1", "react-table": "^7.8.0", "reactstrap": "^8.10.1", "redux": "^4.2.1", @@ -3100,9 +3100,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz", - "integrity": "sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.1.tgz", + "integrity": "sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ==", "engines": { "node": ">=14" } @@ -14063,9 +14063,9 @@ } }, "node_modules/rambda": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", - "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-8.1.0.tgz", + "integrity": "sha512-S6+0BH0N7PQVmYT6kE37DL5hxmnD6fBh3rfYOZSNVFpLjL6NQRYvHBo27WR4derYOnrOIhoXIzXNsTBZU3oZLg==" }, "node_modules/randombytes": { "version": "2.1.0", @@ -14386,11 +14386,11 @@ } }, "node_modules/react-router": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.13.0.tgz", - "integrity": "sha512-Si6KnfEnJw7gUQkNa70dlpI1bul46FuSxX5t5WwlUBxE25DAz2BjVkwaK8Y2s242bQrZPXCpmwLPtIO5pv4tXg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.1.tgz", + "integrity": "sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g==", "dependencies": { - "@remix-run/router": "1.6.3" + "@remix-run/router": "1.7.1" }, "engines": { "node": ">=14" @@ -14400,12 +14400,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.13.0.tgz", - "integrity": "sha512-6Nqoqd7fgwxxVGdbiMHTpDHCYPq62d7Wk1Of7B82vH7ZPwwsRaIa22zRZKPPg413R5REVNiyuQPKDG1bubcOFA==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.1.tgz", + "integrity": "sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw==", "dependencies": { - "@remix-run/router": "1.6.3", - "react-router": "6.13.0" + "@remix-run/router": "1.7.1", + "react-router": "6.14.1" }, "engines": { "node": ">=14" diff --git a/client/package.json b/client/package.json index d7f06d21..90a4792b 100644 --- a/client/package.json +++ b/client/package.json @@ -13,11 +13,11 @@ "d3-scale": "^4.0.2", "font-awesome": "^4.7.0", "memoize-one": "^6.0.0", - "rambda": "^7.5.0", + "rambda": "^8.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.1", - "react-router-dom": "^6.13.0", + "react-router-dom": "^6.14.1", "react-table": "^7.8.0", "reactstrap": "^8.10.1", "redux": "^4.2.1", From 2569c2b088f55386d38f7044225b67d9ec06da9b Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Thu, 6 Jul 2023 10:02:06 -0400 Subject: [PATCH 105/105] chore(client): run npm audit --- client/package-lock.json | 2647 ++++++++++++++++++++++++-------------- 1 file changed, 1662 insertions(+), 985 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 060d40a3..3ac9321a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -37,60 +37,70 @@ "react-scripts": "^5.0.1" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "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==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.22.5" }, "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==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", + "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "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.9", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.9", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0", + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.8.tgz", + "integrity": "sha512-75+KxFB4CZqYRXjx4NlR4J7yGvKumBuZTmV4NV6v09dVXXkuYVYLT68N6HCzLvfJ+fWCxQsntNzKwwIXL4bHnw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.2" }, "engines": { "node": ">=6.9.0" @@ -100,24 +110,15 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/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/@babel/eslint-parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", - "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.7.tgz", + "integrity": "sha512-LH6HJqjOyu/Qtp7LuSycZXK/CYXQ4ohdkliEaL1QTdtOXVdOVpTBKVxAo/+eeyt+x/2SRzB+zUPduVl+xiEvdg==", "dev": true, "dependencies": { - "eslint-scope": "^5.1.1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "eslint-visitor-keys": "^2.1.0" }, "engines": { "node": "^10.13.0 || ^12.13.0 || >=14.0.0" @@ -127,19 +128,6 @@ "eslint": "^7.5.0 || ^8.0.0" } }, - "node_modules/@babel/eslint-parser/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", @@ -149,73 +137,56 @@ "node": ">=10" } }, - "node_modules/@babel/eslint-parser/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/@babel/eslint-parser/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/@babel/generator": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", - "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.7.tgz", + "integrity": "sha512-p+jPjMG+SI8yvIaxGgeW24u7q9+5+TGpZh8/CuB7RhBKd7RCy8FayNEFNNKrNK/eUcY/4ExQqLmyrvBXKsIcwQ==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", "dev": true, "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", + "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" + "@babel/compat-data": "^7.22.6", + "@babel/helper-validator-option": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1" }, "engines": { "node": ">=6.9.0" @@ -224,28 +195,36 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/@babel/helper-compilation-targets/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, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "yallist": "^3.0.2" } }, + "node_modules/@babel/helper-compilation-targets/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/@babel/helper-create-class-features-plugin": { - "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==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz", + "integrity": "sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^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" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@nicolo-ribaudo/semver-v6": "^6.3.3" }, "engines": { "node": ">=6.9.0" @@ -255,13 +234,14 @@ } }, "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==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz", + "integrity": "sha512-nBookhLKxAWo/TUCmhnaEJyLz2dekjQvv5SRpE9epWQBcpedWLKt8aZdsuT9XV5ovzR3fENLjRXVT0GsSlGGhA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "regexpu-core": "^5.3.1" }, "engines": { "node": ">=6.9.0" @@ -271,266 +251,253 @@ } }, "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==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", + "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", "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", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" + "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0-0" } }, - "node_modules/@babel/helper-define-polyfill-provider/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/@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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "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.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", "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" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "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/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", + "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", + "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", "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" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.22.5" }, "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==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", + "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", "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" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -539,9 +506,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", - "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -551,12 +518,12 @@ } }, "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==", + "version": "7.22.5", + "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.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -566,14 +533,14 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", "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" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -582,23 +549,6 @@ "@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", @@ -615,23 +565,6 @@ "@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.9", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.9.tgz", @@ -652,70 +585,6 @@ "@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-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", @@ -748,41 +617,6 @@ "@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", @@ -817,16 +651,10 @@ } }, "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==", + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "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" }, @@ -955,6 +783,36 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -1018,61 +876,305 @@ "@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==", + "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-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", + "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" }, - "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" + "engines": { + "node": ">=6.9.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==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.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==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.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==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1081,13 +1183,14 @@ "@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==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1096,13 +1199,14 @@ "@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==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1111,13 +1215,14 @@ "@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==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1126,15 +1231,14 @@ "@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==", + "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-module-imports": "^7.16.7", "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" + "@babel/plugin-syntax-flow": "^7.16.7" }, "engines": { "node": ">=6.9.0" @@ -1143,13 +1247,13 @@ "@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==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1158,13 +1262,15 @@ "@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==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1173,20 +1279,14 @@ "@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==", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", "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" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1195,13 +1295,13 @@ "@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==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1210,13 +1310,14 @@ "@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==", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { "node": ">=6.9.0" @@ -1225,14 +1326,13 @@ "@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==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1241,13 +1341,14 @@ "@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==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1256,14 +1357,15 @@ "@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==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1272,14 +1374,16 @@ "@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==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-flow": "^7.16.7" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1288,13 +1392,14 @@ "@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==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1303,30 +1408,29 @@ "@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==", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.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==", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1335,13 +1439,14 @@ "@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==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1350,15 +1455,14 @@ "@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==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/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" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { "node": ">=6.9.0" @@ -1367,16 +1471,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "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==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", "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" + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1385,17 +1490,14 @@ "@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==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "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" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1404,14 +1506,14 @@ "@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==", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1420,28 +1522,30 @@ "@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==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.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==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1450,14 +1554,14 @@ "@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==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1466,13 +1570,16 @@ "@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==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { "node": ">=6.9.0" @@ -1482,12 +1589,12 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1577,12 +1684,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", "dev": true, "dependencies": { - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" @@ -1592,12 +1700,12 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1607,17 +1715,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz", - "integrity": "sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.7.tgz", + "integrity": "sha512-o02xM7iY7mSPI+TvaYDH0aYl+lg3+KT7qrD705JlsB/GrZSNaYO/4i+aDFKPiJ7ubq3hgv8NNLCdyB5MFxT8mg==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "semver": "^6.3.0" + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1" }, "engines": { "node": ">=6.9.0" @@ -1626,22 +1734,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime/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/@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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1651,13 +1750,13 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1667,12 +1766,12 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1682,12 +1781,12 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1697,12 +1796,12 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1729,12 +1828,28 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1744,13 +1859,13 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1759,38 +1874,43 @@ "@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==", + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", "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/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.7.tgz", + "integrity": "sha512-1whfDtW+CzhETuzYXfcgZAh8/GFMeEbz0V5dVgya8YeJyCU6Y/P2Gnx4Qb3MylK68Zu9UiwUvbPMPTpFAOJ+sQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@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-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@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", @@ -1800,45 +1920,62 @@ "@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/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", "@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" + "@babel/types": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "core-js-compat": "^3.31.0" }, "engines": { "node": ">=6.9.0" @@ -1847,15 +1984,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env/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/@babel/preset-modules": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", @@ -1909,6 +2037,12 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, "node_modules/@babel/runtime": { "version": "7.21.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", @@ -1934,33 +2068,33 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "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.9", - "@babel/helper-environment-visitor": "^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.9", - "@babel/types": "^7.17.0", + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1969,12 +2103,13 @@ } }, "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==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2685,6 +2820,18 @@ "node": ">=8" } }, + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, "node_modules/@jest/source-map": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", @@ -2983,15 +3130,55 @@ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", + "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==", + "dev": true + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", - "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==", - "dev": true + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/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/@nicolo-ribaudo/semver-v6": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", + "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -3192,6 +3379,12 @@ "integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==", "dev": true }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true + }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -4477,6 +4670,19 @@ "node": ">=6.0" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -4484,15 +4690,15 @@ "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==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "is-string": "^1.0.7" }, "engines": { @@ -4530,14 +4736,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -4547,6 +4753,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -4615,6 +4834,18 @@ "postcss": "^8.1.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/axe-core": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", @@ -4746,9 +4977,9 @@ } }, "node_modules/babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", "dev": true, "dependencies": { "find-cache-dir": "^3.3.1", @@ -4782,15 +5013,6 @@ "url": "https://opencollective.com/webpack" } }, - "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-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -4847,48 +5069,39 @@ } }, "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==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", + "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", - "semver": "^6.1.1" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.1", + "@nicolo-ribaudo/semver-v6": "^6.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-plugin-polyfill-corejs2/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/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==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", + "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.4.1", + "core-js-compat": "^3.31.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==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", + "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1" + "@babel/helper-define-polyfill-provider": "^0.4.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -5152,9 +5365,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -5164,14 +5377,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "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" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -5281,9 +5497,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001332", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", - "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "version": "1.0.30001512", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", + "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==", "dev": true, "funding": [ { @@ -5293,6 +5509,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -5683,28 +5903,18 @@ "dev": true }, "node_modules/core-js-compat": { - "version": "3.22.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.1.tgz", - "integrity": "sha512-CWbNqTluLMvZg1cjsQUbGiCM91dobSHKfDIyCoxuqxthdjGuUlaMbCsSehP3CBiVvG0C7P6UIrC1v0hgFE75jw==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", + "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", "dev": true, "dependencies": { - "browserslist": "^4.20.2", - "semver": "7.0.0" + "browserslist": "^4.21.9" }, "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.22.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.1.tgz", @@ -6358,14 +6568,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.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "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/defined": { @@ -6670,9 +6884,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.114", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.114.tgz", - "integrity": "sha512-gRwLpVYWHGbERPU6o8pKfR168V6enWEXzZc6zQNNXbgJ7UJna+9qzAIHY94+9KOv71D/CH+QebLA9pChD2q8zA==", + "version": "1.4.451", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.451.tgz", + "integrity": "sha512-YYbXHIBxAHe3KWvGOJOuWa6f3tgow44rBW+QAuwVp2DvGqNZeE//K2MowNdWS7XE8li5cgQDrX1LdBr41LufkA==", "dev": true }, "node_modules/emittery": { @@ -6752,31 +6966,45 @@ } }, "node_modules/es-abstract": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", "dev": true, "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.3", "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" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -6791,6 +7019,20 @@ "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", "dev": true }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -7235,25 +7477,26 @@ } }, "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==", + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "dev": true, "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", "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", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", + "resolve": "^2.0.0-next.4", "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" + "string.prototype.matchall": "^4.0.8" }, "engines": { "node": ">=4" @@ -7287,13 +7530,17 @@ } }, "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==", + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8023,6 +8270,15 @@ "node": ">=0.10.3" } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.1.tgz", @@ -8280,6 +8536,24 @@ "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/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -8313,13 +8587,14 @@ } }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8453,6 +8728,21 @@ "node": ">=4" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "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", @@ -8473,6 +8763,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -8523,9 +8825,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" @@ -8540,6 +8842,28 @@ "node": ">=4" } }, + "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "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", @@ -8970,12 +9294,12 @@ "dev": true }, "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==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -9023,6 +9347,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "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", @@ -9070,9 +9408,9 @@ } }, "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==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -9189,9 +9527,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" @@ -9305,6 +9643,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -9357,9 +9714,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", - "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -9440,9 +9797,9 @@ } }, "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==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -11074,31 +11431,98 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console/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/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-watch-typeahead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.0.0.tgz", - "integrity": "sha512-jxoszalAb394WElmiJTFBMzie/RDCF+W7Q29n5LzOPtcoQoHWfdUtHFkbhgf5NwWe8uMOxvKb/g7ea7CshfkTw==", + "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^27.0.0", - "jest-watcher": "^27.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "jest": "^27.0.0" + "@types/yargs-parser": "*" } }, "node_modules/jest-watch-typeahead/node_modules/ansi-regex": { @@ -11171,6 +11595,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "node_modules/jest-watch-typeahead/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -11180,6 +11616,165 @@ "node": ">=8" } }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/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/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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/jest-watch-typeahead/node_modules/jest-watcher/node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format/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/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/jest-watch-typeahead/node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -11644,7 +12239,7 @@ "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": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, "node_modules/lodash.memoize": { @@ -12046,9 +12641,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", - "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/normalize-path": { @@ -12129,9 +12724,9 @@ } }, "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==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12161,14 +12756,14 @@ } }, "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==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -12179,28 +12774,28 @@ } }, "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==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "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==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" @@ -12227,27 +12822,27 @@ } }, "node_modules/object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", "dev": true, "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" @@ -12325,17 +12920,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -14617,9 +15212,9 @@ "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==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -14634,9 +15229,9 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" @@ -14677,32 +15272,26 @@ } }, "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==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, "dependencies": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.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==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "dependencies": { "jsesc": "~0.5.0" @@ -14714,7 +15303,7 @@ "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=", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, "bin": { "jsesc": "bin/jsesc" @@ -15010,6 +15599,20 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -15124,9 +15727,9 @@ } }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -15352,15 +15955,6 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, - "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/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -15563,45 +16157,64 @@ "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==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", + "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16264,6 +16877,20 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-styles": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", @@ -16293,14 +16920,14 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -16330,18 +16957,18 @@ } }, "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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "engines": { "node": ">=4" @@ -16393,6 +17020,36 @@ "yarn": "*" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uplot": { "version": "1.6.24", "resolved": "https://registry.npmjs.org/uplot/-/uplot-1.6.24.tgz", @@ -16992,6 +17649,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",