From ac0d5832b6f943cce59e27175655f30792b4f73d Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Wed, 22 Dec 2021 18:17:48 +0100 Subject: [PATCH] event cucle (#76) * install sass * refact: integration settings - OSC in its own HTTP endpoint - OSC settings have own object in db * refact: simplify event cycle * refact: restructure external triggers http * refact: restructure external triggers osc+socket * refact: restructure data updates * feat: osc integration class * IO improvements - timer uses osc integration - create trigger handler to manage external triggers * refact: refract state machine update * Integration: simple HTTP Client * Integration: http options in datamodel * Integration: call http send on life cycle * feat/62-logging: fix issue #71 --- .gitignore | 12 +- .idea/.gitignore | 5 + client/src/App.jsx | 2 +- client/src/{App.css => App.scss} | 0 client/src/app/api/apiConstants.js | 1 + client/src/app/api/ontimeApi.js | 84 +- .../src/features/control/PlaybackButtons.jsx | 46 +- .../src/features/control/PlaybackControl.jsx | 9 + client/src/features/control/PlaybackTimer.jsx | 30 +- client/src/features/info/Info.jsx | 2 +- .../{Info.module.css => Info.module.scss} | 11 +- client/src/features/info/InfoLogger.jsx | 2 +- client/src/features/info/InfoNif.jsx | 2 +- client/src/features/info/InfoTitle.jsx | 2 +- client/src/features/modals/AliasesModal.jsx | 20 +- .../src/features/modals/AppSettingsModal.jsx | 75 +- .../features/modals/EventSettingsModal.jsx | 21 +- .../modals/IntegrationSettingsModal.jsx | 335 ++++ client/src/features/modals/ModalManager.jsx | 12 +- client/src/features/modals/Modals.module.css | 60 - client/src/features/modals/Modals.module.scss | 99 ++ .../viewers/backstage/StageManager.jsx | 6 +- .../features/viewers/studio/StudioClock.jsx | 16 +- client/src/index.js | 15 +- client/src/{index.css => index.scss} | 2 + client/src/styles/_main.scss | 11 + client/src/styles/_variables.scss | 4 + server/main.js | 9 +- server/package.json | 15 +- server/src/app.js | 43 +- server/src/classes/EventTimer.js | 641 +++++--- server/src/classes/Timer.js | 57 +- .../src/classes/__tests__/classUtils.test.js | 215 ++- .../src/classes/__tests__/eventtimer.test.js | 137 ++ server/src/classes/classUtils.js | 87 +- server/src/classes/integrations/Http.js | 52 + server/src/classes/integrations/Osc.js | 107 ++ server/src/config/config.js | 8 +- server/src/controllers/OscController.js | 14 +- server/src/controllers/eventsController.js | 26 +- server/src/controllers/ontimeController.js | 39 +- server/src/controllers/playbackController.js | 31 +- server/src/models/dataModel.js | 43 +- server/src/package.json | 10 +- server/src/routes/ontimeRouter.js | 10 +- server/src/utils/__tests__/parser.tests.js | 31 +- server/src/utils/__tests__/time.tests.js | 4 +- server/src/utils/__tests__/url.test.js | 36 + server/src/utils/parser.js | 85 +- server/src/utils/time.js | 3 +- server/src/utils/url.js | 21 + server/src/yarn.lock | 1376 +---------------- server/yarn.lock | 5 + 53 files changed, 2041 insertions(+), 1948 deletions(-) create mode 100644 .idea/.gitignore rename client/src/{App.css => App.scss} (100%) rename client/src/features/info/{Info.module.css => Info.module.scss} (96%) create mode 100644 client/src/features/modals/IntegrationSettingsModal.jsx delete mode 100644 client/src/features/modals/Modals.module.css create mode 100644 client/src/features/modals/Modals.module.scss rename client/src/{index.css => index.scss} (94%) create mode 100644 client/src/styles/_main.scss create mode 100644 client/src/styles/_variables.scss create mode 100644 server/src/classes/__tests__/eventtimer.test.js create mode 100644 server/src/classes/integrations/Http.js create mode 100644 server/src/classes/integrations/Osc.js create mode 100644 server/src/utils/__tests__/url.test.js create mode 100644 server/src/utils/url.js diff --git a/.gitignore b/.gitignore index 7a597a038..6819aafb4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,13 +23,17 @@ dist/ npm-debug.log* yarn-debug.log* yarn-error.log* -ontime.code-workspace -TODO.md # working stuff _SS/ -.vscode/launch.json -.eslintrc.json db backup.json server/src/data/db.json server/src/models/db.json +TODO.md + +# vscode stuff +.vscode/* +ontime.code-workspace + +# webstorm stuff +.idea/* \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..b58b603fe --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/client/src/App.jsx b/client/src/App.jsx index a56c126de..fef3e7a31 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,6 +1,6 @@ import { lazy, Suspense, useCallback, useEffect } from 'react'; import { Route, Switch } from 'react-router-dom'; -import './App.css'; +import './App.scss'; import { QueryClient, QueryClientProvider } from 'react-query'; import SocketProvider from 'app/context/socketContext'; import withSocket from 'features/viewers/ViewWrapper'; diff --git a/client/src/App.css b/client/src/App.scss similarity index 100% rename from client/src/App.css rename to client/src/App.scss diff --git a/client/src/app/api/apiConstants.js b/client/src/app/api/apiConstants.js index d6a0f2d2f..2fbdadb91 100644 --- a/client/src/app/api/apiConstants.js +++ b/client/src/app/api/apiConstants.js @@ -2,6 +2,7 @@ export const NODE_PORT = 4001; export const EVENT_TABLE = 'event'; export const EVENTS_TABLE = 'events'; export const APP_TABLE = 'appinfo'; +export const OSC_SETTINGS = 'oscSettings'; const calculateServer = () => { return window.location.origin.replace(window.location.port, `${NODE_PORT}/`); diff --git a/client/src/app/api/ontimeApi.js b/client/src/app/api/ontimeApi.js index 9d850259c..1d736dfb8 100644 --- a/client/src/app/api/ontimeApi.js +++ b/client/src/app/api/ontimeApi.js @@ -3,13 +3,77 @@ import { ontimeURL } from './apiConstants'; export const ontimePlaceholderInfo = { networkInterfaces: [], - version: '', - serverPort: 4001, - oscInPort: '', - oscOutPort: '', - oscOutIP: '', + settings: { + version: '', + serverPort: 4001, + }, }; +export const oscPlaceholderSettings = { + port: '', + portOut: '', + targetIP: '', + enabled: true, +}; + +export const httpPlaceholder = { + onLoad: { + url: '', + enabled: false, + }, + onStart: { + url: '', + enabled: false, + }, + onUpdate: { + url: '', + enabled: false, + }, + onPause: { + url: '', + enabled: false, + }, + onStop: { + url: '', + enabled: false, + }, + onFinish: { + url: '', + enabled: false, + }, +}; + +export const ontimeVars = [ + { + name: '$timer', + description: 'Current running timer', + }, + { + name: '$title', + description: 'Current title', + }, + { + name: '$presenter', + description: 'Current presenter', + }, + { + name: '$subtitle', + description: 'Current subtitle', + }, + { + name: '$next-title', + description: 'Next title', + }, + { + name: '$next-presenter', + description: 'Next presenter', + }, + { + name: '$next-subtitle', + description: 'Next subtitle', + }, +]; + export const getInfo = async () => { const res = await axios.get(ontimeURL + '/info'); return res.data; @@ -20,6 +84,16 @@ export const postInfo = async (data) => { return res; }; +export const getOSC = async () => { + const res = await axios.get(ontimeURL + '/osc'); + return res.data; +}; + +export const postOSC = async (data) => { + const res = await axios.post(ontimeURL + '/osc', data); + return res; +}; + export const downloadEvents = async () => { await axios({ url: ontimeURL + '/db', diff --git a/client/src/features/control/PlaybackButtons.jsx b/client/src/features/control/PlaybackButtons.jsx index 3a6452b48..c5b25f92d 100644 --- a/client/src/features/control/PlaybackButtons.jsx +++ b/client/src/features/control/PlaybackButtons.jsx @@ -1,4 +1,5 @@ import { memo } from 'react'; +import PropTypes from 'prop-types'; import style from './PlaybackControl.module.scss'; import StartIconBtn from 'common/components/buttons/StartIconBtn'; import PauseIconBtn from 'common/components/buttons/PauseIconBtn'; @@ -10,70 +11,77 @@ import ReloadIconButton from 'common/components/buttons/ReloadIconBtn'; const areEqual = (prevProps, nextProps) => { return ( - prevProps.playback === nextProps.playback && - prevProps.selectedId === nextProps.selectedId + prevProps.playback === nextProps.playback + && prevProps.selectedId === nextProps.selectedId + && prevProps.noEvents === nextProps.noEvents ); }; -const Playback = ({ playback, selectedId, playbackControl }) => { +const Playback = (props) => { + const { playback, selectedId, playbackControl, noEvents } = props; const isRolling = playback === 'roll'; + return (
playbackControl('start')} - disabled={!selectedId || isRolling} + disabled={!selectedId || isRolling || noEvents} /> playbackControl('pause')} - disabled={!selectedId || isRolling} + disabled={!selectedId || isRolling || noEvents || playback !== 'start'} /> playbackControl('roll')} />
); }; -const Transport = ({ playback, selectedId, playbackControl }) => { +const Transport = (props) => { + const { playback, selectedId, playbackControl, noEvents } = props; const isRolling = playback === 'roll'; + return (
playbackControl('previous')} - disabled={playback === 'roll'} + disabled={isRolling || noEvents} /> playbackControl('next')} - disabled={playback === 'roll'} + disabled={isRolling || noEvents} /> playbackControl('reload')} - disabled={!selectedId || isRolling} + disabled={selectedId == null || isRolling || noEvents} /> playbackControl('unload')} - disabled={!selectedId && !isRolling} + disabled={(selectedId == null && !isRolling) || noEvents} />
); }; const PlaybackButtons = (props) => { - const { playback, selectedId } = props; + const { playback, selectedId, noEvents } = props; return ( <> @@ -81,3 +89,17 @@ const PlaybackButtons = (props) => { }; export default memo(PlaybackButtons, areEqual); + +PlaybackButtons.propTypes = { + playback: PropTypes.string, + selectedId: PropTypes.string, + playbackControl: PropTypes.func.isRequired, + noEvents: PropTypes.bool.isRequired, +}; + +Transport.propTypes = { + playback: PropTypes.string, + selectedId: PropTypes.string, + playbackControl: PropTypes.func.isRequired, + noEvents: PropTypes.bool.isRequired, +}; diff --git a/client/src/features/control/PlaybackControl.jsx b/client/src/features/control/PlaybackControl.jsx index 366f3505f..64e9dd95a 100644 --- a/client/src/features/control/PlaybackControl.jsx +++ b/client/src/features/control/PlaybackControl.jsx @@ -15,6 +15,7 @@ export default function PlaybackControl() { secondary: null, }); const [selectedId, setSelectedId] = useState(null); + const [numEvents, setNumEvents] = useState(0); const resetTimer = () => { setTimer({ @@ -32,6 +33,7 @@ export default function PlaybackControl() { socket.emit('get-timer'); socket.emit('get-playstate'); socket.emit('get-selected-id'); + socket.emit('get-numevents'); // Handle playstate socket.on('playstate', (data) => { @@ -48,11 +50,16 @@ export default function PlaybackControl() { setSelectedId(data); }); + socket.on('numevents', (data) => { + setNumEvents(data); + }); + // Clear listener return () => { socket.off('playstate'); socket.off('timer'); socket.off('selected-id'); + socket.off('numevents'); }; }, [socket]); @@ -93,11 +100,13 @@ export default function PlaybackControl() { socket.emit('increment-timer', amount)} /> diff --git a/client/src/features/control/PlaybackTimer.jsx b/client/src/features/control/PlaybackTimer.jsx index 5e739c759..5aad78e3d 100644 --- a/client/src/features/control/PlaybackTimer.jsx +++ b/client/src/features/control/PlaybackTimer.jsx @@ -4,24 +4,27 @@ import {stringFromMillis} from 'common/utils/dateConfig'; import {Tooltip} from '@chakra-ui/react'; import {Button} from '@chakra-ui/button'; import {memo} from 'react'; +import PropTypes from "prop-types"; const areEqual = (prevProps, nextProps) => { return ( - prevProps.timer.running === nextProps.timer.running && - prevProps.timer.expectedFinish === nextProps.timer.expectedFinish && - prevProps.timer.startedAt === nextProps.timer.startedAt && - prevProps.playback === nextProps.playback && - prevProps.timer.secondary === nextProps.timer.secondary + prevProps.timer.running === nextProps.timer.running + && prevProps.timer.expectedFinish === nextProps.timer.expectedFinish + && prevProps.timer.startedAt === nextProps.timer.startedAt + && prevProps.playback === nextProps.playback + && prevProps.timer.secondary === nextProps.timer.secondary + && prevProps.selectedId === nextProps.selectedId ); }; const PlaybackTimer = (props) => { - const {timer, playback, handleIncrement} = props; + const {timer, playback, handleIncrement, selectedId} = props; const started = stringFromMillis(timer.startedAt, true); const finish = stringFromMillis(timer.expectedFinish, true); const isNegative = timer.running < 0; const isRolling = playback === 'roll'; const isWaiting = timer.secondary > 0 && timer.running == null; + const disableButtons = (selectedId == null || isRolling); const incrementProps = { size: 'sm', @@ -70,28 +73,28 @@ const PlaybackTimer = (props) => {
+
diff --git a/client/src/features/modals/AppSettingsModal.jsx b/client/src/features/modals/AppSettingsModal.jsx index bdc8d1c35..da66984b2 100644 --- a/client/src/features/modals/AppSettingsModal.jsx +++ b/client/src/features/modals/AppSettingsModal.jsx @@ -1,26 +1,21 @@ import { ModalBody } from '@chakra-ui/modal'; import { FormLabel, FormControl, Input, Button } from '@chakra-ui/react'; -import { getInfo, ontimePlaceholderInfo, postInfo } from 'app/api/ontimeApi'; +import { getOSC, oscPlaceholderSettings, postOSC } from 'app/api/ontimeApi'; import { useEffect, useState } from 'react'; import { useFetch } from 'app/hooks/useFetch'; -import { APP_TABLE } from 'app/api/apiConstants'; +import { OSC_SETTINGS } from 'app/api/apiConstants'; import { showErrorToast } from 'common/helpers/toastManager'; -import style from './Modals.module.css'; +import style from './Modals.module.scss'; export default function AppSettingsModal() { - const { data, status } = useFetch(APP_TABLE, getInfo); - const [formData, setFormData] = useState(ontimePlaceholderInfo); + const { data, status } = useFetch(OSC_SETTINGS, getOSC); + const [formData, setFormData] = useState(oscPlaceholderSettings); const [changed, setChanged] = useState(false); const [submitting, setSubmitting] = useState(false); useEffect(() => { if (data == null) return; - - setFormData({ - oscInPort: data.oscInPort, - oscOutPort: data.oscOutPort, - oscOutIP: data.oscOutIP, - }); + setFormData({ ...data }); }, [data]); const submitHandler = async (event) => { @@ -30,15 +25,15 @@ export default function AppSettingsModal() { let e = { status: false, message: '' }; // Validate fields - if (f.oscInPort < 1024 || f.oscInPort > 65535) { + if (f.port < 1024 || f.port > 65535) { // Port in incorrect range e.status = true; e.message += 'OSC IN Port in incorrect range (1024 - 65535)'; - } else if (f.oscOutPort < 1024 || f.oscOutPort > 65535) { + } else if (f.portOut < 1024 || f.portOut > 65535) { // Port in incorrect range e.status = true; e.message += 'OSC OUT Port in incorrect range (1024 - 65535)'; - } else if (f.oscInPort === f.oscOutPort) { + } else if (f.port === f.portOut) { // Cant use the same port e.status = true; e.message += 'OSC IN and OUT Ports cant be the same'; @@ -51,7 +46,7 @@ export default function AppSettingsModal() { } // Post here - postInfo(formData); + postOSC(formData); setChanged(false); setSubmitting(false); @@ -66,7 +61,7 @@ export default function AppSettingsModal() {

Options related to the application
- !!! Changes take effect after app restart !!! + 🔥 Changes take effect after app restart 🔥

@@ -85,8 +80,8 @@ export default function AppSettingsModal() { /> (Read Only Value) - - + + OSC In Port
@@ -95,18 +90,18 @@ export default function AppSettingsModal() {
{ setChanged(true); setFormData({ ...formData, - oscInPort: parseInt(event.target.value), + port: parseInt(event.target.value), }); }} isDisabled={submitting} @@ -114,8 +109,8 @@ export default function AppSettingsModal() { />
- - + + OSC Out Target IP
@@ -124,23 +119,23 @@ export default function AppSettingsModal() {
{ setChanged(true); setFormData({ ...formData, - oscOutIP: event.target.value, + targetIP: event.target.value, }); }} isDisabled={submitting} style={{ width: '12em', textAlign: 'right' }} />
- - + + OSC Out Port
@@ -149,18 +144,18 @@ export default function AppSettingsModal() {
{ setChanged(true); setFormData({ ...formData, - oscOutPort: parseInt(event.target.value), + portOut: parseInt(event.target.value), }); }} isDisabled={submitting} @@ -170,14 +165,16 @@ export default function AppSettingsModal() {
)} - +
+ +
diff --git a/client/src/features/modals/EventSettingsModal.jsx b/client/src/features/modals/EventSettingsModal.jsx index 58238f042..472995b37 100644 --- a/client/src/features/modals/EventSettingsModal.jsx +++ b/client/src/features/modals/EventSettingsModal.jsx @@ -10,7 +10,7 @@ import { fetchEvent, postEvent } from 'app/api/eventApi'; import { useEffect, useState } from 'react'; import { useFetch } from 'app/hooks/useFetch'; import { EVENT_TABLE } from 'app/api/apiConstants'; -import style from './Modals.module.css'; +import style from './Modals.module.scss'; export default function SettingsModal() { const { data, status } = useFetch(EVENT_TABLE, fetchEvent); @@ -122,6 +122,7 @@ export default function SettingsModal() { name='backstageInfo' placeholder='Information to be shown on backstage screens' autoComplete='off' + resize={false} value={formData.backstageInfo} onChange={(event) => { setChanged(true); @@ -160,14 +161,16 @@ export default function SettingsModal() {
)} - +
+ +
diff --git a/client/src/features/modals/IntegrationSettingsModal.jsx b/client/src/features/modals/IntegrationSettingsModal.jsx new file mode 100644 index 000000000..fd735c3bd --- /dev/null +++ b/client/src/features/modals/IntegrationSettingsModal.jsx @@ -0,0 +1,335 @@ +import { ModalBody } from '@chakra-ui/modal'; +import { + FormLabel, + FormControl, + Input, + Button, + Switch, +} from '@chakra-ui/react'; +import { + getInfo, + httpPlaceholder, + ontimeVars, + postInfo, +} from 'app/api/ontimeApi'; +import { useEffect, useState } from 'react'; +import { useFetch } from 'app/hooks/useFetch'; +import { APP_TABLE } from 'app/api/apiConstants'; +import { showErrorToast } from 'common/helpers/toastManager'; +import style from './Modals.module.scss'; + +export default function IntegrationSettingsModal() { + const { data, status } = useFetch(APP_TABLE, getInfo); + const [formData, setFormData] = useState(httpPlaceholder); + const [changed, setChanged] = useState(false); + const [submitting, setSubmitting] = useState(false); + + const ready = status === 'success'; + const integrationInputProps = { + size: 'sm', + autoComplete: 'off', + isDisabled: submitting || !ready, + }; + + useEffect(() => { + if (data == null) return; + + setFormData({ + onLoad: data?.onLoad, + onStart: data?.onStart, + onUpdate: data?.onUpdate, + onPause: data?.onPause, + onStop: data?.onStop, + }); + }, [data]); + + const submitHandler = async (event) => { + event.preventDefault(); + + const f = formData; + let e = { status: false, message: '' }; + + // set fields with error + if (e.status) { + showErrorToast('Invalid Input', e.message); + return; + } + + // Post here + postInfo(f); + + setChanged(false); + setSubmitting(false); + }; + + return ( + <> +
+ + <> +

+ Integrate with third party over an HTTP API +
+ 🔥 Changes take effect after app restart 🔥 +

+
+

+ Add HTTP messages that ontime will send during the app lifecycle +

+

+ You can use the variables below to pass data directly from + ontime eg: + + http://127.0.0.1:8088/API/?setHeadline=$title + &setSub=$presenter + +

+ + + {ontimeVars.map((v) => ( + + + + + ))} +
{v.name}{v.description}
+
+ + <> + + On Load + When a new event loads + + + { + setChanged(true); + setFormData({ + ...formData, + onLoad: { + ...formData.onLoad, + url: event.target.value, + }, + }); + }} + /> + { + setChanged(true); + setFormData({ + ...formData, + onLoad: { + ...formData.onLoad, + enabled: event.target.value, + }, + }); + }} + /> + + + On Start + + When an timer starts / resumes + + + + { + setChanged(true); + setFormData({ + ...formData, + onStart: { + ...formData.onStart, + url: event.target.value, + }, + }); + }} + /> + { + setChanged(true); + setFormData({ + ...formData, + onStart: { + ...formData.onStart, + enabled: event.target.value, + }, + }); + }} + /> + + + On Update + At every clock tick + + + { + setChanged(true); + setFormData({ + ...formData, + onUpdate: { + ...formData.onUpdate, + url: event.target.value, + }, + }); + }} + /> + { + setChanged(true); + setFormData({ + ...formData, + onUpdate: { + ...formData.onUpdate, + enabled: event.target.value, + }, + }); + }} + /> + + + On Pause + When a timer pauses + + + { + setChanged(true); + setFormData({ + ...formData, + onPause: { + ...formData.onPause, + url: event.target.value, + }, + }); + }} + /> + { + setChanged(true); + setFormData({ + ...formData, + onPause: { + ...formData.onPause, + enabled: event.target.value, + }, + }); + }} + /> + + + On Stop + When an event is unloaded + + + { + setChanged(true); + setFormData({ + ...formData, + onStop: { + ...formData.onStop, + url: event.target.value, + }, + }); + }} + /> + { + setChanged(true); + setFormData({ + ...formData, + onStop: { + ...formData.onStop, + enabled: event.target.value, + }, + }); + }} + /> + + + On Finish + When an event is finished + + + { + setChanged(true); + setFormData({ + ...formData, + onStop: { + ...formData.onFinish, + url: event.target.value, + }, + }); + }} + /> + { + setChanged(true); + setFormData({ + ...formData, + onStop: { + ...formData.onStop, + enabled: event.target.value, + }, + }); + }} + /> + + + +
+ +
+
+
+ + ); +} diff --git a/client/src/features/modals/ModalManager.jsx b/client/src/features/modals/ModalManager.jsx index d371991fa..2ae575b18 100644 --- a/client/src/features/modals/ModalManager.jsx +++ b/client/src/features/modals/ModalManager.jsx @@ -10,6 +10,7 @@ import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/tabs'; import EventSettingsModal from './EventSettingsModal'; import AppSettingsModal from './AppSettingsModal'; import AliasesModal from './AliasesModal'; +import IntegrationSettingsModal from './IntegrationSettingsModal'; export default function ModalManager(props) { const { isOpen, onClose } = props; @@ -19,6 +20,7 @@ export default function ModalManager(props) { onClose={onClose} closeOnOverlayClick={false} motionPreset={'slideInBottom'} + size='lg' > @@ -27,9 +29,10 @@ export default function ModalManager(props) { - Event Settings - Application Settings - URL Aliases + Event Data + Application Settings + URL Aliases + {/*Integration*/} @@ -41,6 +44,9 @@ export default function ModalManager(props) { + {/**/} + {/* */} + {/**/} diff --git a/client/src/features/modals/Modals.module.css b/client/src/features/modals/Modals.module.css deleted file mode 100644 index 9949ab53f..000000000 --- a/client/src/features/modals/Modals.module.css +++ /dev/null @@ -1,60 +0,0 @@ -.modalBody { - font-weight: 400; -} - -.modalBody > * { - margin-top: 0.5em; -} - -.modalBody > button { - margin-top: 1em; -} - -.notes { - font-weight: 400; - color: #2b6cb0; -} - -p.notes { - text-align: center; - border-color: #2b6cb055; - border-width: 0 2px; - font-size: 0.9em; - margin-bottom: 1em; -} - -span.notes { - font-size: 0.8em; - padding-left: 0.4em; -} - -.modalInline { - display: flex; - gap: 2em; -} - -.highNotes { - background-color: #2b6cb022; - margin: 1em 0; - padding: 0.3em; -} - -.flexNote { - font-size: 0.9em; - padding-bottom: 0.3em; -} - -a::after { - content: ' \2197'; - color: #ff7597; -} - -a:hover { - color: #ff7597; -} - -.separator { - border: 1px solid #2b6cb055; - width: 50%; - margin: 0.5em auto; -} diff --git a/client/src/features/modals/Modals.module.scss b/client/src/features/modals/Modals.module.scss new file mode 100644 index 000000000..353d23b37 --- /dev/null +++ b/client/src/features/modals/Modals.module.scss @@ -0,0 +1,99 @@ +@use '../../styles/variables' as *; + +//////////////////////////////////// main + +.modalBody, +.modalBodyDisabled { + font-weight: 400; + + .notes { + font-weight: 400; + color: $light-bg; + } + + .separator { + border: 1px solid $light-bg-transparent; + width: 50%; + margin: 0.5em auto; + } + + .modalInline { + display: flex; + gap: 2em; + align-items: center; + } + + .submitContainer { + display: flex; + flex-direction: row-reverse; + + button { + margin-top: 1em; + } + } + +} + +.modalBody > *, +.modalBodyDisabled > * { + margin-top: 0.5em; +} + +//////////////////////////////////// notes +p { + &.notes { + text-align: center; + border-color: $light-bg-transparent; + border-width: 0 2px; + font-size: 0.9em; + margin-bottom: 1em; + } +} + +span { + &.notes { + font-size: 0.8em; + padding-left: 0.4em; + } +} + +.highNotes { + background-color: $light-text; + margin: 1em 0; + padding: 0.3em; + font-size: 0.9em; + + table { + background-color: $light-text; + width: 100%; + margin-top: 0.3em; + } + + .noteItem { + user-select: text; + font-weight: 700; + padding-right: 2em; + } + + .flexNote { + user-select: text; + padding-bottom: 0.3em; + } + + .emNote { + user-select: text; + display: block; + background-color: #fffc; + } +} + +// Define style for a link +a { + &::after { + content: ' \2197'; + color: $accent; + } + &:hover { + color: $accent; + } +} diff --git a/client/src/features/viewers/backstage/StageManager.jsx b/client/src/features/viewers/backstage/StageManager.jsx index 39d507a83..6751bfc08 100644 --- a/client/src/features/viewers/backstage/StageManager.jsx +++ b/client/src/features/viewers/backstage/StageManager.jsx @@ -21,10 +21,10 @@ export default function StageManager(props) { useEffect(() => { if (backstageEvents == null) return; - setFilteredEvents(getEventsWithDelay(backstageEvents)); + const f = getEventsWithDelay(backstageEvents) - console.log('hhh', getEventsWithDelay(backstageEvents)) - }, [backstageEvents]); + setFilteredEvents(f); + }, [backstageEvents]); // Format messages diff --git a/client/src/features/viewers/studio/StudioClock.jsx b/client/src/features/viewers/studio/StudioClock.jsx index f85c98722..3b947b713 100644 --- a/client/src/features/viewers/studio/StudioClock.jsx +++ b/client/src/features/viewers/studio/StudioClock.jsx @@ -3,7 +3,11 @@ import useFitText from "use-fit-text"; import NavLogo from "../../../common/components/nav/NavLogo"; import {useEffect, useState} from "react"; import {formatDisplay} from "../../../common/utils/dateConfig"; -import {formatEventList, trimEventlist} from "../../../common/utils/eventsManager"; +import { + formatEventList, + getEventsWithDelay, + trimEventlist +} from "../../../common/utils/eventsManager"; export default function StudioClock(props) { const {title, time, backstageEvents, selectedId, nextId, onAir} = props; @@ -25,11 +29,11 @@ export default function StudioClock(props) { useEffect(() => { if (backstageEvents == null) return; - const events = backstageEvents.filter((e) => e.type === 'event'); - - let e = trimEventlist(events, selectedId, MAX_TITLES); - e = formatEventList(e, selectedId, nextId); - setSchedule(e); + const delayed = getEventsWithDelay(backstageEvents); + const events = delayed.filter((e) => e.type === 'event'); + const trimmed = trimEventlist(events, selectedId, MAX_TITLES); + const formatted = formatEventList(trimmed, selectedId, nextId); + setSchedule(formatted); }, [backstageEvents, selectedId, nextId]); diff --git a/client/src/index.js b/client/src/index.js index 5d0b0d778..c24442802 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -1,26 +1,17 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './index.css'; +import './index.scss'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { BrowserRouter } from 'react-router-dom'; - -// 1. import Chakra components -import { ChakraProvider, extendTheme } from '@chakra-ui/react'; - -// 2. Extend the theme to include custom colors, fonts, etc -const colors = { - // not yet -}; +import { ChakraProvider } from '@chakra-ui/react'; // Load Open Sans typeface require('typeface-open-sans'); -const theme = extendTheme({ colors }); - ReactDOM.render( - + diff --git a/client/src/index.css b/client/src/index.scss similarity index 94% rename from client/src/index.css rename to client/src/index.scss index ec2585e8c..cea7ef3b7 100644 --- a/client/src/index.css +++ b/client/src/index.scss @@ -1,3 +1,5 @@ +@use './styles/main'; + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', diff --git a/client/src/styles/_main.scss b/client/src/styles/_main.scss new file mode 100644 index 000000000..dd4d3c18a --- /dev/null +++ b/client/src/styles/_main.scss @@ -0,0 +1,11 @@ +//////////////////////////////////// general app style + +// no decoration on lists +ul { + list-style-type: none; +} + +// no resizing on text areas +textarea { + resize: none !important; +} \ No newline at end of file diff --git a/client/src/styles/_variables.scss b/client/src/styles/_variables.scss new file mode 100644 index 000000000..cf214a508 --- /dev/null +++ b/client/src/styles/_variables.scss @@ -0,0 +1,4 @@ +$light-bg: #2b6cb0; +$light-bg-transparent: #2b6cb055; +$light-text: #2b6cb022; +$accent: #ff7597; diff --git a/server/main.js b/server/main.js index 80149efbf..5b92123a9 100644 --- a/server/main.js +++ b/server/main.js @@ -17,18 +17,15 @@ let loaded = 'Nothing loaded'; let isQuitting = false; const nodePath = - env != 'prod' + env !== 'prod' ? path.join('file://', __dirname, 'src/app.js') : path.join('file://', __dirname, '../', 'extraResources', 'src/app.js'); (async () => { try { - const { startServer, startOSCServer, startOSCClient } = await import( + const { startServer, startOSCServer } = await import( nodePath ); - // Start OSC Client (Feedback) - await startOSCClient(); - // Start express server loaded = await startServer(); @@ -140,7 +137,7 @@ app.whenReady().then(() => { setTimeout(() => { // Load page served by node const reactApp = - env == 'prod' + env === 'prod' ? 'http://localhost:4001/editor' : 'http://localhost:3000/editor'; diff --git a/server/package.json b/server/package.json index dd767dadb..432f44a62 100644 --- a/server/package.json +++ b/server/package.json @@ -33,6 +33,9 @@ "dist-mac": "electron-builder --publish=never --x64 --mac", "dist-all": "electron-builder -mw" }, + "jest": { + "testPathIgnorePatterns": ["dist"] + }, "build": { "productName": "ontime", "appId": "no.lightdev.ontime", @@ -64,7 +67,9 @@ }, "files": [ "**/*", - "assets/" + "assets/", + "!**/{yarn.lock,yarn-error.log}", + "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" ], "directories": { "buildResources": "./assets/" @@ -74,14 +79,18 @@ "from": "../client/build", "to": "extraResources/client/build", "filter": [ - "**/*" + "**/*", + "!**/{yarn.lock,yarn-error.log}", + "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" ] }, { "from": "src", "to": "extraResources/src", "filter": [ - "**/*" + "**/*", + "!**/{yarn.lock,yarn-error.log}", + "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" ] } ] diff --git a/server/src/app.js b/server/src/app.js index dcc4b878e..c3028230e 100644 --- a/server/src/app.js +++ b/server/src/app.js @@ -22,7 +22,6 @@ const adapter = new JSONFile(file); export const db = new Low(adapter); // dependencies -import { Client } from 'node-osc'; import express from 'express'; import http from 'http'; import cors from 'cors'; @@ -48,8 +47,8 @@ if (db.data == null || !isValid) { // get data // there is also the case of the db being corrupt -// try to parse the data -export const data = await parseJson(db.data); +// try to parse the data, make sure that all fields exist (enforce) +export const data = await parseJson(db.data, true); db.data = data; await db.write(); @@ -113,17 +112,17 @@ app.use((err, req, res, next) => { * ---------------- * * Configuration of services comes from app general config - * It can be overriden here by the settings in the db - * It can also be overriden on call + * It can be overridden here by the settings in the db + * It can also be overridden on call * */ -const s = data.settings; -const oscIP = s.oscOutIP || config.osc.ipOut; -const oscOutPort = s.oscOutPort || config.osc.portOut; -const oscInPort = s.oscInPort || config.osc.port; +const osc = data.osc; +const oscIP = osc?.targetIP || config.osc.targetIP; +const oscOutPort = osc?.portOut || config.osc.portOut; +const oscInPort = osc?.port || config.osc.port; -const serverPort = s.serverPort || config.server.port; +const serverPort = data.settings.serverPort || config.server.port; // Start OSC server import { initiateOSC, shutdownOSCServer } from './controllers/OscController.js'; @@ -140,17 +139,6 @@ export const startOSCServer = async (overrideConfig = null) => { initiateOSC(oscSettings); }; -// Start OSC Client -let oscClient = null; - -export const startOSCClient = async (overrideConfig = null) => { - // Setup default port - const port = overrideConfig?.port || oscOutPort; - console.log('initialise OSC Client on port: ', port); - - oscClient = new Client(oscIP, oscOutPort); -}; - // create HTTP server const server = http.createServer(app); @@ -168,8 +156,14 @@ export const startServer = async (overrideConfig = null) => { const returnMessage = `HTTP Server is listening on port ${port}`; server.listen(port, '0.0.0.0', () => console.log(returnMessage)); + // OSC Config + const oscConfig = { + ip: oscIP, + port: overrideConfig?.port || oscOutPort + } + // init timer - global.timer = new EventTimer(server, oscClient, config); + global.timer = new EventTimer(server, config.timer, oscConfig, data.http); global.timer.setupWithEventList(data.events); return returnMessage; @@ -178,7 +172,7 @@ export const startServer = async (overrideConfig = null) => { export const shutdown = async () => { console.log('Node service shutdown'); - user.event('NODE', 'shutdown', 'requesting node shutfown').send(); + user.event('NODE', 'shutdown', 'requesting node shutdown').send(); // shutdown express server server.close(); @@ -186,9 +180,6 @@ export const shutdown = async () => { // shutdown OSC Server shutdownOSCServer(); - // shutdown OSC Client - oscClient.close(); - // shutdown timer global.timer.shutdown(); }; diff --git a/server/src/classes/EventTimer.js b/server/src/classes/EventTimer.js index 1d0f8a43a..03a79b57c 100644 --- a/server/src/classes/EventTimer.js +++ b/server/src/classes/EventTimer.js @@ -1,6 +1,9 @@ -import { Timer } from './Timer.js'; -import { Server } from 'socket.io'; -import {DAYMS, getSelectionByRoll} from './classUtils.js'; +import {Timer} from './Timer.js'; +import {Server} from 'socket.io'; +import {DAY_TO_MS, getSelectionByRoll, replacePlaceholder, updateRoll} from './classUtils.js'; +import {OSCIntegration} from './integrations/Osc.js'; +import {HTTPIntegration} from "./integrations/Http.js"; +import {cleanURL} from "../utils/url.js"; /* * EventTimer adds functions specific to APP @@ -11,11 +14,35 @@ import {DAYMS, getSelectionByRoll} from './classUtils.js'; */ export class EventTimer extends Timer { + + // Keep track of Timer lifecycle + // idle: before it is initialised + // load: when a new event is loaded + // update: every update call cycle (1 x second) + // stop: when the timer is stopped + // finish: when a timer finishes + cycleState = { + idle: 'idle', + onLoad: 'onLoad', + armed: 'armed', + onStart: 'onStart', + onUpdate: 'onUpdate', + onPause: 'onPause', + onStop: 'onStop', + onFinish: 'onFinish', + }; + ontimeCycle = 'idle'; + prevCycle = null; + lastUpdate = null; + // Socket IO Object io = null; - // OSC Client - oscClient = null; + // OSC Object + osc = null; + + // HTTP Client Object + http = null; _numClients = 0; _interval = null; @@ -62,10 +89,21 @@ export class EventTimer extends Timer { _eventlist = null; onAir = false; - constructor(httpServer, oscClient, config) { + /** + * Instantiates an event timer object + * @param httpServer + * @param timerConfig + * @param [oscConfig] + * @param [httpConfig] + */ + constructor(httpServer, timerConfig, oscConfig, httpConfig) { + // call super constructor super(); + // initialise class variables + this.numEvents = 0; + // initialise socketIO server this.io = new Server(httpServer, { cors: { @@ -76,135 +114,47 @@ export class EventTimer extends Timer { }, }); + // Todo: extract + // initialise osc object + if (oscConfig != null) { + console.log('initialise OSC Client on port: ', oscConfig?.port); + this.osc = new OSCIntegration(); + this.osc.init(oscConfig); + } + + // Todo: extract + // initialise http object + if (httpConfig != null) { + this.http = new HTTPIntegration(); + this.http.init(httpConfig); + this.httpMessages = httpConfig.messages; + } + // set recurrent emits this._interval = setInterval( - () => this.broadcastTimer(), - config.timer.refresh + () => this.runCycle(), + timerConfig?.refresh || 1000 ); // listen to new connections this._listenToConnections(); - - // set oscClient - this.updateOSCClient(oscClient); - } - - /** - * @description Updates the osc client used in the object - * @param {object} oscClient - */ - updateOSCClient(oscClient) { - this.oscClient = oscClient; - } - - /** - * @description Sends osc value from predefined messages - * @param {string} event - message to be sent - */ - sendOSC(event) { - if (this.oscClient == null) return; - - const add = '/ontime'; - const play = 'play'; - const pause = 'pause'; - const stop = 'stop'; - const prev = 'prev'; - const next = 'next'; - const reload = 'reload'; - const finished = 'finished'; - const time = this.timeTag; - const overtime = this.current > 0 ? 0 : 1; - const title = this.titles?.titleNow || ''; - const presenter = this.titles?.presenterNow || ''; - - switch (event) { - case 'time': - // Send Timetag Message - this.oscClient.send(add + '/time', time, (err) => { - if (err) console.error(err); - }); - break; - case 'finished': - // Runs when timer reaches 0 - this.oscClient.send(add, finished, (err) => { - if (err) console.error(err); - }); - break; - case 'overtime': - // Whether timer is negative - this.oscClient.send(add + '/overtime', overtime, (err) => { - if (err) console.error(err); - }); - break; - case 'titles': - // Send Title of current event - this.oscClient.send(add + '/title', title, (err) => { - if (err) console.error(err); - }); - - // Send presenter data on current event - this.oscClient.send(add + '/presenter', presenter, (err) => { - if (err) console.error(err); - }); - break; - case 'play': - // Play Message - this.oscClient.send(add, play, (err) => { - if (err) console.error(err); - }); - break; - case 'pause': - // Pause Message - this.oscClient.send(add, pause, (err) => { - if (err) console.error(err); - }); - break; - case 'stop': - // Stop Message - this.oscClient.send(add, stop, (err) => { - if (err) console.error(err); - }); - break; - case 'prev': - this.oscClient.send(add, prev, (err) => { - if (err) console.error(err); - }); - break; - case 'next': - this.oscClient.send(add, next, (err) => { - if (err) console.error(err); - }); - break; - case 'reload': - this.oscClient.send(add, reload, (err) => { - if (err) console.error(err); - }); - break; - - default: - break; - } } /** * @description Shutdown process */ shutdown() { - console.log('Closing socket server'); + console.log('Shutting down integrations') + console.log('... Closing socket server'); this.io.close(); + console.log('... Closing osc server'); + this.osc.shutdown(); } // send current timer broadcastTimer() { // through websockets this.io.emit('timer', this.getTimes()); - - // through OSC, only if running - if (this.state === 'start' || this.state === 'roll') { - this.sendOSC('time'); - this.sendOSC('overtime'); - this.sendOSC('titles'); - } } // broadcast state @@ -230,11 +180,239 @@ export class EventTimer extends Timer { this.io.emit(address, payload); } + /** + * @description Interface for triggering playback actions + * @param {string} action - state to be triggered + * @returns {boolean} Whether action was called + */ + trigger(action) { + // Todo: reply should come from status change + let reply = true; + switch (action) { + case 'start': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.start(); + this.runCycle(); + break; + case 'pause': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.pause(); + this.runCycle(); + break; + case 'stop': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.stop(); + this.runCycle(); + break; + case 'roll': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.roll(); + this.runCycle(); + break; + case 'previous': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.previous(); + this.runCycle(); + break; + case 'next': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.next(); + this.runCycle(); + break; + case 'unload': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.unload(); + this.runCycle(); + break; + case 'reload': + if (this.numEvents === 0 || this.numEvents == null) return false; + // Call action and force update + this.reload(); + this.runCycle(); + break; + case 'onAir': + // Call action + this.setonAir(true); + break; + case 'offAir': + // Call action and force update + this.setonAir(false); + break; + default: + // Error, disable flag + console.log('ERROR: Unhandled action triggered') + reply = false; + break; + } + return reply; + } + + + /** + * @description State machine checks what actions need to + * happen at every app cycle + */ + runCycle() { + const h = this.httpMessages?.messages; + let httpMessage = null; + + switch (this.ontimeCycle) { + case "idle": + break; + case "armed": + // if we come from roll, see if we can start + if (this.state === 'roll') { + this.update(); + } + break; + case "onLoad": + // broadcast change + this.broadcastState(); + + // check integrations - http + if (h?.onLoad?.enabled) { + if (h?.onLoad?.url != null || h?.onLoad?.url !== '') { + httpMessage = h?.onLoad?.url; + } + } + + // update lifecycle: armed + this.ontimeCycle = this.cycleState.armed; + break; + case "onStart": + // broadcast current state + this.broadcastState(); + // send OSC if there is something running + // _finish at is only set when an event is loaded + if (this._finishAt > 0) { + this.osc.send(this.osc.implemented.play); + } + + // check integrations - http + if (h?.onLoad?.enabled) { + if (h?.onLoad?.url != null || h?.onStart?.url !== '') { + httpMessage = h?.onStart?.url; + } + } + + // update lifecycle: onUpdate + this.ontimeCycle = this.cycleState.onUpdate; + break; + case "onUpdate": + // call update + this.update(); + // broadcast current state + this.broadcastTimer(); + // through OSC, only if running + if (this.state === 'start' || this.state === 'roll') { + if (this.current != null && this.secondaryTimer == null) { + this.osc.send(this.osc.implemented.time, this.timeTag); + this.osc.send(this.osc.implemented.overtime, this.current > 0 ? 0 : 1); + this.osc.send(this.osc.implemented.title, this.titles?.titleNow || ''); + } + } + + // check integrations - http + if (h?.onLoad?.enabled) { + if (h?.onLoad?.url != null || h?.onUpdate?.url !== '') { + httpMessage = h?.onUpdate?.url; + } + } + + break; + case "onPause": + // broadcast current state + this.broadcastState(); + // send OSC + this.osc.send(this.osc.implemented.pause); + + // check integrations - http + if (h?.onLoad?.enabled) { + if (h?.onLoad?.url != null || h?.onPause?.url !== '') { + httpMessage = h?.onPause?.url; + } + } + + // update lifecycle: armed + this.ontimeCycle = this.cycleState.armed; + + break; + case "onStop": + // broadcast change + this.broadcastState(); + + // send OSC if something was actually stopped + if (this.prevCycle === this.cycleState.onUpdate) { + this.osc.send(this.osc.implemented.stop); + } + + // check integrations - http + if (h?.onLoad?.enabled) { + if (h?.onLoad?.url != null || h?.onStop?.url !== '') { + httpMessage = h?.onStop?.url; + } + } + + // update lifecycle: idle + this.ontimeCycle = this.cycleState.idle; + break; + case "onFinish": + console.log('onFinish') + // broadcast change + this.broadcastState(false); + // finished an event + this.osc.send(this.osc.implemented.finished); + + // check integrations - http + if (h?.onLoad?.enabled) { + if (h?.onLoad?.url != null || h?.onFinish?.url !== '') { + httpMessage = h?.onFinish?.url; + } + } + + // update lifecycle: onUpdate + this.ontimeCycle = this.cycleState.onUpdate; + break; + default: + console.log(`ERROR: Unhandled cycle: ${this.ontimeCycle}`) + } + + // send http message if any + if (httpMessage != null) { + const v = { + '$timer': this.timeTag, + '$title': this.titles.titleNow, + '$presenter': this.titles.presenterNow, + '$subtitle': this.titles.subtitleNow, + '$next-title': this.titles.titleNext, + '$next-presenter': this.titles.presenterNext, + '$next-subtitle': this.titles.subtitleNext, + } + const m = cleanURL(replacePlaceholder(httpMessage, v)); + this.http.send(m); + } + + // update + this.update(); + + // reset cycle + this.prevCycle = this.ontimeCycle; + } + update() { + + // if there is nothing selected, update clock const now = this._getCurrentTime(); - // if there is nothing selected, update only clock - if (this.selectedEventId == null && this.state !== 'roll') { + // if we are not updating, send the timers + if (this.ontimeCycle !== this.cycleState.onUpdate) { this.clock = now; this.broadcastThis('timer', { clock: now, @@ -244,81 +422,59 @@ export class EventTimer extends Timer { expectedFinish: this._getExpectedFinish(), startedAt: this._startedAt, }); - return; } - // only implement roll here - if (this.state !== 'roll') { - super.update(); - } else { - // update timer as usual - this.clock = now; - if (this.selectedEventId && this.current > 0) { - // something is running, update - this.current = this._finishAt - now; - } else if (this.secondaryTimer > 0) { - // waiting to start, update secondary - this.secondaryTimer = this._secondaryTarget - now; + // Have we skipped onStart? + if (this.state === 'start' || this.state === 'roll') { + if (this.ontimeCycle === this.cycleState.armed) { + // update lifecycle: onStart + this.ontimeCycle = this.cycleState.onStart; + this.runCycle(); + } + } + + // update default functions + super.update(); + + if (this._finishedFlag) { + // update lifecycle: onFinish and call cycle + + this.ontimeCycle = this.cycleState.onFinish; + this._finishedFlag = false; + this.runCycle(); + } + + // only implement roll here, rest implemented in super + if (this.state === 'roll') { + const u = { + selectedEventId: this.selectedEventId, + current: this.current, + // safeguard on midnight rollover + _finishAt: this._finishAt >= this._startedAt ? this._finishAt : this._finishAt + DAY_TO_MS, + clock: this.clock, + secondaryTimer: this.secondaryTimer, + _secondaryTarget: this._secondaryTarget, } - // look for event if none is loaded - const currentRunning = this.current <= 0 && this.current !== null; - const secondaryRunning = - this.secondaryTimer <= 0 && this.secondaryTimer !== null; + const {updatedTimer, updatedSecondaryTimer, doRollLoad, isFinished} = updateRoll(u); - if (currentRunning) { - // finished an event - this.sendOSC('finished'); - this._finishedFlag = true; + this.current = updatedTimer; + this.secondaryTimer = updatedSecondaryTimer; + + if (isFinished) { + // update lifecycle: onFinish + this.ontimeCycle = this.cycleState.onFinish; + this.runCycle(); } - if (currentRunning || secondaryRunning) { - // look for events + if (doRollLoad) { this.rollLoad(); - - // broadcast state without recalculating timer - this.broadcastState(false); } } - - // if event is finished - if ( - this.current <= 0 && - (this.state === 'start' || this.state === 'roll') && - !this._finishedFlag - ) { - if (this._finishedAt === null) { - this._finishedAt = now; - } - this.sendOSC('finished'); - this._finishedFlag = true; - } } _setterManager(action, payload) { switch (action) { - /*******************************************/ - // playstate - case 'set-playstate': - // check state is defined - if (payload === 'start') this.start(); - else if (payload === 'pause') this.pause(); - else if (payload === 'stop') this.stop(); - else if (payload === 'previous') this.previous(); - else if (payload === 'next') this.next(); - else if (payload === 'reload') this.reload(); - else if (payload === 'unload') this.unload(); - else if (payload === 'roll') this.roll(); - - // TODO: Cleanup - // here tdo this.broadcastState; - // remove broadcast from functions - this.broadcastThis('playstate', this.state); - this.broadcastThis('selected-id', this.selectedEventId); - this.broadcastThis('titles', this.titles); - - break; - /*******************************************/ // Presenter message case 'set-presenter-text': @@ -423,7 +579,7 @@ export class EventTimer extends Timer { /*******************************************/ // playstate socket.on('set-playstate', (data) => { - this._setterManager('set-playstate', data); + this.trigger(data); }); socket.on('get-playstate', () => { @@ -453,6 +609,10 @@ export class EventTimer extends Timer { socket.emit('selected-id', this.selectedEventId); }); + socket.on('get-numevents', () => { + socket.emit('numevents', this.numEvents); + }); + socket.on('get-next-id', () => { socket.emit('next-id', this.nextEventId); }); @@ -539,8 +699,11 @@ export class EventTimer extends Timer { this._eventlist = []; this.numEvents = 0; - // broadcast change - this.broadcastState(); + // update lifecycle: onStop + this.ontimeCycle = this.cycleState.onStop; + + // update clients + this.broadcastThis('numevents', this.numEvents); } setupWithEventList(eventlist) { @@ -559,6 +722,12 @@ export class EventTimer extends Timer { // load first event this.loadEvent(0); + + // update clients + this.broadcastThis('numevents', this.numEvents); + + // run cycle + this.runCycle(); } updateEventList(eventlist) { @@ -601,7 +770,11 @@ export class EventTimer extends Timer { this.loadEvent(eventIndex, type); } - this.broadcastState(); + // update clients + this.broadcastThis('numevents', this.numEvents); + + // run cycle + this.runCycle(); } updateSingleEvent(id, entry) { @@ -640,7 +813,11 @@ export class EventTimer extends Timer { console.log(error); } - this.broadcastState(); + // update clients + this.broadcastThis('numevents', this.numEvents); + + // run cycle + this.runCycle(); } deleteId(eventId) { @@ -670,37 +847,53 @@ export class EventTimer extends Timer { this._loadTitlesNow(); } - this.broadcastState(); + // update clients + this.broadcastThis('numevents', this.numEvents); + + // run cycle + this.runCycle(); } + /** + * @description loads an event with a given Id + * @param eventId - ID of event in eventlist + */ loadEventById(eventId) { const eventIndex = this._eventlist.findIndex((e) => e.id === eventId); if (eventIndex === -1) return; this.pause(); this.loadEvent(eventIndex, 'load', true); + // run cycle + this.runCycle(); } + /** + * @description loads an event with a given index + * @param eventIndex - Index of event in eventlist + */ loadEventByIndex(eventIndex) { if (eventIndex === -1 || eventIndex > this.numEvents) return; this.pause(); this.loadEvent(eventIndex, 'load', true); + // run cycle + this.runCycle(); } // Loads a given event // load timers // load selectedEventIndex // load titles - loadEvent(eventIndex, type = 'load', broadcastChange = 'false') { + loadEvent(eventIndex, type = 'load') { const e = this._eventlist[eventIndex]; if (e == null) return; const start = e.timeStart == null || e.timeStart === '' ? 0 : e.timeStart; let end = e.timeEnd == null || e.timeEnd === '' ? 0 : e.timeEnd; // in case the end is earlier than start, we assume is the day after - if (end < start) end += DAYMS; + if (end < start) end += DAY_TO_MS; - // time stuff changes on wheter we keep the running clock + // time stuff changes on whether we keep the running clock if (type === 'load') { this._resetTimers(); @@ -724,9 +917,8 @@ export class EventTimer extends Timer { // look for event after this._loadTitlesNext(); - if (broadcastChange) - // broadcast current state - this.broadcastState(); + // update lifecycle: onLoad + this.ontimeCycle = this.cycleState.onLoad; } _loadTitlesNow() { @@ -980,54 +1172,55 @@ export class EventTimer extends Timer { } start() { + // do we need to change + if (this.state === 'start') return; + // if there is nothing selected, no nothing if (this.selectedEventId == null) return; // call super super.start(); - // broadcast current state - this.broadcastState(); - - // send OSC - this.sendOSC('play'); + // update lifecycle: onStart + this.ontimeCycle = this.cycleState.onStart; } pause() { + // do we need to change + if (this.state === 'pause') return; + // if there is nothing selected, no nothing if (this.selectedEventId == null) return; // call super super.pause(); - // broadcast current state - this.broadcastState(); - - // send OSC - this.sendOSC('pause'); + // update lifecycle: onPause + this.ontimeCycle = this.cycleState.onPause; } stop() { + // do we need to change + if (this.state === 'stop') return; + // call super super.stop(); - // broadcast current state - this.broadcastState(); - - // send OSC - this.sendOSC('stop'); + // update lifecycle: onPause + this.ontimeCycle = this.cycleState.onStop; } increment(amount) { // call super super.increment(amount); - // broadcast current state - this.broadcastState(); + // run cycle + this.runCycle(); } rollLoad() { const now = this._getCurrentTime(); + let prevLoaded = this.selectedEventId; // maybe roll has already been loaded if (this.secondaryTimer === null) { @@ -1103,18 +1296,31 @@ export class EventTimer extends Timer { if (publicIndex !== null) { this._loadThisTitles(this._eventlist[publicIndex], 'now-public'); } + + if (prevLoaded !== this.selectedEventId) { + // update lifecycle: onLoad + this.ontimeCycle = this.cycleState.onLoad; + // ensure we go through onLoad cycle + this.runCycle(); + } } roll() { // do we need to change if (this.state === 'roll') return; + if (this.numEvents === 0 || this.numEvents == null) return; + // set state this.state = 'roll'; + // update lifecycle: armed + this.ontimeCycle = this.cycleState.armed; + // load into event this.rollLoad(); + // broadcast change this.broadcastState(); } @@ -1122,6 +1328,9 @@ export class EventTimer extends Timer { // check that we have events to run if (this.numEvents < 1) return; + // maybe this is the first event? + if (this.selectedEventIndex === 0) return; + // if there is no event running, go to first if (this.selectedEventIndex == null) { this.loadEvent(0); @@ -1129,7 +1338,7 @@ export class EventTimer extends Timer { } // send OSC - this.sendOSC('prev'); + this.osc.send(this.osc.implemented.previous); // change playstate this.pause(); @@ -1145,6 +1354,9 @@ export class EventTimer extends Timer { // check that we have events to run if (this.numEvents < 1) return; + // maybe this is the last event? + if (this.selectedEventIndex === this.numEvents - 1) return; + // if there is no event running, go to first if (this.selectedEventIndex == null) { this.loadEvent(0); @@ -1152,7 +1364,7 @@ export class EventTimer extends Timer { } // send OSC - this.sendOSC('next'); + this.osc.send(this.osc.implemented.next); // change playstate this.pause(); @@ -1173,16 +1385,21 @@ export class EventTimer extends Timer { // reset selected this._resetSelection(); + // broadcast state + this.broadcastState(); + // reset playstate this.stop(); } reload() { - // reset playstate + if (this.numEvents === 0 || this.numEvents == null) return; + + // change playstate this.pause(); // send OSC - this.sendOSC('reload'); + this.osc.send(this.osc.implemented.reload); // reload data this.loadEvent(this.selectedEventIndex); diff --git a/server/src/classes/Timer.js b/server/src/classes/Timer.js index 4c7977ec3..485e35b99 100644 --- a/server/src/classes/Timer.js +++ b/server/src/classes/Timer.js @@ -24,11 +24,35 @@ export class Timer { constructor() {} + // call setup separately + setupWithSeconds(seconds, autoStart = false) { + // aux + const now = this._getCurrentTime(); + this.clock = now; + + // populate targets + this.duration = seconds * 1000; + this._finishAt = now + seconds * 1000; + + // start counting + this._startedAt = now; + + if (autoStart) { + this.state = 'start'; + } else { + this._pausedAt = now; + this._pausedInterval = 0; + } + this._pausedTotal = 0; + this.update(); + } + // update() update() { // get current time const now = this._getCurrentTime(); this.clock = now; + let checkFinish = false; // check playstate switch (this.state) { @@ -40,6 +64,8 @@ export class Timer { this.current = this._startedAt + this.duration + this._pausedTotal - now; + // enable flag + checkFinish = true; break; case 'pause': // update paused time @@ -48,28 +74,37 @@ export class Timer { if (this._startedAt != null) { // update current timer this.current = - this._startedAt + - this.duration + - this._pausedTotal + - this._pausedInterval - - now; + this._startedAt + + this.duration + + this._pausedTotal + + this._pausedInterval + - now; } + + // enable flag + checkFinish = true; break; case 'stop': // nothing here yet break; - default: - console.error('Timer: no playstate on update call', this.state); - break; + } + + if (checkFinish) { + // is event finished? + const isTimeOver = this.current <= 0; + const isUpdating = (this.state !== 'pause'); + + if (isTimeOver && isUpdating && this._finishedAt == null) { + if (this._finishedAt === null) this._finishedAt = now; + this._finishedFlag = true; + } } } // helpers static toSeconds(millis) { if (millis == null) return null; - return Math.ceil(millis * 0.001); - } // get current time in epoc @@ -148,7 +183,7 @@ export class Timer { // do we need to change if (this.state === 'start') return; else if (this._startedAt == null) { - // it hasnt started yet + // it hasn't started yet const now = this._getCurrentTime(); // set start time as now this._startedAt = now; diff --git a/server/src/classes/__tests__/classUtils.test.js b/server/src/classes/__tests__/classUtils.test.js index e896a583f..f5b1f4f30 100644 --- a/server/src/classes/__tests__/classUtils.test.js +++ b/server/src/classes/__tests__/classUtils.test.js @@ -1,22 +1,29 @@ -import {DAYMS, getSelectionByRoll, normaliseEndTime, sortArrayByProperty} from '../classUtils.js'; +import { + DAY_TO_MS, + getSelectionByRoll, + replacePlaceholder, + normaliseEndTime, + sortArrayByProperty, + updateRoll +} from '../classUtils.js'; // test sortArrayByProperty() describe('sort simple arrays of objects', () => { it('sort array 1-5', () => { const arr1 = [ - { timeStart: 1 }, - { timeStart: 5 }, - { timeStart: 3 }, - { timeStart: 2 }, - { timeStart: 4 }, + {timeStart: 1}, + {timeStart: 5}, + {timeStart: 3}, + {timeStart: 2}, + {timeStart: 4}, ]; const arr1Expected = [ - { timeStart: 1 }, - { timeStart: 2 }, - { timeStart: 3 }, - { timeStart: 4 }, - { timeStart: 5 }, + {timeStart: 1}, + {timeStart: 2}, + {timeStart: 3}, + {timeStart: 4}, + {timeStart: 5}, ]; const sorted = sortArrayByProperty(arr1, 'timeStart'); @@ -25,21 +32,21 @@ describe('sort simple arrays of objects', () => { it('sort array 1-5 with null', () => { const arr1 = [ - { timeStart: 1 }, - { timeStart: 5 }, - { timeStart: 3 }, - { timeStart: 2 }, - { timeStart: 4 }, - { timeStart: null }, + {timeStart: 1}, + {timeStart: 5}, + {timeStart: 3}, + {timeStart: 2}, + {timeStart: 4}, + {timeStart: null}, ]; const arr1Expected = [ - { timeStart: null }, - { timeStart: 1 }, - { timeStart: 2 }, - { timeStart: 3 }, - { timeStart: 4 }, - { timeStart: 5 }, + {timeStart: null}, + {timeStart: 1}, + {timeStart: 2}, + {timeStart: 3}, + {timeStart: 4}, + {timeStart: 5}, ]; const sorted = sortArrayByProperty(arr1, 'timeStart'); @@ -244,7 +251,6 @@ describe('test that roll loads selection in right order', () => { }); // test getSelectionByRoll() - describe('test that roll behaviour with overlapping times', () => { const eventlist = [ { @@ -384,6 +390,68 @@ describe('test that roll behaviour with overlapping times', () => { }); }); +// test replacePlaceholder() +describe('test that it replaces data correctly', () => { + const values = { + $timer: "timer", + $title: "title", + $presenter: "presenter", + $subtitle: "subtitle", + "$next-title": "next title", + "$next-presenter": "next presenter", + "$next-subtitle": "next subtitle" + }; + + it('replaces timer', () => { + const test = '___1232132 $timer'; + const expected = '___1232132 timer'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); + + it('replaces title', () => { + const test = '___1232132 $title'; + const expected = '___1232132 title'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); + + it('replaces presenter', () => { + const test = '___1232132 $presenter'; + const expected = '___1232132 presenter'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); + + it('replaces subtitle', () => { + const test = '___1232132 $subtitle'; + const expected = '___1232132 subtitle'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); + + it('replaces next next title', () => { + const test = '___1232132 $next-title'; + const expected = '___1232132 next title'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); + + it('replaces next presenter', () => { + const test = '___1232132 $next-presenter'; + const expected = '___1232132 next presenter'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); + + it('replaces next subtitle', () => { + const test = '___1232132 $next-subtitle'; + const expected = '___1232132 next subtitle'; + const s = replacePlaceholder(test, values) + expect(s).toBe(expected); + }); +}); + // test getSelectionByRoll() on issue #58 describe('test that roll behaviour multi day event edge cases', () => { @@ -407,8 +475,8 @@ describe('test that roll behaviour multi day event edge cases', () => { timers: { _startedAt: eventlist[0].timeStart, _finishAt: eventlist[0].timeEnd, - current: eventlist[0].timeEnd + DAYMS - now, - duration: DAYMS - eventlist[0].timeStart + eventlist[0].timeEnd, + current: eventlist[0].timeEnd + DAY_TO_MS - now, + duration: DAY_TO_MS - eventlist[0].timeStart + eventlist[0].timeEnd, }, timeToNext: null, }; @@ -454,10 +522,10 @@ test('test typical scenarios', () => { expect(normaliseEndTime(t1.start, t1.end)).toBe(t1_expected); const t2 = { - start: 10+DAYMS, + start: 10 + DAY_TO_MS, end: 20, } - const t2_expected = 20+DAYMS; + const t2_expected = 20 + DAY_TO_MS; expect(normaliseEndTime(t2.start, t2.end)).toBe(t2_expected); @@ -470,3 +538,94 @@ test('test typical scenarios', () => { expect(normaliseEndTime(t3.start, t3.end)).toBe(t3_expected); }); + +// test updateRoll() +describe('typical scenarios', () => { + + it('it updates running events correctly', () => { + const timers = { + selectedEventId: 1, + current: 10, + _finishAt: 15, + clock: 11, + secondaryTimer: null, + _secondaryTarget: null, + }; + + const expected = { + updatedTimer: timers._finishAt - timers.clock, + updatedSecondaryTimer: null, + doRollLoad: false, + isFinished: false, + }; + + expect(updateRoll(timers)).toStrictEqual(expected); + + // test that it can jump time + timers._finishAt = 1000; + timers.clock = 600; + expected.updatedTimer = timers._finishAt - timers.clock; + + expect(updateRoll(timers)).toStrictEqual(expected); + }); + + it('it updates secondary timer', () => { + const timers = { + selectedEventId: null, + current: null, + _finishAt: null, + clock: 11, + secondaryTimer: 1, + _secondaryTarget: 15, + }; + + const expected = { + updatedTimer: null, + updatedSecondaryTimer: timers._secondaryTarget - timers.clock, + doRollLoad: false, + isFinished: false, + }; + + expect(updateRoll(timers)).toStrictEqual(expected); + }); + + it('flags an event end', () => { + const timers = { + selectedEventId: 1, + current: 10, + _finishAt: 11, + clock: 12, + secondaryTimer: null, + _secondaryTarget: null, + }; + + const expected = { + updatedTimer: timers._finishAt - timers.clock, + updatedSecondaryTimer: null, + doRollLoad: true, + isFinished: true, + }; + + expect(updateRoll(timers)).toStrictEqual(expected); + }); + + it('secondary events do not trigger event ends', () => { + const timers = { + selectedEventId: null, + current: null, + _finishAt: null, + clock: 16, + secondaryTimer: 1, + _secondaryTarget: 15, + }; + + const expected = { + updatedTimer: null, + updatedSecondaryTimer: timers._secondaryTarget - timers.clock, + doRollLoad: true, + isFinished: false, + }; + + expect(updateRoll(timers)).toStrictEqual(expected); + }); +}); diff --git a/server/src/classes/__tests__/eventtimer.test.js b/server/src/classes/__tests__/eventtimer.test.js new file mode 100644 index 000000000..fc6c61a97 --- /dev/null +++ b/server/src/classes/__tests__/eventtimer.test.js @@ -0,0 +1,137 @@ +import {EventTimer} from "../EventTimer"; +import http from 'http'; +import express from "express"; + +// Create server +const app = express(); +const server = http.createServer(app); + + +// necessary config +const timerConfig = {refresh: 1000}; + +beforeEach(async () => { + server.listen(0, '0.0.0.0'); +}); + +afterEach(async () => { + await server.close(); +}); + + +test('object instantiates correctly', async () => { + const t = new EventTimer(server, timerConfig); + + // it contains everything from Timer + expect(t.clock).toBeNull(); + expect(t.duration).toBeNull(); + expect(t.current).toBeNull(); + expect(t.timeTag).toBeNull(); + expect(t.secondaryTimer).toBeNull(); + expect(t._secondaryTarget).toBeNull(); + expect(t._finishAt).toBeNull(); + expect(t._finishedAt).toBeNull(); + expect(t._finishedFlag).toBeFalsy(); + expect(t._startedAt).toBeNull(); + expect(t._pausedAt).toBeNull(); + expect(t._pausedInterval).toBeNull(); + expect(t._pausedTotal).toBeNull(); + expect(t.state).toBe('stop'); + + // and its own properties + expect(t.ontimeCycle).toBe('idle'); + expect(t.prevCycle).toBeNull(); + expect(t.lastUpdate).toBeNull(); + expect(t.io).not.toBeNull(); + expect(t.osc).toBeNull(); + expect(t.http).toBeNull(); + expect(t._numClients).toBe(0); + expect(t._interval).not.toBeNull(); + expect(t.presenter).toStrictEqual({text: '', visible: false}); + expect(t.public).toStrictEqual({text: '', visible: false}); + expect(t.lower).toStrictEqual({text: '', visible: false}); + expect(t.lower).toStrictEqual({text: '', visible: false}); + + const expectTitlesPublic = { + titleNow: null, + subtitleNow: null, + presenterNow: null, + titleNext: null, + subtitleNext: null, + presenterNext: null, + }; + + const expectTitles = { + ...expectTitlesPublic, + noteNow: null, + noteNext: null, + }; + + expect(t.titlesPublic).toStrictEqual(expectTitlesPublic); + expect(t.titles).toStrictEqual(expectTitles); + + expect(t.selectedEventIndex).toBeNull(); + expect(t.selectedEventId).toBeNull(); + expect(t.nextEventId).toBeNull(); + expect(t.selectedPublicEventId).toBeNull(); + expect(t.nextPublicEventId).toBeNull(); + expect(t.numEvents).toBe(0); + expect(t._eventlist).toBeNull(); + expect(t.onAir).toBeFalsy(); +}); + + +describe('test triggers behaviour', () => { + const t = new EventTimer(server, timerConfig); + + it('ignores bad commands', () => { + const success = t.trigger('test'); + expect(success).toBeFalsy(); + }) + + it('does not allow triggering events with an empty list', () => { + + expect(t.numEvents).toBe(0); + + expect(t.trigger('start')).toBeFalsy(); + expect(t.trigger('pause')).toBeFalsy(); + expect(t.trigger('stop')).toBeFalsy(); + expect(t.trigger('roll')).toBeFalsy(); + expect(t.trigger('previous')).toBeFalsy(); + expect(t.trigger('next')).toBeFalsy(); + expect(t.trigger('reload')).toBeFalsy(); + + expect(t.onAir).toBeFalsy(); + expect(t.trigger('onAir')).toBeTruthy(); + expect(t.onAir).toBeTruthy(); + expect(t.trigger('offAir')).toBeTruthy(); + expect(t.onAir).toBeFalsy(); + }); + + it('...and is consistent by calling the class methods', () => { + + expect(t.numEvents).toBe(0); + expect(t.state).toBe('stop'); + + t.start(); + expect(t.state).toBe('stop'); + + t.pause(); + expect(t.state).toBe('stop'); + + t.stop(); + expect(t.state).toBe('stop'); + + t.roll(); + expect(t.state).toBe('stop'); + + t.previous(); + expect(t.state).toBe('stop'); + + t.next(); + expect(t.state).toBe('stop'); + + t.reload(); + expect(t.state).toBe('stop'); + }); +}) \ No newline at end of file diff --git a/server/src/classes/classUtils.js b/server/src/classes/classUtils.js index 81d866fbc..8465fa03a 100644 --- a/server/src/classes/classUtils.js +++ b/server/src/classes/classUtils.js @@ -1,13 +1,17 @@ -export const DAYMS = 86400000; + +/** + * Utility variable: 24 hour in milliseconds . + * @type {number} + */ +export const DAY_TO_MS = 86400000; /** * @description handle events that span over midnight - * @param {num} start - When does the event start - * @param {num} end - When does the event end - * @returns {num} normalised time + * @param {number} start - When does the event start + * @param {number} end - When does the event end + * @returns {number} normalised time */ -export const normaliseEndTime = (start, end) => (end < start ? end + DAYMS : end); - +export const normaliseEndTime = (start, end) => (end < start ? end + DAY_TO_MS : end); /** * @description Sorts an array of objects by given property @@ -22,6 +26,21 @@ export const sortArrayByProperty = (arr, property) => { }); }; +/** + * @description Replaces placeholder variables in string with given data + * @param {string} str - string to analyse + * @param {object} values - map of variables: values to use + * @returns {string} finished string + */ + +export const replacePlaceholder = (str, values) => { + for (let [k, v] of Object.entries(values)) { + str = str.replace(k, v); + console.log(k, v); + } + return str; +}; + /** * @description Used in roll mode, returns selection variables from array * @param {array} arr - event list @@ -130,3 +149,59 @@ export const getSelectionByRoll = (arr, now) => { }; }; +/** + * @description Implements update functions for roll mode + * @param {object} currentTimers + * @param {object} currentTimers.selectedEventId - Id of currently selected event + * @param {object} currentTimers.current - Running timer + * @param {object} currentTimers._finishAt - Expected finish time + * @param {object} currentTimers.clock - time now + * @param {object} currentTimers.secondaryTimer - secondary timer + * @param {object} currentTimers._secondaryTarget - finish time of secondary timer + * @returns {object} object with selection variables + */ +export const updateRoll = (currentTimers) => { + + const {selectedEventId,current,_finishAt,clock,secondaryTimer,_secondaryTarget} = currentTimers; + + // timers + let updatedTimer = current; + let updatedSecondaryTimer = secondaryTimer; + // whether rollLoad should be called + let doRollLoad = false; + // whether runCycle should be called + let isFinished = false; + + if (selectedEventId && current >= 0) { + // if we have something selected and a timer, we are running + // this is true because roll never goes into negative times + + // update timer + updatedTimer = _finishAt - clock; + if (updatedTimer < 0) { + isFinished = true; + } + console.log(updatedTimer, isFinished, _finishAt) + + } else if (secondaryTimer >= 0) { + // if secondaryTimer is running we are in waiting to roll + + // update secondary + updatedSecondaryTimer = _secondaryTarget - clock; + } + + // if nothing is running, we need to find out if + // a) we just finished an event (finished was set to true) + // b) we need to look for events + // this could be caused by a secondary timer or event finished + const secondaryRunning = + updatedSecondaryTimer <= 0 && updatedSecondaryTimer != null; + + if (isFinished || secondaryRunning) { + // look for events + doRollLoad = true; + } + + return {updatedTimer, updatedSecondaryTimer, doRollLoad, isFinished}; +} + diff --git a/server/src/classes/integrations/Http.js b/server/src/classes/integrations/Http.js new file mode 100644 index 000000000..f3a9194a0 --- /dev/null +++ b/server/src/classes/integrations/Http.js @@ -0,0 +1,52 @@ +/** Class contains logic towards outgoing HTTP communications. */ +import * as http from 'http'; + +export class HTTPIntegration { + + constructor() { + // nothing to do here + } + + + /** + * @description Initializes oscClient + * @param {object} httpConfig - Http configurations options + */ + init(httpConfig) { + + } + + /** + * @description Sends http get request from predefined messages + * @param {string} path - complete http path + */ + async send(path) { + if (path == null) { + console.log('HTTP ERROR: Message undefined'); + return; + } + + const options = new URL(path); + let str = ''; + + const req = http.request(options, res => { + console.log(`statusCode: ${res.statusCode}`) + + res.on('data', function (chunk) { + str += chunk; + }); + + res.on('end', function () { + console.log(str); + }); + }) + + req.on('error', error => { + console.error(error) + }) + + req.end() + } + + shutdown() { /* Nothing to shutdown */ } +} \ No newline at end of file diff --git a/server/src/classes/integrations/Osc.js b/server/src/classes/integrations/Osc.js new file mode 100644 index 000000000..8c78250ff --- /dev/null +++ b/server/src/classes/integrations/Osc.js @@ -0,0 +1,107 @@ +/** Class contains logic towards outgoing OSC communications. */ +import {Client, Message} from 'node-osc'; + +export class OSCIntegration { + + ADDRESS = '/ontime'; + + constructor() { + // OSC Client + this.oscClient = null; + } + + /** + * @description Returns list of implemented messages + * @returns {object} implemented messages + */ + get implemented() { + return { + play: 'play', + pause: 'pause', + stop: 'stop', + previous: 'prev', + next: 'next', + reload: 'reload', + finished: 'finished', + time: 'time', + overtime: 'overtime', + title: 'title', + presenter:'presenter', + } + } + + /** + * @description Initializes oscClient + * @param {object} oscConfig - oscClient configuration options + * @param {string} oscConfig.ip - oscClient object + * @param {number} oscConfig.port - OSC Destination Port + */ + init(oscConfig) { + const {ip, port} = oscConfig; + try { + this.oscClient = new Client(ip, port); + console.log(`Initialised OSC Client at ${ip}:${port}`); + } catch (error) { + this.oscClient = null; + console.log(`Failed initialising OSC Client: ${error}`); + } + } + + /** + * @description Sends osc from predefined messages + * @param {string} messageType - message to be sent + * @param {string} [payload] - optional payload required in some message types + */ + async send(messageType, payload) { + if (this.oscClient == null) { + console.log('OSC ERROR: Client not initialised'); + return; + } + + if (messageType == null) { + console.log('OSC ERROR: Message undefined'); + return; + } + + // only specify special cases + switch (payload) { + case 'overtime': + // Whether timer is negative + this.oscClient.send(`${this.ADDRESS}/overtime`, payload, (err) => { + if (err) console.error(err); + }); + break; + case 'title': + if (payload != null && payload !== "") { + // Send Title of current event + this.oscClient.send(`${this.ADDRESS}/title`, payload, (err) => { + if (err) console.error(err); + }); + } + break; + case 'presenter': + if (payload != null && payload !== "") { + // Send presenter data on current event + this.oscClient.send(`${this.ADDRESS}/presenter`, payload, (err) => { + if (err) console.error(err); + }); + } + break; + default: + // catch all for messages, allows to add new messages + // but should be used with the integrations definition + const message = new Message(`${this.ADDRESS}/${messageType}`) + if (payload != null) message.append(payload) + this.oscClient.send(message, (err) => { + if (err) console.error(err); + }); + break; + } + } + + shutdown() { + // Shutdown client object + this.oscClient.close(); + this.oscClient = null; + } +} \ No newline at end of file diff --git a/server/src/config/config.js b/server/src/config/config.js index fb0132048..848c76e80 100644 --- a/server/src/config/config.js +++ b/server/src/config/config.js @@ -11,7 +11,13 @@ export const config = { }, osc: { port: 8888, - ipOut: '127.0.0.1', portOut: 9999, + targetIP: '127.0.0.1', + enabled: true, + }, + http: { + user: '', + pwd: '', + enabled: true, }, }; diff --git a/server/src/controllers/OscController.js b/server/src/controllers/OscController.js index 45a6d715e..398d11937 100644 --- a/server/src/controllers/OscController.js +++ b/server/src/controllers/OscController.js @@ -47,32 +47,32 @@ export const initiateOSC = (config) => { case 'start': case 'play': console.log('calling play'); - global.timer.start(); + global.timer.trigger('start'); break; case 'pause': console.log('calling pause'); - global.timer.pause(); + global.timer.trigger('pause'); break; case 'prev': console.log('calling prev'); - global.timer.previous(); + global.timer.trigger('previous'); break; case 'next': console.log('calling next'); - global.timer.next(); + global.timer.trigger('next'); break; case 'unload': case 'stop': console.log('calling unload'); - global.timer.unload(); + global.timer.trigger('unload'); break; case 'reload': console.log('calling reload'); - global.timer.reload(); + global.timer.trigger('reload'); break; case 'roll': console.log('calling roll'); - global.timer.roll(); + global.timer.trigger('roll'); break; case 'delay': console.log('calling delay with', args); diff --git a/server/src/controllers/eventsController.js b/server/src/controllers/eventsController.js index a990d216b..128a0a7e7 100644 --- a/server/src/controllers/eventsController.js +++ b/server/src/controllers/eventsController.js @@ -9,14 +9,6 @@ import { block as blockDef, } from '../models/eventsDefinition.js'; -function _getEventsCount() { - return Array.from(data.events).length; -} - -function _pushNew(entry) { - return data.events.push(entry).write(); -} - async function _insertAt(entry, index) { // get events let events = data.events; @@ -26,7 +18,7 @@ async function _insertAt(entry, index) { // Remove order field from object delete entry.order; - // Insert at beggining + // Insert at beginning if (order === 0) { events.unshift(entry); } @@ -47,7 +39,7 @@ async function _insertAt(entry, index) { } async function _removeById(eventId) { - data.events = Array.from(data.events).filter((e) => e.id != eventId); + data.events = Array.from(data.events).filter((e) => e.id !== eventId); await db.write(); } @@ -122,7 +114,7 @@ export const eventsPost = async (req, res) => { const index = newEvent.order || 0; // add new event in place - _insertAt(newEvent, index); + await _insertAt(newEvent, index); // update timers _updateTimers(); @@ -160,7 +152,7 @@ export const eventsPut = async (req, res) => { const e = data.events[eventIndex]; data.events[eventIndex] = { ...e, ...req.body }; data.events[eventIndex].revision++; - db.write(); + await db.write(); // update timer _updateTimersSingle(eventId, req.body); @@ -176,7 +168,7 @@ export const eventsPut = async (req, res) => { // Returns - export const eventsPatch = async (req, res) => { // Code is the same as put, call that - eventsPut(req, res); + await eventsPut(req, res); }; export const eventsReorder = async (req, res) => { @@ -207,7 +199,7 @@ export const eventsReorder = async (req, res) => { // save events data.events = events; - db.write(); + await db.write(); // TODO: would it be more efficient to reorder at timer? // update timer @@ -273,7 +265,7 @@ export const eventsApplyDelay = async (req, res) => { // update events data.events = events; - db.write(); + await db.write(); // update timer _updateTimers(); @@ -297,7 +289,7 @@ export const eventsDelete = async (req, res) => { try { // remove new event - _removeById(req.params.eventId); + await _removeById(req.params.eventId); // update timer _deleteTimerId(req.params.eventId); @@ -316,7 +308,7 @@ export const eventsDeleteAll = async (req, res) => { try { // set with nothing data.events = []; - db.write(); + await db.write(); // update timer object global.timer.clearEventList(); diff --git a/server/src/controllers/ontimeController.js b/server/src/controllers/ontimeController.js index 1b0258b61..3e9555bba 100644 --- a/server/src/controllers/ontimeController.js +++ b/server/src/controllers/ontimeController.js @@ -95,9 +95,13 @@ const getNetworkInterfaces = () => { export const getInfo = async (req, res) => { const version = data.settings.version; const serverPort = data.settings.serverPort; - const oscInPort = data.settings.oscInPort; - const oscOutPort = data.settings.oscOutPort; - const oscOutIP = data.settings.oscOutIP; + + const osc = { + port: data.osc.port, + portOut: data.osc.portOut, + targetIP: data.osc.targetIP, + enabled: data.osc.enabled, + }; // get nif and inject localhost const ni = getNetworkInterfaces(); @@ -108,9 +112,7 @@ export const getInfo = async (req, res) => { networkInterfaces: ni, version, serverPort, - oscInPort, - oscOutPort, - oscOutIP, + osc, }); }; @@ -132,6 +134,31 @@ export const postInfo = async (req, res) => { } }; +// Create controller for POST request to '/ontime/osc' +// Returns - +export const getOSC = async (req, res) => { + // send object with network information + res.status(200).send(data.osc); +}; + +// Create controller for POST request to '/ontime/osc' +// Returns ACK message +export const postOSC = async (req, res) => { + if (!req.body) { + res.status(400).send('No object found in request'); + return; + } + // TODO: validate data + try { + data.osc = { ...data.osc, ...req.body }; + await db.write(); + res.sendStatus(200); + } catch (error) { + res.status(400).send(error); + console.log(error); + } +}; + // Create controller for POST request to '/ontime/db' // Returns - export const dbUpload = async (req, res) => { diff --git a/server/src/controllers/playbackController.js b/server/src/controllers/playbackController.js index 0a5aca215..b97ba1b26 100644 --- a/server/src/controllers/playbackController.js +++ b/server/src/controllers/playbackController.js @@ -8,79 +8,68 @@ export const pbGet = async (req, res) => { // Turns onAir flag to true export const onAir = async (req, res) => { console.log('Setting onAir to true'); - global.timer.setonAir(true); - res.sendStatus(200); + global.timer.trigger('onAir') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/onAir' // Turns onAir flag to true export const offAir = async (req, res) => { console.log('Setting onAir to false'); - global.timer.setonAir(false); - res.sendStatus(200); + global.timer.trigger('offAir') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/start' // Starts timer object export const pbStart = async (req, res) => { console.log('Calling start'); - global.timer.start(); - res.sendStatus(200); + global.timer.trigger('start') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/pause' // Pauses timer object export const pbPause = async (req, res) => { console.log('Calling pause'); - global.timer.pause(); - res.sendStatus(200); + global.timer.trigger('pause') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/stop' // Stops timer object export const pbStop = async (req, res) => { console.log('Calling stop'); - global.timer.stop(); - res.sendStatus(200); + global.timer.trigger('stop') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/roll' // Sets timer object to roll mode export const pbRoll = async (req, res) => { console.log('Calling roll'); - global.timer.roll(); - res.sendStatus(501); + global.timer.trigger('roll') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/previous' // Sets timer object to roll mode export const pbPrevious = async (req, res) => { console.log('Calling previous'); - global.timer.previous(); - res.sendStatus(200); + global.timer.trigger('previous') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/next' // Sets timer object to roll mode export const pbNext = async (req, res) => { console.log('Calling next'); - global.timer.next(); - res.sendStatus(200); + global.timer.trigger('next') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/unload' // Unloads any events export const pbUnload = async (req, res) => { console.log('Calling unload'); - global.timer.unload(); - - res.sendStatus(200); + global.timer.trigger('unload') ? res.sendStatus(200) : res.sendStatus(400); }; // Create controller for GET request to '/playback/reload' // Reloads current event export const pbReload = async (req, res) => { console.log('Calling reload'); - global.timer.reload(); - res.sendStatus(200); + global.timer.trigger('reload') ? res.sendStatus(200) : res.sendStatus(400); }; diff --git a/server/src/models/dataModel.js b/server/src/models/dataModel.js index 0e012ad6b..dff445966 100644 --- a/server/src/models/dataModel.js +++ b/server/src/models/dataModel.js @@ -11,10 +11,43 @@ export const dbModelv1 = { app: 'ontime', version: 1, serverPort: 4001, - oscInPort: 8888, - oscOutPort: 9999, - oscOutIP: '127.0.0.1', - oscEnabled: false, - lock: false, + lock: null, + }, + osc: { + port: 8888, + portOut: 9999, + targetIP: '127.0.0.1', + enabled: true, + }, + http: { + user: null, + pwd: null, + messages: { + onLoad: { + url: '', + enabled: false, + }, + onStart: { + url: '', + enabled: false, + }, + onUpdate: { + url: '', + enabled: false, + }, + onPause: { + url: '', + enabled: false, + }, + onStop: { + url: '', + enabled: false, + }, + onFinish: { + url: '', + enabled: false, + }, + }, + enabled: true, }, }; diff --git a/server/src/package.json b/server/src/package.json index 1ed93838b..d00612903 100644 --- a/server/src/package.json +++ b/server/src/package.json @@ -15,15 +15,7 @@ "socket.io": "^4.3.1", "universal-analytics": "^0.4.23" }, - "devDependencies": { - "eslint": "^7.26.0", - "eslint-config-airbnb": "^18.2.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jsx-a11y": "^6.4.1", - "eslint-plugin-react": "^7.23.2", - "eslint-plugin-react-hooks": "^4.2.0", - "eslint-plugin-simple-import-sort": "^7.0.0" - }, + "devDependencies": {}, "scripts": { "nodestart": "nodemon app.js", "start": "node app.js" diff --git a/server/src/routes/ontimeRouter.js b/server/src/routes/ontimeRouter.js index 1aa8975b2..958b6ff2a 100644 --- a/server/src/routes/ontimeRouter.js +++ b/server/src/routes/ontimeRouter.js @@ -8,6 +8,8 @@ import { getInfo, postInfo, dbPathToUpload, + getOSC, + postOSC, } from '../controllers/ontimeController.js'; // create route between controller and '/ontime/db' endpoint @@ -17,10 +19,16 @@ router.get('/db', dbDownload); router.post('/db', uploadFile, dbUpload); // create route between controller and '/ontime/info' endpoint -router.get('/info', uploadFile, getInfo); +router.get('/info', getInfo); // create route between controller and '/ontime/info' endpoint router.post('/info', postInfo); +// create route between controller and '/ontime/osc' endpoint +router.get('/osc', getOSC); + +// create route between controller and '/ontime/osc' endpoint +router.post('/osc', postOSC); + // create route between controller and '/ontime/dbpath' endpoint router.post('/dbpath', dbPathToUpload); diff --git a/server/src/utils/__tests__/parser.tests.js b/server/src/utils/__tests__/parser.tests.js index 8244a617c..d6d020cfc 100644 --- a/server/src/utils/__tests__/parser.tests.js +++ b/server/src/utils/__tests__/parser.tests.js @@ -6,7 +6,6 @@ import { validateEventv1, } from '../parser.js'; import { dbModelv1 as dbModel } from '../../models/dataModel.js'; -import { describe } from 'jest-circus'; describe('test json parser with valid def', () => { const testData = { @@ -133,6 +132,7 @@ describe('test json parser with valid def', () => { revision: 0, id: '4b31', }; + expect(first).toStrictEqual(expected); }); it('loaded event settings', () => { @@ -192,7 +192,7 @@ describe('test parser edge cases', () => { const parseResponse = await parseJsonv1(testData); expect(console.log).toHaveBeenCalledWith( - 'ERROR: ID colision on import, skipping' + 'ERROR: ID collision on import, skipping' ); expect(parseResponse?.events.length).toBe(1); }); @@ -267,11 +267,7 @@ describe('test corrupt data', () => { app: 'ontime', version: 1, serverPort: 4001, - oscInPort: 8888, - oscOutPort: 9999, - oscOutIP: '127.0.0.1', - oscEnabled: false, - lock: false, + lock: null, }, }; @@ -293,11 +289,7 @@ describe('test corrupt data', () => { app: 'ontime', version: 1, serverPort: 4001, - oscInPort: 8888, - oscOutPort: 9999, - oscOutIP: '127.0.0.1', - oscEnabled: false, - lock: false, + lock: null, }, }; @@ -313,11 +305,7 @@ describe('test corrupt data', () => { app: 'ontime', version: 1, serverPort: 4001, - oscInPort: 8888, - oscOutPort: 9999, - oscOutIP: '127.0.0.1', - oscEnabled: false, - lock: false, + lock: null, }, }; @@ -430,7 +418,6 @@ describe('test makeString function', () => { expect(converted).toBe(expected); val = { doing: 'testing' }; - expected = 'testing'; converted = makeString(val, 'fallback'); expect(converted).toBe('fallback'); }); @@ -477,8 +464,8 @@ describe('test parseExcel function', () => { const expectedParsedEvents = [ { - timeStart: 28800000, - timeEnd: 32410000, + timeStart: 25200000, + timeEnd: 28810000, title: 'Guest Welcome', presenter: 'Carlos', subtitle: 'Getting things started', @@ -487,8 +474,8 @@ describe('test parseExcel function', () => { type: 'event', }, { - timeStart: 32400000, - timeEnd: 34200000, + timeStart: 28800000, + timeEnd: 30600000, title: 'A song from the hearth', presenter: 'Still Carlos', subtitle: 'Derailing early', diff --git a/server/src/utils/__tests__/time.tests.js b/server/src/utils/__tests__/time.tests.js index 6c2c7d61b..1b443c48e 100644 --- a/server/src/utils/__tests__/time.tests.js +++ b/server/src/utils/__tests__/time.tests.js @@ -1,4 +1,3 @@ -import { describe } from 'jest-circus'; import { excelDateStringToMillis, stringFromMillis } from '../time.js'; describe('test string to milis function', () => { @@ -60,12 +59,15 @@ describe('test string to milis function', () => { describe('test excel date parser', () => { it('parses the given dates correctly', () => { + const d0 = '1899-12-30T00:00:00.000Z'; const d1 = '1899-12-30T08:00:00.000Z'; const d2 = '1899-12-30T08:30:00.000Z'; + const d0Millis = 0; const d1Millis = 28800000; const d2Millis = 30600000; + expect(excelDateStringToMillis(d0)).toBe(d0Millis); expect(excelDateStringToMillis(d1)).toBe(d1Millis); expect(excelDateStringToMillis(d2)).toBe(d2Millis); }); diff --git a/server/src/utils/__tests__/url.test.js b/server/src/utils/__tests__/url.test.js new file mode 100644 index 000000000..c9d055c35 --- /dev/null +++ b/server/src/utils/__tests__/url.test.js @@ -0,0 +1,36 @@ +// test cleanURL() + +import {cleanURL} from "../url"; + +describe('url is correctly formatted', () => { + it('has no leading spaces', () => { + const test = ' http://testing'; + const expected = 'http://testing'; + expect(cleanURL(test)).toBe(expected); + }); + + it('has no trailing spaces', () => { + const test = 'http://testing '; + const expected = 'http://testing'; + expect(cleanURL(test)).toBe(expected); + }); + + it('doesnt contain spaces', () => { + const test = 'http://t e s t i n g'; + const expected = 'http://t%20e%20s%20t%20i%20n%20g'; + expect(cleanURL(test)).toBe(expected); + }); + + it('only contains allowed characters', () => { + const test = 'http://<>[]{}|\^'; + const expected = 'http://'; + expect(cleanURL(test)).toBe(expected); + }); + + it('begins with http://', () => { + const test = 'ontime.com'; + const expected = 'http://ontime.com'; + expect(cleanURL(test)).toBe(expected); + }); + +}); \ No newline at end of file diff --git a/server/src/utils/parser.js b/server/src/utils/parser.js index eca861231..2f777bc7d 100644 --- a/server/src/utils/parser.js +++ b/server/src/utils/parser.js @@ -16,7 +16,7 @@ export const ALLOWED_TYPES = ['JSON', 'EXCEL']; /** * @description Middleware function that checks file type and calls relevant parser - * @argument {string} file - reference to file + * @param {string} file - reference to file * @return {object} - parse result message */ export const fileHandler = async (file) => { @@ -82,7 +82,7 @@ export const fileHandler = async (file) => { /** * @description Excel array parser - * @argument {array} excelData - array with excel sheet + * @param {array} excelData - array with excel sheet * @returns {object} - parsed object */ export const parseExcelv1 = async (excelData) => { @@ -196,11 +196,12 @@ export const parseExcelv1 = async (excelData) => { /** * @description JSON parser function for v1 of data system - * @argument {object} jsonData - json data JSON object to be parsed + * @param {object} jsonData - json data JSON object to be parsed + * @param {boolean} [enforce=false] - flag, tells to create an object anyway * @returns {object} - parsed object */ -export const parseJsonv1 = async (jsonData) => { +export const parseJsonv1 = async (jsonData, enforce=false) => { if (!jsonData || typeof jsonData !== 'object') { console.log('ERROR: Invalid JSON format'); return -1; @@ -213,9 +214,9 @@ export const parseJsonv1 = async (jsonData) => { let events = []; let ids = []; for (const e of jsonData.events) { - // doublecheck unique ids + // double check unique ids if (ids.indexOf(e?.id) !== -1) { - console.log('ERROR: ID colision on import, skipping'); + console.log('ERROR: ID collision on import, skipping'); continue; } @@ -243,13 +244,17 @@ export const parseJsonv1 = async (jsonData) => { // write to db returnData.events = events; console.log(`Uploaded file with ${numEntries} entries`); + } else if (enforce) { + returnData.events = []; + console.log(`Created events object in db`); } if ('event' in jsonData) { console.log('Found event data, importing...'); const e = jsonData.event; - // filter known properties - const event = { + + // filter known properties and write to db + returnData.event = { ...dbModelv1.event, title: e.title || dbModelv1.event.title, url: e.url || dbModelv1.event.url, @@ -257,9 +262,9 @@ export const parseJsonv1 = async (jsonData) => { backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo, endMessage: e.endMessage || dbModelv1.event.endMessage, }; - - // write to db - returnData.event = event; + } else if (enforce) { + returnData.event = dbModelv1.event; + console.log(`Created event object in db`); } // Settings handled partially @@ -271,11 +276,9 @@ export const parseJsonv1 = async (jsonData) => { if (s.app == null || s.version == null) { console.log('ERROR: unknown app version, skipping'); } else { - let settings = {}; - - if (s.oscInPort) settings.oscInPort = s.oscInPort; - if (s.oscOutPort) settings.oscOutPort = s.oscOutPort; - if (s.oscOutIP) settings.oscOutIP = s.oscOutIP; + let settings = { + lock: s.lock || null, + }; // write to db returnData.settings = { @@ -283,6 +286,49 @@ export const parseJsonv1 = async (jsonData) => { ...settings, }; } + } else if (enforce) { + returnData.settings = dbModelv1.settings; + console.log(`Created settings object in db`); + } + + // Import OSC settings if any + if ('osc' in jsonData) { + console.log('Found OSC definition, importing...'); + const s = jsonData.osc; + let osc = {}; + + if (s.port) osc.port = s.port; + if (s.portOut) osc.portOut = s.portOut; + if (s.targetIP) osc.targetIP = s.targetIP; + if (s.enabled) osc.enabled = s.enabled; + + // write to db + returnData.osc = { + ...dbModelv1.osc, + ...osc, + }; + } else if (enforce) { + returnData.osc = dbModelv1.osc; + console.log(`Created osc object in db`); + } + + // Import HTTP settings if any + if ('http' in jsonData) { + console.log('Found HTTP definition, importing...'); + const h = jsonData.osc; + let http = {}; + + if (h.user) http.user = h.user; + if (h.pwd) http.pwd = h.pwd; + + // write to db + returnData.http = { + ...dbModelv1.http, + ...http, + }; + } else if (enforce) { + returnData.http = dbModelv1.http; + console.log(`Created http object in db`); } return returnData; @@ -292,7 +338,7 @@ export const parseJsonv1 = async (jsonData) => { * @description Ensures variable is string, it skips object types * @param {any} val - variable to convert * @param {string} [fallback=''] - fallback value - * @returns {string} - value as string or fallback if not possibe + * @returns {string} - value as string or fallback if not possible */ export const makeString = (val, fallback = '') => { if (typeof val === 'string') return val; @@ -348,7 +394,7 @@ export const validateEventv1 = (eventArgs) => { /** * @description Delete file from system - * @argument {string} file - reference to file + * @param {string} file - reference to file */ const deleteFile = async (file) => { // delete a file @@ -361,7 +407,8 @@ const deleteFile = async (file) => { /** * @description Delete file from system - * @argument {string} file - reference to file + * @param {string} file - reference to file + * @returns {boolean} - whether file is valid JSON */ export const validateFile = (file) => { try { diff --git a/server/src/utils/time.js b/server/src/utils/time.js index 16fa9a1b5..771943b11 100644 --- a/server/src/utils/time.js +++ b/server/src/utils/time.js @@ -40,9 +40,8 @@ export const stringFromMillis = ( */ export const excelDateStringToMillis = (excelDate) => { const date = new Date(excelDate); - if (date instanceof Date && !isNaN(date)) { - const h = date.getHours(); + const h = date.getUTCHours(); const m = date.getMinutes(); const s = date.getSeconds(); diff --git a/server/src/utils/url.js b/server/src/utils/url.js new file mode 100644 index 000000000..cad293cf6 --- /dev/null +++ b/server/src/utils/url.js @@ -0,0 +1,21 @@ +/** + * @description Cleans given url + * @param {string} url - URL to be checked + * @returns {string} Sanitized url + */ +export const cleanURL = (url) => { + + // trim whitespaces + let r = url.trim(); + + // clear any whitespaces + r = r.split(' ').join('%20'); + + // contain only allowed characters + r = r.replace(/([^\x00-\x7F]|[@\s<>\[\]{}|\\^])+/g, '') + + // starts with http:// + if (!r.startsWith('http://')) r = `http://${r}` + + return r; +} \ No newline at end of file diff --git a/server/src/yarn.lock b/server/src/yarn.lock index 6be3babc9..2069066cb 100644 --- a/server/src/yarn.lock +++ b/server/src/yarn.lock @@ -2,42 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/helper-validator-identifier@^7.14.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/highlight@^7.10.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/runtime-corejs3@^7.10.2": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz#403139af262b9a6e8f9ba04a6fdcebf8de692bf1" - integrity sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg== - dependencies: - core-js-pure "^3.16.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" - integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/runtime@^7.15.4": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" @@ -45,35 +9,6 @@ dependencies: regenerator-runtime "^0.13.4" -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== - dependencies: - ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" - import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - strip-json-comments "^3.1.1" - -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.0" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== - "@types/component-emitter@^1.2.10": version "1.2.11" resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.11.tgz#50d47d42b347253817a39709fef03ce66a108506" @@ -89,11 +24,6 @@ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - "@types/node@>=10.0.0": version "16.11.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.4.tgz#90771124822d6663814f7c1c9b45a6654d8fd964" @@ -107,16 +37,6 @@ accepts@~1.3.4, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-jsx@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - adler-32@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25" @@ -132,7 +52,7 @@ adler-32@~1.3.0: dependencies: printj "~1.2.2" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.12.3: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -142,94 +62,16 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.6.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" - integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - append-field@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -array-includes@^3.1.1, array-includes@^3.1.3, array-includes@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - -array.prototype.flat@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - -array.prototype.flatmap@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" - integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.19.0" - asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -242,16 +84,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -267,21 +99,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axe-core@^4.0.2: - version "4.3.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.3.tgz#b55cd8e8ddf659fe89b064680e1c6a4dceab0325" - integrity sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA== - -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - base64-arraybuffer@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz#87bd13525626db4a9838e00a508c2b73efcf348c" @@ -320,14 +137,6 @@ body-parser@1.19.0, body-parser@~1.19.0: raw-body "2.4.0" type-is "~1.6.17" -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - buffer-from@^1.0.0, buffer-from@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -346,19 +155,6 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -373,52 +169,11 @@ cfb@^1.1.4: crc-32 "~1.2.0" printj "~1.3.0" -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - codepage@~1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/codepage/-/codepage-1.15.0.tgz#2e00519024b39424ec66eeb3ec07227e692618ab" integrity sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA== -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -431,11 +186,6 @@ component-emitter@~1.3.0: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - concat-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" @@ -446,11 +196,6 @@ concat-stream@^1.5.2: readable-stream "^2.2.2" typedarray "^0.0.6" -confusing-browser-globals@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" - integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== - content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -478,11 +223,6 @@ cookie@0.4.1, cookie@~0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== -core-js-pure@^3.16.0: - version "3.18.3" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.18.3.tgz#7eed77dcce1445ab68fd68715856633e2fb3b90c" - integrity sha512-qfskyO/KjtbYn09bn1IPkuhHl5PlJ6IzJ9s9sraJ1EqcuGyLGKzhSM1cY0zgyL9hx42eulQLZ6WaeK5ycJCkqw== - core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -509,20 +249,6 @@ crc-32@~1.2.0: exit-on-epipe "~1.0.1" printj "~1.1.0" -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -damerau-levenshtein@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d" - integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw== - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -530,39 +256,20 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@2.6.9, debug@^2.6.9: +debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.0.1, debug@^4.1.1, debug@~4.3.1, debug@~4.3.2: +debug@^4.1.1, debug@~4.3.1, debug@~4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -591,20 +298,6 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dotenv@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" @@ -623,16 +316,6 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.0.0: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -661,278 +344,11 @@ engine.io@~6.0.0: engine.io-parser "~5.0.0" ws "~8.2.3" -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -es-abstract@^1.19.0, es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.1" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" - is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" - 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" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-config-airbnb-base@^14.2.1: - version "14.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e" - integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA== - dependencies: - confusing-browser-globals "^1.0.10" - object.assign "^4.1.2" - object.entries "^1.1.2" - -eslint-config-airbnb@^18.2.1: - version "18.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz#b7fe2b42f9f8173e825b73c8014b592e449c98d9" - integrity sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg== - dependencies: - eslint-config-airbnb-base "^14.2.1" - object.assign "^4.1.2" - object.entries "^1.1.2" - -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== - dependencies: - debug "^3.2.7" - resolve "^1.20.0" - -eslint-module-utils@^2.7.0: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c" - integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ== - dependencies: - debug "^3.2.7" - find-up "^2.1.0" - pkg-dir "^2.0.0" - -eslint-plugin-import@^2.22.1: - version "2.25.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz#b3b9160efddb702fc1636659e71ba1d10adbe9e9" - integrity sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g== - dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.0" - has "^1.0.3" - is-core-module "^2.7.0" - is-glob "^4.0.3" - minimatch "^3.0.4" - object.values "^1.1.5" - resolve "^1.20.0" - tsconfig-paths "^3.11.0" - -eslint-plugin-jsx-a11y@^6.4.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd" - integrity sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg== - dependencies: - "@babel/runtime" "^7.11.2" - aria-query "^4.2.2" - array-includes "^3.1.1" - ast-types-flow "^0.0.7" - axe-core "^4.0.2" - axobject-query "^2.2.0" - damerau-levenshtein "^1.0.6" - emoji-regex "^9.0.0" - has "^1.0.3" - jsx-ast-utils "^3.1.0" - language-tags "^1.0.5" - -eslint-plugin-react-hooks@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556" - integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ== - -eslint-plugin-react@^7.23.2: - version "7.26.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.26.1.tgz#41bcfe3e39e6a5ac040971c1af94437c80daa40e" - integrity sha512-Lug0+NOFXeOE+ORZ5pbsh6mSKjBKXDXItUD2sQoT+5Yl0eoT82DqnXeTMfUare4QVCn9QwXbfzO/dBLjLXwVjQ== - dependencies: - array-includes "^3.1.3" - array.prototype.flatmap "^1.2.4" - doctrine "^2.1.0" - estraverse "^5.2.0" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.0.4" - object.entries "^1.1.4" - object.fromentries "^2.0.4" - object.hasown "^1.0.0" - object.values "^1.1.4" - prop-types "^15.7.2" - resolve "^2.0.0-next.3" - semver "^6.3.0" - string.prototype.matchall "^4.0.5" - -eslint-plugin-simple-import-sort@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz#a1dad262f46d2184a90095a60c66fef74727f0f8" - integrity sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw== - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint@^7.26.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== - dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -1008,7 +424,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -1018,18 +434,6 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -1043,26 +447,6 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" - integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -1092,38 +476,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -1131,32 +483,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@^7.1.3: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^13.6.0, globals@^13.9.0: - version "13.11.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" - integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== - dependencies: - type-fest "^0.20.2" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -1170,40 +496,6 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -1242,158 +534,26 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" +inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-core-module@^2.2.0, is-core-module@^2.7.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== - dependencies: - has "^1.0.3" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== - -is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== - dependencies: - has-tostringtag "^1.0.0" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-weakref@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" - integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== - dependencies: - call-bind "^1.0.0" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -1404,29 +564,11 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -1437,33 +579,16 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -1474,64 +599,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" - integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== - dependencies: - array-includes "^3.1.3" - object.assign "^4.1.2" - -language-subtag-registry@~0.3.2: - version "0.3.21" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" - integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== - -language-tags@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= - dependencies: - language-subtag-registry "~0.3.2" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - -loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - lowdb@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-3.0.0.tgz#c10ab4e7eb86f1cbe255e35e60ffb0c6f42049e0" @@ -1539,13 +606,6 @@ lowdb@3.0.0: dependencies: steno "^2.1.0" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -1578,14 +638,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -1612,11 +665,6 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - multer@^1.4.2: version "1.4.3" resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.3.tgz#4db352d6992e028ac0eacf7be45c6efd0264297b" @@ -1636,11 +684,6 @@ nanoid@^3.1.22: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -1672,61 +715,6 @@ object-assign@^4, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.entries@^1.1.2, object.entries@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.fromentries@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.hasown@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" - integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.values@^1.1.4, object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -1739,25 +727,6 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - 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" - osc-min@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/osc-min/-/osc-min-1.1.2.tgz#3dcaff10f804c39b7080cf4c10fd7a4103cd9037" @@ -1765,32 +734,6 @@ osc-min@^1.1.1: dependencies: binpack "~0" -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -1816,26 +759,6 @@ passport@~0.4.1: passport-strategy "1.x.x" pause "0.0.1" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -1851,18 +774,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - printj@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" @@ -1883,20 +794,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - proxy-addr@~2.0.5: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -1945,11 +842,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -react-is@^16.8.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - readable-stream@1.1.x: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -1978,19 +870,6 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regexp.prototype.flags@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -regexpp@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -2017,39 +896,6 @@ request@^2.88.2: tunnel-agent "^0.6.0" uuid "^3.3.2" -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -resolve@^2.0.0-next.3: - version "2.0.0-next.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" - integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -2065,18 +911,6 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -2111,36 +945,6 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - socket.io-adapter@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.2.tgz#039cd7c71a52abad984a6d57da2c0b7ecdd3c289" @@ -2167,11 +971,6 @@ socket.io@^4.3.1: socket.io-adapter "~2.3.2" socket.io-parser "~4.0.4" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - ssf@~0.11.2: version "0.11.2" resolved "https://registry.yarnpkg.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c" @@ -2209,45 +1008,6 @@ streamsearch@0.1.2: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string.prototype.matchall@^4.0.5: - version "4.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" - integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" - side-channel "^1.0.4" - -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -2260,54 +1020,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -table@^6.0.9: - version "6.7.2" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" - integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -2321,16 +1033,6 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tsconfig-paths@^3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36" - integrity sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.0" - strip-bom "^3.0.0" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -2343,18 +1045,6 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -2375,16 +1065,6 @@ uid-safe@~2.1.5: dependencies: random-bytes "~1.0.0" -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== - dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" - which-boxed-primitive "^1.0.2" - universal-analytics@^0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" @@ -2421,11 +1101,6 @@ uuid@^3.0.0, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -2440,44 +1115,16 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - wmf@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da" integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw== -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - word@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961" integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - ws@~8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" @@ -2500,8 +1147,3 @@ xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/server/yarn.lock b/server/yarn.lock index 9582af137..74f69d977 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -1356,6 +1356,11 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" +confusing-browser-globals@^1.0.10: + version "1.0.11" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"