diff --git a/.eslintrc b/.eslintrc index 8976859eb..389d1e47f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -9,5 +9,26 @@ "extends": [ "eslint:recommended" ], - "rules": {} + "rules": { + // disallow certain object properties + // https://eslint.org/docs/rules/no-restricted-properties + "no-restricted-properties": [ + "error", + { + "object": "global", + "property": "isNaN", + "message": "Please use Number.isNaN instead" + }, + { + "object": "self", + "property": "isNaN", + "message": "Please use Number.isNaN instead" + }, + { + "object": "window", + "property": "isNaN", + "message": "Please use Number.isNaN instead" + } + ] + } } \ No newline at end of file diff --git a/.github/workflows/ontime_cy.yml b/.github/workflows/ontime_cy.yml index 3eb4a7cf1..ba151a10e 100644 --- a/.github/workflows/ontime_cy.yml +++ b/.github/workflows/ontime_cy.yml @@ -56,9 +56,15 @@ jobs: # App - name: Electron - Install dependencies - run: yarn install + run: yarn install && yarn make && yarn setdb working-directory: ./server - name: Electron - Run tests run: yarn test working-directory: ./server + + - name: Cypress run + uses: cypress-io/github-action@v2 + with: + working-directory: ./server + start: yarn cypress \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6819aafb4..c4f3a891e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ node_modules/ # testing coverage/ +*.mp4 # production build/ diff --git a/.prettierrc b/.prettierrc index 7dbcef92e..96e88733c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,5 +3,6 @@ "tabWidth": 2, "semi": true, "singleQuote": true, - "jsxSingleQuote": true + "jsxSingleQuote": true, + "printWidth": 100 } \ No newline at end of file diff --git a/client/.eslintrc b/client/.eslintrc index 5a459fde9..b5a3ec4a3 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -8,5 +8,6 @@ "jest/no-mocks-import": "warn", "no-useless-concat": "warn", "prefer-template": "warn" + } } diff --git a/client/package.json b/client/package.json index e64adc4bc..c12f6ef44 100644 --- a/client/package.json +++ b/client/package.json @@ -48,6 +48,7 @@ }, "devDependencies": { "@testing-library/react-hooks": "^7.0.2", + "prop-types": "^15.8.1", "react-test-renderer": "^17.0.2", "sass": "^1.44.0" } diff --git a/client/src/App.jsx b/client/src/App.jsx index ac14536c4..10b4aaf57 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -9,9 +9,14 @@ import { ALIASES } from './app/api/apiConstants'; import { getAliases } from './app/api/ontimeApi'; const Editor = lazy(() => import('features/editors/Editor')); -const PresenterView = lazy(() => - import('features/viewers/presenter/PresenterView') + +const TimerView = lazy(() => + import('features/viewers/timer/Timer') ); +const MinimalTimerView = lazy(() => + import('features/viewers/timer/MinimalTimer') +); + const StageManager = lazy(() => import('features/viewers/backstage/StageManager') ); @@ -22,7 +27,8 @@ const Lower = lazy(() => const Pip = lazy(() => import('features/viewers/production/Pip')); const StudioClock = lazy(() => import('features/viewers/studio/StudioClock')); -const SPresenter = withSocket(PresenterView); +const STimer = withSocket(TimerView); +const SMinimalTimer = withSocket(MinimalTimerView); const SStageManager = withSocket(StageManager); const SPublic = withSocket(Public); const SLowerThird = withSocket(Lower); @@ -36,6 +42,8 @@ function App() { // Handle keyboard shortcuts const handleKeyPress = useCallback((e) => { + // handle held key + if (e.repeat) return; // check if the alt key is pressed if (e.altKey) { if (e.key === 't' || e.key === 'T') { @@ -75,11 +83,19 @@ function App() { - } /> + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + } /> - } /> - } /> - } /> + } /> + } /> } /> } /> @@ -95,7 +111,7 @@ function App() { } /> {/* Send to default if nothing found */} - } /> + } /> diff --git a/client/src/app/api/ontimeApi.js b/client/src/app/api/ontimeApi.js index 26331631f..47cc97e31 100644 --- a/client/src/app/api/ontimeApi.js +++ b/client/src/app/api/ontimeApi.js @@ -25,7 +25,7 @@ export const oscPlaceholderSettings = { port: '', portOut: '', targetIP: '', - enabled: true, + enabled: false, }; export const httpPlaceholder = { @@ -66,7 +66,7 @@ export const ontimeVars = [ }, { name: '$presenter', - description: 'Current presenter', + description: 'Current timer', }, { name: '$subtitle', @@ -78,7 +78,7 @@ export const ontimeVars = [ }, { name: '$next-presenter', - description: 'Next presenter', + description: 'Next timer', }, { name: '$next-subtitle', diff --git a/client/src/app/entryValidator.js b/client/src/app/entryValidator.js new file mode 100644 index 000000000..34afcc96c --- /dev/null +++ b/client/src/app/entryValidator.js @@ -0,0 +1,13 @@ +/** + * @description Validates two time entries + * @param {number} timeStart + * @param {number} timeEnd + * @returns {{catch: string, value: boolean}} + */ +export const validateTimes = (timeStart, timeEnd) => { + let validate = { value: true, catch: '' }; + if (timeStart > timeEnd) { + validate.catch = 'Start time later than end time'; + } + return validate; +}; diff --git a/client/src/app/sampleData.js b/client/src/app/sampleData.js index f443fcb74..b67bac23b 100644 --- a/client/src/app/sampleData.js +++ b/client/src/app/sampleData.js @@ -2,7 +2,7 @@ const dummy = new Date(); export const sampleData = { presenterMessage: { - text: 'Only the presenter sees this', + text: 'Only the timer sees this', active: false, }, publicMessage: { diff --git a/client/src/common/components/buttons/DeleteIconBtn.jsx b/client/src/common/components/buttons/DeleteIconBtn.jsx index 193c77a5e..001d23af6 100644 --- a/client/src/common/components/buttons/DeleteIconBtn.jsx +++ b/client/src/common/components/buttons/DeleteIconBtn.jsx @@ -1,6 +1,7 @@ import { IconButton } from '@chakra-ui/button'; import { useState } from 'react'; -import { FiMinus } from 'react-icons/fi'; +import { IoRemove } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function DeleteIconBtn(props) { const { actionHandler, ...rest } = props; @@ -12,15 +13,17 @@ export default function DeleteIconBtn(props) { }; return ( - } - colorScheme='red' - onClick={handleClick} - _focus={{ boxShadow: 'none' }} - disabled={loading} - isLoading={loading} - {...rest} - /> + + } + colorScheme='red' + onClick={handleClick} + _focus={{ boxShadow: 'none' }} + disabled={loading} + isLoading={loading} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/EnableBtn.jsx b/client/src/common/components/buttons/EnableBtn.jsx new file mode 100644 index 000000000..d7cf68846 --- /dev/null +++ b/client/src/common/components/buttons/EnableBtn.jsx @@ -0,0 +1,18 @@ +import { IoCloseSharp, IoCheckmarkSharp } from 'react-icons/io5'; +import { Button } from '@chakra-ui/button'; + +export default function EnableBtn(props) { + const { active, text, actionHandler } = props; + return ( + + ); +} diff --git a/client/src/common/components/buttons/NextIconBtn.jsx b/client/src/common/components/buttons/NextIconBtn.jsx index 983df5ed5..8a611a07a 100644 --- a/client/src/common/components/buttons/NextIconBtn.jsx +++ b/client/src/common/components/buttons/NextIconBtn.jsx @@ -1,18 +1,21 @@ import { IconButton } from '@chakra-ui/button'; -import { FiSkipForward } from 'react-icons/fi'; +import { IoPlaySkipForward } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function NextIconBtn(props) { const { clickhandler, ...rest } = props; return ( - } - colorScheme='whiteAlpha' - backgroundColor='#ffffff11' - variant='outline' - onClick={clickhandler} - width={90} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='whiteAlpha' + backgroundColor='#ffffff11' + variant='outline' + onClick={clickhandler} + width={90} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/OnAirIconBtn.jsx b/client/src/common/components/buttons/OnAirIconBtn.jsx new file mode 100644 index 000000000..dd73da590 --- /dev/null +++ b/client/src/common/components/buttons/OnAirIconBtn.jsx @@ -0,0 +1,22 @@ +import { IconButton } from '@chakra-ui/button'; +import { IoMicSharp, IoMicOffOutline } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; + +export default function OnAirIconBtn(props) { + const { actionHandler, active, ...rest } = props; + return ( + + : } + colorScheme='blue' + variant={active ? 'solid' : 'outline'} + onClick={() => + actionHandler('update', { field: 'isPublic', value: !active }) + } + _focus={{ boxShadow: 'none' }} + {...rest} + /> + + ); +} diff --git a/client/src/common/components/buttons/PauseIconBtn.jsx b/client/src/common/components/buttons/PauseIconBtn.jsx index 4c3dff097..c44c60688 100644 --- a/client/src/common/components/buttons/PauseIconBtn.jsx +++ b/client/src/common/components/buttons/PauseIconBtn.jsx @@ -1,17 +1,20 @@ import { IconButton } from '@chakra-ui/button'; -import { FiPause } from 'react-icons/fi'; +import { IoPause } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function PauseIconBtn(props) { const { clickhandler, active, ...rest } = props; return ( - } - colorScheme='orange' - variant={active ? 'solid' : 'outline'} - onClick={clickhandler} - width={120} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='orange' + variant={active ? 'solid' : 'outline'} + onClick={clickhandler} + width={120} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/PrevIconBtn.jsx b/client/src/common/components/buttons/PrevIconBtn.jsx index a5ac937e1..8b33af75a 100644 --- a/client/src/common/components/buttons/PrevIconBtn.jsx +++ b/client/src/common/components/buttons/PrevIconBtn.jsx @@ -1,18 +1,21 @@ import { IconButton } from '@chakra-ui/button'; -import { FiSkipBack } from 'react-icons/fi'; +import { IoPlaySkipBack } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function PrevIconBtn(props) { const { clickhandler, ...rest } = props; return ( - } - colorScheme='whiteAlpha' - backgroundColor='#ffffff11' - variant='outline' - onClick={clickhandler} - width={90} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='whiteAlpha' + backgroundColor='#ffffff11' + variant='outline' + onClick={clickhandler} + width={90} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/ReloadIconBtn.jsx b/client/src/common/components/buttons/ReloadIconBtn.jsx index 4eec731e1..17ab489b0 100644 --- a/client/src/common/components/buttons/ReloadIconBtn.jsx +++ b/client/src/common/components/buttons/ReloadIconBtn.jsx @@ -1,18 +1,21 @@ import { IconButton } from '@chakra-ui/button'; -import { FiRefreshCcw } from 'react-icons/fi'; +import { IoReload } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function ReloadIconButton(props) { const { clickhandler, ...rest } = props; return ( - } - colorScheme='whiteAlpha' - backgroundColor='#ffffff05' - variant='outline' - onClick={clickhandler} - width={90} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='whiteAlpha' + backgroundColor='#ffffff05' + variant='outline' + onClick={clickhandler} + width={90} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/RollIconBtn.jsx b/client/src/common/components/buttons/RollIconBtn.jsx index 079e76f55..c452fc0c3 100644 --- a/client/src/common/components/buttons/RollIconBtn.jsx +++ b/client/src/common/components/buttons/RollIconBtn.jsx @@ -1,17 +1,20 @@ import { IconButton } from '@chakra-ui/button'; -import { FiClock } from 'react-icons/fi'; +import { IoTimeOutline } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function RollIconBtn(props) { const { clickhandler, active, ...rest } = props; return ( - } - colorScheme='blue' - variant={active ? 'solid' : 'outline'} - onClick={clickhandler} - width={120} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='blue' + variant={active ? 'solid' : 'outline'} + onClick={clickhandler} + width={120} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/StartIconBtn.jsx b/client/src/common/components/buttons/StartIconBtn.jsx index 06b9bbd80..b7cdeb1e7 100644 --- a/client/src/common/components/buttons/StartIconBtn.jsx +++ b/client/src/common/components/buttons/StartIconBtn.jsx @@ -1,17 +1,20 @@ import { IconButton } from '@chakra-ui/button'; -import { FiPlay } from 'react-icons/fi'; +import { IoPlay } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function StartIconBtn(props) { const { clickhandler, active, ...rest } = props; return ( - } - colorScheme='green' - variant={active ? 'solid' : 'outline'} - onClick={clickhandler} - width={120} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='green' + variant={active ? 'solid' : 'outline'} + onClick={clickhandler} + width={120} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/UnloadIconBtn.jsx b/client/src/common/components/buttons/UnloadIconBtn.jsx index c5c5f1812..715d87884 100644 --- a/client/src/common/components/buttons/UnloadIconBtn.jsx +++ b/client/src/common/components/buttons/UnloadIconBtn.jsx @@ -1,18 +1,20 @@ import { IconButton } from '@chakra-ui/button'; -import { FiXOctagon } from 'react-icons/fi'; +import { IoStop } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function UnloadIconBtn(props) { const { clickhandler, ...rest } = props; return ( - } - colorScheme='red' - backgroundColor='#ff000022' - variant='outline' - onClick={clickhandler} - width={90} - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='red' + variant='outline' + onClick={clickhandler} + width={90} + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/buttons/VisibleIconBtn.jsx b/client/src/common/components/buttons/VisibleIconBtn.jsx index ba2f4ec0f..5e236a7e7 100644 --- a/client/src/common/components/buttons/VisibleIconBtn.jsx +++ b/client/src/common/components/buttons/VisibleIconBtn.jsx @@ -1,19 +1,22 @@ import { IconButton } from '@chakra-ui/button'; -import { FiSun } from 'react-icons/fi'; +import { IoSunny } from 'react-icons/io5'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function VisibleIconBtn(props) { const { actionHandler, active, ...rest } = props; return ( - } - colorScheme='blue' - variant={active ? 'solid' : 'outline'} - onClick={() => - actionHandler('update', { field: 'isPublic', value: !active }) - } - _focus={{ boxShadow: 'none' }} - {...rest} - /> + + } + colorScheme='blue' + variant={active ? 'solid' : 'outline'} + onClick={() => + actionHandler('update', { field: 'isPublic', value: !active }) + } + _focus={{ boxShadow: 'none' }} + {...rest} + /> + ); } diff --git a/client/src/common/components/errorBoundary/ErrorBoundary.jsx b/client/src/common/components/errorBoundary/ErrorBoundary.jsx index d8662eac9..ce078de51 100644 --- a/client/src/common/components/errorBoundary/ErrorBoundary.jsx +++ b/client/src/common/components/errorBoundary/ErrorBoundary.jsx @@ -19,7 +19,6 @@ class ErrorBoundary extends React.Component { error: error, errorInfo: info, }); - // TODO: Log the error to an error reporting service this.context.emitError(error.toString()); } diff --git a/client/src/common/components/eventTimes/EventTimes.jsx b/client/src/common/components/eventTimes/EventTimes.jsx index cfc0d9300..18fd45f1a 100644 --- a/client/src/common/components/eventTimes/EventTimes.jsx +++ b/client/src/common/components/eventTimes/EventTimes.jsx @@ -1,27 +1,33 @@ import EditableTimer from 'common/input/EditableTimer'; import { useContext } from 'react'; import { LoggingContext } from '../../../app/context/LoggingContext'; +import { validateTimes } from '../../../app/entryValidator'; +import PropTypes from 'prop-types'; export default function EventTimes(props) { - const { actionHandler, delay, timeStart, timeEnd } = props; + const { actionHandler, delay, timeStart, timeEnd, previousEnd } = props; const { emitWarning } = useContext(LoggingContext); - const handleValidate = (entry, v) => { - // we dont enforce validation here - - if (v == null || timeStart == null || timeEnd == null) return true; + const handleValidate = (entry, val) => { + if (val == null || timeStart == null || timeEnd == null) return true; if (timeStart === 0) return true; - let validate = { value: true, catch: '' }; - if (entry === 'timeStart' && v > timeEnd) { - validate.catch = 'Start time later than end time'; - } else if (entry === 'timeEnd' && v < timeStart) { - validate.catch = 'End time earlier than start time'; + let start = timeStart; + let end = timeEnd; + if (entry === 'timeStart') { + start = val; + } else if (entry === 'timeEnd') { + end = val; + } else { + return; } - if (validate.catch !== '') - emitWarning(`Time Input Warning: ${validate.catch}`); - return validate.value; + const valid = validateTimes(start, end); + // give warning but not enforce validation + if (!valid.value) { + emitWarning(`Time Input Warning: ${valid.catch}`); + } + return valid.value; }; return ( @@ -32,6 +38,7 @@ export default function EventTimes(props) { actionHandler={actionHandler} time={timeStart} delay={delay} + previousEnd={previousEnd} /> ); } + +EventTimes.propTypes = { + actionHandler: PropTypes.func.isRequired, + delay: PropTypes.number.isRequired, + timeStart: PropTypes.number.isRequired, + timeEnd: PropTypes.number.isRequired, + previousEnd: PropTypes.number.isRequired, +}; diff --git a/client/src/common/components/eventTimes/EventTimesVertical.jsx b/client/src/common/components/eventTimes/EventTimesVertical.jsx index 5081b7aee..c69b39f46 100644 --- a/client/src/common/components/eventTimes/EventTimesVertical.jsx +++ b/client/src/common/components/eventTimes/EventTimesVertical.jsx @@ -2,6 +2,8 @@ import EditableTimer from 'common/input/EditableTimer'; import { stringFromMillis } from 'ontime-utils/time'; import { useContext } from 'react'; import { LoggingContext } from '../../../app/context/LoggingContext'; +import { validateTimes } from '../../../app/entryValidator'; +import PropTypes from 'prop-types'; const label = { fontSize: '0.75em', @@ -9,8 +11,7 @@ const label = { }; const TimesDelayed = (props) => { - const { handleValidate, actionHandler, delay, timeStart, timeEnd, duration } = - props; + const { handleValidate, actionHandler, delay, timeStart, timeEnd, duration, previousEnd } = props; const scheduledStart = stringFromMillis(timeStart, false); const scheduledEnd = stringFromMillis(timeEnd, false); @@ -26,6 +27,7 @@ const TimesDelayed = (props) => { actionHandler={actionHandler} time={timeStart} delay={delay} + previousEnd={previousEnd} /> End {scheduledEnd} @@ -36,6 +38,7 @@ const TimesDelayed = (props) => { actionHandler={actionHandler} time={timeEnd} delay={delay} + previousEnd={previousEnd} /> Duration { actionHandler={actionHandler} time={duration} delay={0} + previousEnd={previousEnd} /> ); }; +TimesDelayed.propTypes = { + handleValidate: PropTypes.func.isRequired, + actionHandler: PropTypes.func.isRequired, + delay: PropTypes.number.isRequired, + timeStart: PropTypes.number.isRequired, + timeEnd: PropTypes.number.isRequired, + duration: PropTypes.number.isRequired, + previousEnd: PropTypes.number.isRequired, +}; + const Times = (props) => { - const { handleValidate, actionHandler, timeStart, timeEnd, duration } = props; + const { handleValidate, actionHandler, timeStart, timeEnd, duration, previousEnd } = props; return ( <> @@ -61,6 +75,7 @@ const Times = (props) => { actionHandler={actionHandler} time={timeStart} delay={0} + previousEnd={previousEnd} /> End { actionHandler={actionHandler} time={timeEnd} delay={0} + previousEnd={previousEnd} /> Duration { actionHandler={actionHandler} time={duration} delay={0} + previousEnd={previousEnd} /> ); }; +Times.propTypes = { + handleValidate: PropTypes.func.isRequired, + actionHandler: PropTypes.func.isRequired, + timeStart: PropTypes.number.isRequired, + timeEnd: PropTypes.number.isRequired, + duration: PropTypes.number.isRequired, + previousEnd: PropTypes.number.isRequired, +}; + export default function EventTimesVertical(props) { - const { delay, timeStart, timeEnd, duration } = props; + const { delay, timeStart, timeEnd, duration, previousEnd } = props; const { emitWarning } = useContext(LoggingContext); - const handleValidate = (entry, v) => { - // we dont enforce validation here - - if (v == null || timeStart == null || timeEnd == null) return true; + const handleValidate = (entry, val) => { + if (val == null || timeStart == null || timeEnd == null) return true; if (timeStart === 0) return true; - let validate = { value: true, catch: '' }; - if (entry === 'timeStart' && v > timeEnd) { - validate.catch = 'Start time later than end time'; - } else if (entry === 'timeEnd' && v < timeStart) { - validate.catch = 'End time earlier than start time'; + let start = timeStart; + let end = timeEnd; + if (entry === 'timeStart') { + start = val; + } else if (entry === 'timeEnd') { + end = val; + } else { + return; } - if (validate.catch !== '') { - emitWarning(`Time Input Warning: ${validate.catch}`); + const valid = validateTimes(start, end); + // give warning but not enforce validation + if (!valid.value) { + emitWarning(`Time Input Warning: ${valid.catch}`); } - return validate.value; + return valid.value; }; - return ( - (delay != null) && (delay > 0) ? ( - - ) : ( - - ) - ) + return delay != null && delay > 0 ? ( + + ) : ( + + ); } + +EventTimesVertical.propTypes = { + delay: PropTypes.number.isRequired, + timeStart: PropTypes.number.isRequired, + timeEnd: PropTypes.number.isRequired, + duration: PropTypes.number.isRequired, + previousEnd: PropTypes.number.isRequired, +}; \ No newline at end of file diff --git a/client/src/common/components/nav/NavLogo.jsx b/client/src/common/components/nav/NavLogo.jsx index 14264ee28..d0ed75a72 100644 --- a/client/src/common/components/nav/NavLogo.jsx +++ b/client/src/common/components/nav/NavLogo.jsx @@ -16,6 +16,8 @@ export default function NavLogo(props) { // Handle keyboard shortcuts const handleKeyPress = useCallback((e) => { + // handle held key + if (e.repeat) return; // Space bar if (e.keyCode === 32) { setShowNav((s) => !s); @@ -54,44 +56,51 @@ export default function NavLogo(props) { className={showNav ? style.nav : style.navHidden} > - Presenter + Timer + + + Minimal Timer Backstage Public Lower Thirds PIP Studio Clock diff --git a/client/src/common/components/smallTimer/SmallTimer.jsx b/client/src/common/components/smallTimer/SmallTimer.jsx deleted file mode 100644 index 57a573f36..000000000 --- a/client/src/common/components/smallTimer/SmallTimer.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import styles from './SmallTimer.module.css'; - -export default function SmallTimer({ label, time }) { - return ( -
-
{label}
-
{time}
-
- ); -} diff --git a/client/src/common/components/smallTimer/SmallTimer.module.css b/client/src/common/components/smallTimer/SmallTimer.module.css deleted file mode 100644 index 1ba221c26..000000000 --- a/client/src/common/components/smallTimer/SmallTimer.module.css +++ /dev/null @@ -1,23 +0,0 @@ -.smallTimer { - display: flex; - flex-direction: column; -} - -.label, -.timer { - font-size: 1vw; - text-align: center; -} - -.label { - font-size: 1.5vw; - color: #888; - margin-bottom: 0; -} - -.timer { - font-family: 'Open Sans', sans-serif; - - font-size: 4vw; - letter-spacing: 0.125em; -} diff --git a/client/src/common/input/ChakraInput.jsx b/client/src/common/input/ChakraInput.jsx deleted file mode 100644 index c282b2f65..000000000 --- a/client/src/common/input/ChakraInput.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import { FormErrorMessage } from '@chakra-ui/form-control'; -import { FormLabel } from '@chakra-ui/form-control'; -import { FormControl } from '@chakra-ui/form-control'; -import { Input } from '@chakra-ui/input'; -import { Field } from 'formik'; - -export default function ChakraInput(props) { - const { label, name, ...rest } = props; - return ( - - {({ field, form }) => { - return ( - - {label} - - {form.errors[name]} - - ); - }} - - ); -} diff --git a/client/src/common/input/EditableTimer.jsx b/client/src/common/input/EditableTimer.jsx index 8de5dbbc7..97b2756b9 100644 --- a/client/src/common/input/EditableTimer.jsx +++ b/client/src/common/input/EditableTimer.jsx @@ -1,15 +1,13 @@ import { Editable, EditableInput, EditablePreview } from '@chakra-ui/editable'; import { useContext, useEffect, useState } from 'react'; -import { - isTimeString, - timeStringToMillis, -} from '../utils/dateConfig'; +import { forgivingStringToMillis } from '../utils/dateConfig'; import { stringFromMillis } from 'ontime-utils/time'; import style from './EditableTimer.module.css'; import { LoggingContext } from '../../app/context/LoggingContext'; +import PropTypes from 'prop-types'; export default function EditableTimer(props) { - const { name, actionHandler, time, delay, validate } = props; + const { name, actionHandler, time, delay, validate, previousEnd } = props; const { emitError } = useContext(LoggingContext); const [value, setValue] = useState(''); @@ -33,13 +31,26 @@ export default function EditableTimer(props) { // Check if there is anything there if (value === '') return false; - // check if its valid time string - if (!isTimeString(value)) return false; + let newValMillis; - // convert entered value to milliseconds - const newValMillis = timeStringToMillis(value); + // check for known aliases + if (value === 'p' || value === 'prev' || value === 'previous') { + // string to pass should be the time of the end before + if (previousEnd != null) { + newValMillis = previousEnd; + } else { + newValMillis = 0; + } + } else if (value.startsWith('+')) { + // string to pass should add to the end before + const val = value.substring(1); + newValMillis = previousEnd + forgivingStringToMillis(val); + } else { + // convert entered value to milliseconds + newValMillis = forgivingStringToMillis(value); + } - // Time now and time submitedVal + // Time now and time submittedVal const originalMillis = time + delay; // check if time is different from before @@ -67,3 +78,12 @@ export default function EditableTimer(props) { ); } + +EditableTimer.propTypes = { + name: PropTypes.string.isRequired, + actionHandler: PropTypes.func.isRequired, + time: PropTypes.number.isRequired, + delay: PropTypes.number.isRequired, + validate: PropTypes.func.isRequired, + previousEnd: PropTypes.number.isRequired, +}; diff --git a/client/src/common/utils/__tests__/dateConfig.test.js b/client/src/common/utils/__tests__/dateConfig.test.js index d91e2a1c7..757896b76 100644 --- a/client/src/common/utils/__tests__/dateConfig.test.js +++ b/client/src/common/utils/__tests__/dateConfig.test.js @@ -1,7 +1,9 @@ import { formatDisplay, + isTimeString, millisToMinutes, millisToSeconds, + forgivingStringToMillis, timeStringToMillis, } from '../dateConfig'; @@ -244,3 +246,59 @@ describe('test timeStringToMillis function', () => { expect(timeStringToMillis(t.val)).toBe(t.result); }); }); + +describe('test isTimeString() function', () => { + test('it validates time strings', () => { + const ts = ['2', '2:10', '2:10:22']; + for (const s of ts) { + expect(isTimeString(s)).toBe(true); + } + }); + + test('it fails overloaded times', () => { + const ts = ['70', '89:10', '26:10:22']; + for (const s of ts) { + expect(isTimeString(s)).toBe(false); + } + }); +}); + +describe('test isTimeString() function handle different separators', () => { + const ts = ['2:10', '2,10', '2.10']; + for (const s of ts) { + test(`it handles ${s}`, () => { + expect(isTimeString(s)).toBe(true); + }); + } +}); + +describe('test timeHelper() function handles separators', () => { + const ts = ['1:2:3:10', '2,10', '2.10']; + for (const s of ts) { + test(`it handles ${s}`, () => { + expect(typeof forgivingStringToMillis(s)).toBe('number'); + }); + } +}); + +describe('test timeHelper() parses strings correctly', () => { + const ts = [ + { value: '', expect: 0 }, + { value: '0', expect: 0 }, + { value: '-0', expect: 0 }, + { value: '1', expect: 60 * 1000 }, + { value: '-1', expect: 60 * 1000 }, + { value: '1.2', expect: 60 * 1000 + 2 * 1000 }, + { value: '1.70', expect: 60 * 1000 + 70 * 1000 }, + { value: '1.1.1', expect: 60 * 60 * 1000 + 60 * 1000 + 1000 }, + { value: '12.1.1', expect: 12 * 60 * 60 * 1000 + 60 * 1000 + 1000 }, + { value: '12.55.1', expect: 12 * 60 * 60 * 1000 + 55 * 60 * 1000 + 1000 }, + { value: '12.55.40', expect: 12 * 60 * 60 * 1000 + 55 * 60 * 1000 + 40 * 1000 }, + ]; + + for (const s of ts) { + test(`it handles ${s.value}`, () => { + expect(forgivingStringToMillis(s.value)).toBe(s.expect); + }); + } +}); diff --git a/client/src/common/utils/dateConfig.js b/client/src/common/utils/dateConfig.js index 171729cd8..39ec0c868 100644 --- a/client/src/common/utils/dateConfig.js +++ b/client/src/common/utils/dateConfig.js @@ -5,7 +5,6 @@ const mts = 1000; // millis to seconds const mtm = 1000 * 60; // millis to minutes const mth = 1000 * 60 * 60; // millis to hours - /** * another go at simpler string formatting (counters) * @description Converts seconds to string representing time @@ -13,8 +12,7 @@ const mth = 1000 * 60 * 60; // millis to hours * @param {boolean} [hideZero] - whether to show hours in case its 00 * @returns {string} String representing absolute time 00:12:02 */ - -export function formatDisplay(seconds, hideZero=false) { +export function formatDisplay(seconds, hideZero = false) { // add an extra 0 if necessary const format = (val) => `0${Math.floor(val)}`.slice(-2); @@ -31,8 +29,6 @@ export function formatDisplay(seconds, hideZero=false) { * @param {number} millis - time in seconds * @returns {number} Amount in seconds */ - -// millis to seconds export const millisToSeconds = (millis) => { return millis < 0 ? Math.ceil(millis / mts) : Math.floor(millis / mts); }; @@ -42,8 +38,6 @@ export const millisToSeconds = (millis) => { * @param {number} millis - time in seconds * @returns {number} Amount in seconds */ - -// millis to minutes export const millisToMinutes = (millis) => { return millis < 0 ? Math.ceil(millis / mtm) : Math.floor(millis / mtm); }; @@ -53,15 +47,12 @@ export const millisToMinutes = (millis) => { * @param {string} string - time string "23:00:12" * @returns {number} Amount in milliseconds */ - -// timeStringToMillis export const timeStringToMillis = (string) => { if (typeof string !== 'string') return 0; const time = string.split(':'); if (time.length === 1) return Math.abs(time[0] * mts); if (time.length === 2) return Math.abs(time[0]) * mtm + time[1] * mts; - if (time.length === 3) - return Math.abs(time[0]) * mth + time[1] * mtm + time[2] * mts; + if (time.length === 3) return Math.abs(time[0]) * mth + time[1] * mtm + time[2] * mts; else return 0; }; @@ -70,8 +61,6 @@ export const timeStringToMillis = (string) => { * @param {string} string - time string "23:00:12" * @returns {boolean} string represents time */ - -// isTimeString export const isTimeString = (string) => { // ^ # Start of string // (?: # Try to match... @@ -83,6 +72,42 @@ export const isTimeString = (string) => { // ([0-5]?\d) # SS (required) // $ # End of string - const regex = /^(?:(?:([01]?\d|2[0-3]):)?([0-5]?\d):)?([0-5]?\d)$/; + const regex = /^(?:(?:([01]?\d|2[0-3])[:,.])?([0-5]?\d)[:,.])?([0-5]?\d)$/; return regex.test(string); }; + +const parse = (valueAsString) => { + const parsed = parseInt(valueAsString, 10); + if (isNaN(parsed)) { + return 0; + } + return Math.abs(parsed); +}; + +/** + * @description Parses a time string to millis + * @param string - time string + * @returns {number} - time string in millis + */ +export const forgivingStringToMillis = (string) => { + let millis = 0; + + // split string at known separators : , . + const separatorRegex = /[\s,:.]+/; + const [first, second, third] = string.split(separatorRegex); + + if (first != null && second != null && third != null) { + // if string has three sections, treat as [hours] [minutes] [seconds] + millis = parse(first) * mth; + millis += parse(second) * mtm; + millis += parse(third) * mts; + } else if (third == null) { + // if string has two sections, treat as [minutes] [seconds] + millis = parse(first) * mtm; + millis += parse(second) * mts; + } else if (second == null) { + // if string has one section, treat as [minutes] + millis = parse(first) * mtm; + } + return millis; +}; diff --git a/client/src/features/control/MessageControl.jsx b/client/src/features/control/MessageControl.jsx index 264c4c5f8..c9e4316ea 100644 --- a/client/src/features/control/MessageControl.jsx +++ b/client/src/features/control/MessageControl.jsx @@ -1,8 +1,8 @@ -import {Editable, EditableInput, EditablePreview} from '@chakra-ui/editable'; -import {Switch} from "@chakra-ui/react"; -import {useEffect, useState} from 'react'; -import {useSocket} from 'app/context/socketContext'; +import { Editable, EditableInput, EditablePreview } from '@chakra-ui/editable'; +import { useEffect, useState } from 'react'; +import { useSocket } from 'app/context/socketContext'; import VisibleIconBtn from 'common/components/buttons/VisibleIconBtn'; +import OnAirIconBtn from '../../common/components/buttons/OnAirIconBtn'; import style from './MessageControl.module.scss'; const inputProps = { @@ -10,7 +10,7 @@ const inputProps = { }; const InputRow = (props) => { - const {label, placeholder, text, visible} = props; + const { label, placeholder, text, visible } = props; return ( <> @@ -23,8 +23,8 @@ const InputRow = (props) => { className={style.inline} color={text === '' ? '#666' : 'inherit'} > - - + + { if (socket == null) return; - // Handle presenter messages - socket.on('messages-presenter', (data) => { - setPres({...data}); + // Handle timer messages + socket.on('messages-timer', (data) => { + setPres({ ...data }); }); // Handle public messages socket.on('messages-public', (data) => { - setPubl({...data}); + setPubl({ ...data }); }); // Handle lower third messages socket.on('messages-lower', (data) => { - setLower({...data}); + setLower({ ...data }); }); // Handle lower third messages @@ -83,7 +83,7 @@ export default function MessageControl() { // Clear listeners return () => { socket.off('messages-public'); - socket.off('messages-presenter'); + socket.off('messages-timer'); socket.off('messages-lower'); socket.off('onAir'); }; @@ -92,10 +92,10 @@ export default function MessageControl() { const messageControl = async (action, payload) => { switch (action) { case 'pres-text': - socket.emit('set-presenter-text', payload); + socket.emit('set-timer-text', payload); break; case 'toggle-pres-visible': - socket.emit('set-presenter-visible', !pres.visible); + socket.emit('set-timer-visible', !pres.visible); break; case 'publ-text': socket.emit('set-public-text', payload); @@ -146,13 +146,13 @@ export default function MessageControl() { />
- messageControl('toggle-onAir')}> - On Air? - + actionHandler={() => messageControl('toggle-onAir')} + /> + On Air {`/ontime/offAir << OSC >> /ontime/onAir`} diff --git a/client/src/features/control/MessageControl.module.scss b/client/src/features/control/MessageControl.module.scss index 52889d3fb..1ff50b75e 100644 --- a/client/src/features/control/MessageControl.module.scss +++ b/client/src/features/control/MessageControl.module.scss @@ -1,12 +1,12 @@ +@use '../../styles/main' as *; +@use '../../styles/mixins' as *; + .messageContainer, .onAirToggle { - background-color: rgba(0, 0, 0, 0.05); - border: 1px solid rgba(0, 0, 0, 0.05); - border-radius: 4px; + @include main-container; display: flex; gap: 0.5em; padding: 0.5em; - } .messageContainer { @@ -17,17 +17,20 @@ grid-template-columns: 1fr auto; gap: 1em; } + .label { padding: 0; margin: 0; font-size: 0.9em; color: #ccc; } + .inline { border-radius: 4px; background-color: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05); } + .padleft { padding-left: 0.5em; } @@ -35,20 +38,25 @@ .onAirToggle { margin-top: 1em; - display: flex; - gap: 1em; align-items: center; - line-height: 3em; + display: grid; + grid-template-areas: + 'btn label' + 'btn osc'; + grid-template-columns: 2.5em 1fr; + grid-template-rows: 1.2em 0.8em; + + .btn { + grid-area: btn; + } .onAirLabel { + grid-area: label; font-size: 1.2em; } + .oscLabel { - color: #4bffabcc; - font-size: 0.8em; - float: right; - padding-right: 1em; - -webkit-user-select: text; - user-select: text; + @include osc-label; + grid-area: osc; } } diff --git a/client/src/features/control/PlaybackTimer.jsx b/client/src/features/control/PlaybackTimer.jsx index fe5118187..a30e036c1 100644 --- a/client/src/features/control/PlaybackTimer.jsx +++ b/client/src/features/control/PlaybackTimer.jsx @@ -1,37 +1,37 @@ import style from './PlaybackControl.module.scss'; import Countdown from 'common/components/countdown/Countdown'; import { stringFromMillis } from 'ontime-utils/time'; -import {Tooltip} from '@chakra-ui/react'; -import {Button} from '@chakra-ui/button'; -import {memo} from 'react'; -import PropTypes from "prop-types"; +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.selectedId === nextProps.selectedId + 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, selectedId} = 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 disableButtons = selectedId == null || isRolling; const incrementProps = { size: 'sm', width: '2.9em', colorScheme: 'whiteAlpha', variant: 'outline', - _focus: {boxShadow: 'none'}, + _focus: { boxShadow: 'none' }, }; return ( @@ -39,12 +39,12 @@ const PlaybackTimer = (props) => {
-
+
-
+
{ )}
- - + + - +1 - - + + - -5 - - + + - +5 - + +
diff --git a/client/src/features/editors/list/BlockBlock.jsx b/client/src/features/editors/BlockBlock/BlockBlock.jsx similarity index 79% rename from client/src/features/editors/list/BlockBlock.jsx rename to client/src/features/editors/BlockBlock/BlockBlock.jsx index 35c717636..c66aed7f8 100644 --- a/client/src/features/editors/list/BlockBlock.jsx +++ b/client/src/features/editors/BlockBlock/BlockBlock.jsx @@ -1,8 +1,9 @@ import { Draggable } from 'react-beautiful-dnd'; import { FiMoreVertical } from 'react-icons/fi'; import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn'; -import ActionButtons from './ActionButtons'; +import ActionButtons from '../list/ActionButtons'; import style from './BlockBlock.module.css'; +import PropTypes from 'prop-types'; export default function BlockBlock(props) { const { index, data, actionHandler } = props; @@ -27,3 +28,10 @@ export default function BlockBlock(props) { ); } + + +BlockBlock.propTypes = { + index: PropTypes.number.isRequired, + data: PropTypes.object.isRequired, + actionHandler: PropTypes.func.isRequired, +}; diff --git a/client/src/features/editors/list/BlockBlock.module.css b/client/src/features/editors/BlockBlock/BlockBlock.module.css similarity index 100% rename from client/src/features/editors/list/BlockBlock.module.css rename to client/src/features/editors/BlockBlock/BlockBlock.module.css diff --git a/client/src/features/editors/list/DelayBlock.jsx b/client/src/features/editors/DelayBlock/DelayBlock.jsx similarity index 67% rename from client/src/features/editors/list/DelayBlock.jsx rename to client/src/features/editors/DelayBlock/DelayBlock.jsx index 1a9811e82..473413269 100644 --- a/client/src/features/editors/list/DelayBlock.jsx +++ b/client/src/features/editors/DelayBlock/DelayBlock.jsx @@ -1,11 +1,12 @@ import { Draggable } from 'react-beautiful-dnd'; import { FiMoreVertical } from 'react-icons/fi'; import { millisToMinutes } from 'common/utils/dateConfig'; -import ActionButtons from './ActionButtons'; +import ActionButtons from '../list/ActionButtons'; import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn'; import ApplyIconBtn from 'common/components/buttons/ApplyIconBtn'; import DelayInput from 'common/input/DelayInput'; import style from './DelayBlock.module.css'; +import PropTypes from 'prop-types'; export default function DelayBlock(props) { const { eventsHandler, data, index, actionHandler } = props; @@ -14,25 +15,15 @@ export default function DelayBlock(props) { eventsHandler('applyDelay', { id: data.id, duration: data.duration }); }; - let delayValue = - data.duration != null ? millisToMinutes(data.duration) : undefined; - + let delayValue = data.duration != null ? millisToMinutes(data.duration) : undefined; return ( {(provided) => ( -
+
- +
@@ -43,3 +34,10 @@ export default function DelayBlock(props) { ); } + +DelayBlock.propTypes = { + eventsHandler: PropTypes.func.isRequired, + data: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + actionHandler: PropTypes.func.isRequired, +}; diff --git a/client/src/features/editors/list/DelayBlock.module.css b/client/src/features/editors/DelayBlock/DelayBlock.module.css similarity index 100% rename from client/src/features/editors/list/DelayBlock.module.css rename to client/src/features/editors/DelayBlock/DelayBlock.module.css diff --git a/client/src/features/editors/list/EventBlock.jsx b/client/src/features/editors/EventBlock/EventBlock.jsx similarity index 64% rename from client/src/features/editors/list/EventBlock.jsx rename to client/src/features/editors/EventBlock/EventBlock.jsx index bc047f2f4..261370e61 100644 --- a/client/src/features/editors/list/EventBlock.jsx +++ b/client/src/features/editors/EventBlock/EventBlock.jsx @@ -5,17 +5,17 @@ import { Draggable } from 'react-beautiful-dnd'; import EventTimes from 'common/components/eventTimes/EventTimes'; import EventTimesVertical from 'common/components/eventTimes/EventTimesVertical'; import EditableText from 'common/input/EditableText'; -import ActionButtons from './ActionButtons'; +import ActionButtons from '../list/ActionButtons'; import PublicIconBtn from 'common/components/buttons/PublicIconBtn'; import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn'; import { millisToMinutes } from 'common/utils/dateConfig'; import style from './EventBlock.module.css'; -import { SelectCollapse, HandleCollapse } from 'app/context/collapseAtom'; +import { HandleCollapse, SelectCollapse } from 'app/context/collapseAtom'; import { useAtom } from 'jotai'; +import PropTypes from 'prop-types'; const ExpandedBlock = (props) => { - const { provided, data, eventIndex, next, delay, delayValue, actionHandler } = - props; + const { provided, data, eventIndex, next, delay, delayValue, previousEnd, actionHandler } = props; const oscid = data.id.length > 4 ? '...' : data.id; @@ -28,14 +28,12 @@ const ExpandedBlock = (props) => { return ( <> - +
Next - {delayValue != null && ( - + {delayValue} - )} + {delayValue != null && + {delayValue}}
{ timeEnd={data.timeEnd} duration={duration} delay={delay} + previousEnd={previousEnd} className={style.time} />
@@ -53,25 +52,19 @@ const ExpandedBlock = (props) => { label='Title' defaultValue={data.title} placeholder='Add Title' - submitHandler={(v) => - actionHandler('update', { field: 'title', value: v }) - } + submitHandler={(v) => actionHandler('update', { field: 'title', value: v })} /> - actionHandler('update', { field: 'presenter', value: v }) - } + submitHandler={(v) => actionHandler('update', { field: 'presenter', value: v })} /> - actionHandler('update', { field: 'subtitle', value: v }) - } + submitHandler={(v) => actionHandler('update', { field: 'subtitle', value: v })} /> { placeholder='Add Note' style={{ color: '#d69e2e' }} maxchar={160} - submitHandler={(v) => - actionHandler('update', { field: 'note', value: v }) - } + submitHandler={(v) => actionHandler('update', { field: 'note', value: v })} /> {`/ontime/goto ${eventIndex + 1} << OSC >> /ontime/gotoid ${oscid}`} @@ -89,38 +80,43 @@ const ExpandedBlock = (props) => {
- +
); }; +ExpandedBlock.propTypes = { + provided: PropTypes.any.isRequired, + data: PropTypes.object.isRequired, + eventIndex: PropTypes.number.isRequired, + next: PropTypes.bool.isRequired, + delay: PropTypes.number, + delayValue: PropTypes.number, + previousEnd: PropTypes.number.isRequired, + actionHandler: PropTypes.func.isRequired, +}; + const CollapsedBlock = (props) => { - const { provided, data, next, delay, delayValue, actionHandler } = props; + const { provided, data, next, delay, delayValue, previousEnd, actionHandler } = props; return ( <> - +
Next - {delayValue != null && ( - + {delayValue} - )} + {delayValue != null && + {delayValue}}
@@ -128,33 +124,32 @@ const CollapsedBlock = (props) => { label='Title' defaultValue={data.title} placeholder='Add Title' - submitHandler={(v) => - actionHandler('update', { field: 'title', value: v }) - } + submitHandler={(v) => actionHandler('update', { field: 'title', value: v })} />
- +
); }; +CollapsedBlock.propTypes = { + provided: PropTypes.any.isRequired, + data: PropTypes.object.isRequired, + next: PropTypes.bool.isRequired, + delay: PropTypes.any, + delayValue: PropTypes.any, + previousEnd: PropTypes.number.isRequired, + actionHandler: PropTypes.func.isRequired, +}; + export default function EventBlock(props) { - const { data, selected, delay, index, eventIndex, actionHandler } = props; - const [collapsed] = useAtom( - useMemo(() => SelectCollapse(data.id), [data.id]) - ); + const { data, selected, delay, index, eventIndex, previousEnd, actionHandler } = props; + const [collapsed] = useAtom(useMemo(() => SelectCollapse(data.id), [data.id])); const [, setCollapsed] = useAtom(HandleCollapse); - // TODO: should this go inside useEffect() - // Would I then need to add this to state? const isSelected = selected ? style.active : ''; const isCollapsed = collapsed ? style.collapsed : style.expanded; const classSelect = `${style.event} ${isCollapsed} ${isSelected}`; @@ -169,11 +164,7 @@ export default function EventBlock(props) { return ( {(provided) => ( -
+
) : ( @@ -196,6 +188,7 @@ export default function EventBlock(props) { next={props.next} delay={delay} delayValue={delayValue} + previousEnd={previousEnd} actionHandler={actionHandler} /> )} @@ -204,3 +197,13 @@ export default function EventBlock(props) { ); } + +EventBlock.propTypes = { + data: PropTypes.object.isRequired, + selected: PropTypes.bool.isRequired, + delay: PropTypes.number, + index: PropTypes.number.isRequired, + eventIndex: PropTypes.number.isRequired, + previousEnd: PropTypes.number.isRequired, + actionHandler: PropTypes.func.isRequired, +}; diff --git a/client/src/features/editors/list/EventBlock.module.css b/client/src/features/editors/EventBlock/EventBlock.module.css similarity index 100% rename from client/src/features/editors/list/EventBlock.module.css rename to client/src/features/editors/EventBlock/EventBlock.module.css diff --git a/client/src/features/editors/list/ActionButtons.jsx b/client/src/features/editors/list/ActionButtons.jsx index 6035a00ec..2e8b1a029 100644 --- a/client/src/features/editors/list/ActionButtons.jsx +++ b/client/src/features/editors/list/ActionButtons.jsx @@ -1,6 +1,7 @@ import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu'; import { IconButton } from '@chakra-ui/button'; -import { FiPlus, FiMinusCircle, FiClock } from 'react-icons/fi'; +import { FiClock, FiMinusCircle, FiPlus } from 'react-icons/fi'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function ActionButtons(props) { const { showAdd, showDelay, showBlock, actionHandler } = props; @@ -12,16 +13,18 @@ export default function ActionButtons(props) { return ( - } - _expanded={{ bg: 'orange.300', color: 'white' }} - _focus={{ boxShadow: 'none' }} - backgroundColor={'orange.200'} - color={'orange.500'} - /> + + } + _expanded={{ bg: 'orange.300', color: 'white' }} + _focus={{ boxShadow: 'none' }} + backgroundColor={'orange.200'} + color={'orange.500'} + /> + } diff --git a/client/src/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx index 8c535ddb8..d255ec312 100644 --- a/client/src/features/editors/list/EventList.jsx +++ b/client/src/features/editors/list/EventList.jsx @@ -20,8 +20,10 @@ export default function EventList(props) { // Handle keyboard shortcuts const handleKeyPress = useCallback( (e) => { + // handle held key + if (e.repeat) return; // Check if the alt key is pressed - if (e.altKey) { + if (e.altKey && (!e.ctrlKey || !e.shiftKey)) { // Arrow down if (e.keyCode === 40) { if (cursor == null) setCursor(0); @@ -145,6 +147,8 @@ export default function EventList(props) { let cumulativeDelay = 0; let eventIndex = -1; + let previousEnd = 0; + let thisEnd = 0; return (
@@ -167,6 +171,8 @@ export default function EventList(props) { cumulativeDelay = 0; } else if (e.type === 'event') { eventIndex++; + previousEnd = thisEnd; + thisEnd = e.timeEnd; } return ( @@ -184,6 +190,7 @@ export default function EventList(props) { next={nextId === e.id} eventsHandler={eventsHandler} delay={cumulativeDelay} + previousEnd={previousEnd} />
); diff --git a/client/src/features/editors/list/EventListItem.jsx b/client/src/features/editors/list/EventListItem.jsx index f4c5d5a98..6f0e0c088 100644 --- a/client/src/features/editors/list/EventListItem.jsx +++ b/client/src/features/editors/list/EventListItem.jsx @@ -1,6 +1,6 @@ -import DelayBlock from './DelayBlock'; -import BlockBlock from './BlockBlock'; -import EventBlock from './EventBlock'; +import DelayBlock from '../DelayBlock/DelayBlock'; +import BlockBlock from '../BlockBlock/BlockBlock'; +import EventBlock from '../EventBlock/EventBlock'; import { memo, useContext } from 'react'; import { LoggingContext } from '../../../app/context/LoggingContext'; @@ -10,7 +10,8 @@ const areEqual = (prevProps, nextProps) => { prevProps.selected === nextProps.selected && prevProps.next === nextProps.next && prevProps.index === nextProps.index && - prevProps.delay === nextProps.delay + prevProps.delay === nextProps.delay && + prevProps.previousEnd === nextProps.previousEnd ); }; @@ -24,6 +25,7 @@ const EventListItem = (props) => { next, eventsHandler, delay, + previousEnd, ...rest } = props; const { emitError } = useContext(LoggingContext); @@ -79,6 +81,7 @@ const EventListItem = (props) => { next={next} actionHandler={actionHandler} delay={delay} + previousEnd={previousEnd} /> ); case 'block': diff --git a/client/src/features/menu/MenuActionButtons.jsx b/client/src/features/menu/MenuActionButtons.jsx index 25a34304f..3f9205267 100644 --- a/client/src/features/menu/MenuActionButtons.jsx +++ b/client/src/features/menu/MenuActionButtons.jsx @@ -1,7 +1,8 @@ import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu'; import { IconButton } from '@chakra-ui/button'; -import { FiTrash2, FiPlus, FiClock, FiMinusCircle } from 'react-icons/fi'; +import { FiClock, FiMinusCircle, FiPlus, FiTrash2 } from 'react-icons/fi'; import { Divider } from '@chakra-ui/layout'; +import { Tooltip } from '@chakra-ui/tooltip'; export default function MenuActionButtons(props) { const { actionHandler } = props; @@ -12,16 +13,18 @@ export default function MenuActionButtons(props) { return ( - } - _expanded={{ bg: 'orange.300', color: 'white' }} - _focus={{ boxShadow: 'none' }} - backgroundColor={'orange.200'} - color={'orange.500'} - /> + + } + _expanded={{ bg: 'orange.300', color: 'white' }} + _focus={{ boxShadow: 'none' }} + backgroundColor={'orange.200'} + color={'orange.500'} + /> + } onClick={() => actionHandler('event')}> Add Event first diff --git a/client/src/features/menu/buttons/DownloadIconBtn.jsx b/client/src/features/menu/buttons/DownloadIconBtn.jsx index 63c81c7e3..cd662f453 100644 --- a/client/src/features/menu/buttons/DownloadIconBtn.jsx +++ b/client/src/features/menu/buttons/DownloadIconBtn.jsx @@ -5,7 +5,7 @@ import { FiDownload } from 'react-icons/fi'; export default function DownloadIconBtn(props) { const { clickhandler, ...rest } = props; return ( - + } diff --git a/client/src/features/menu/buttons/InfoIconBtn.jsx b/client/src/features/menu/buttons/InfoIconBtn.jsx deleted file mode 100644 index ad286e912..000000000 --- a/client/src/features/menu/buttons/InfoIconBtn.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import { IconButton } from '@chakra-ui/button'; -import { Tooltip } from '@chakra-ui/tooltip'; -import { FiHome } from 'react-icons/fi'; - -export default function InfoIconBtn(props) { - const { clickhandler, ...rest } = props; - return ( - - } - colorScheme='white' - onClick={clickhandler} - _focus={{ boxShadow: 'none' }} - {...rest} - /> - - ); -} diff --git a/client/src/features/menu/buttons/UploadIconBtn.jsx b/client/src/features/menu/buttons/UploadIconBtn.jsx index 71d56e8ad..13ed4ce64 100644 --- a/client/src/features/menu/buttons/UploadIconBtn.jsx +++ b/client/src/features/menu/buttons/UploadIconBtn.jsx @@ -5,7 +5,7 @@ import { FiUpload } from 'react-icons/fi'; export default function UploadIconBtn(props) { const { clickhandler, ...rest } = props; return ( - + } diff --git a/client/src/features/modals/AliasesModal.jsx b/client/src/features/modals/AliasesModal.jsx index 9ee5d77c7..7b5e60d70 100644 --- a/client/src/features/modals/AliasesModal.jsx +++ b/client/src/features/modals/AliasesModal.jsx @@ -1,5 +1,5 @@ import { Button, IconButton } from '@chakra-ui/button'; -import { FiInfo, FiMinus, FiSun } from 'react-icons/fi'; +import { IoInformationCircleOutline, IoRemove, IoSunny } from 'react-icons/io5'; import { ModalBody } from '@chakra-ui/modal'; import { Input } from '@chakra-ui/react'; import { getAliases, postAliases } from '../../app/api/ontimeApi'; @@ -176,7 +176,7 @@ export default function AliasesModal() {
Custom Aliases
- + URL aliases are useful in two main scenarios Complicated URLs @@ -267,7 +267,7 @@ export default function AliasesModal() { } + icon={} colorScheme='blue' variant={alias.enabled ? null : 'outline'} onClick={() => setEnabled(alias.id, !alias.enabled)} @@ -276,7 +276,7 @@ export default function AliasesModal() { } + icon={} colorScheme='red' onClick={() => deleteAlias(alias.id)} /> diff --git a/client/src/features/modals/AppSettingsModal.jsx b/client/src/features/modals/AppSettingsModal.jsx index 44fd28bcf..c5ba0130f 100644 --- a/client/src/features/modals/AppSettingsModal.jsx +++ b/client/src/features/modals/AppSettingsModal.jsx @@ -99,7 +99,7 @@ export default function AppSettingsModal() {

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

diff --git a/client/src/features/modals/EventSettingsModal.jsx b/client/src/features/modals/EventSettingsModal.jsx index b96a19bb2..eec43133e 100644 --- a/client/src/features/modals/EventSettingsModal.jsx +++ b/client/src/features/modals/EventSettingsModal.jsx @@ -39,6 +39,7 @@ export default function SettingsModal() { setSubmitting(true); await postEvent(formData); + await refetch(); setChanged(false); setSubmitting(false); diff --git a/client/src/features/modals/OscSettingsModal.jsx b/client/src/features/modals/OscSettingsModal.jsx index fbfe24875..6e9f14197 100644 --- a/client/src/features/modals/OscSettingsModal.jsx +++ b/client/src/features/modals/OscSettingsModal.jsx @@ -8,7 +8,70 @@ import style from './Modals.module.scss'; import { LoggingContext } from '../../app/context/LoggingContext'; import SubmitContainer from './SubmitContainer'; import { inputProps, portInputProps } from './modalHelper'; +import { IoInformationCircleOutline } from 'react-icons/io5'; +import EnableBtn from '../../common/components/buttons/EnableBtn'; +// currently defined endpoints +// temporary +const oscCycleEndpoints = [ + { + title: 'On Event Start', + message: '/ontime/eventNumber', + value: '8 | int', + }, + { + title: 'On Update', + message: '/ontime/time', + value: '10:12:12 | string', + }, + { + title: 'On Update', + message: '/ontime/overtime', + value: '0-1 | int', + }, + { + title: 'On Update', + message: '/ontime/title', + value: 'Title of running event | string', + }, + { + title: 'On Finish', + message: '/ontime/finished', + value: '-', + }, +]; +const oscTriggerEndpoints = [ + { + title: 'On Start', + message: '/ontime/play', + value: '-', + }, + { + title: 'On Pause', + message: '/ontime/pause', + value: '-', + }, + { + title: 'On Previous', + message: '/ontime/prev', + value: '-', + }, + { + title: 'On Next', + message: '/ontime/next', + value: '-', + }, + { + title: 'On Reload', + message: '/ontime/reload', + value: '-', + }, + { + title: 'On Stop', + message: '/ontime/stop', + value: '-', + }, +]; export default function OscSettingsModal() { const { data, status, refetch } = useFetch(OSC_SETTINGS, getOSC); @@ -57,6 +120,7 @@ export default function OscSettingsModal() { } else { // Post here await postOSC(formData); + await refetch(); setChanged(false); } setSubmitting(false); @@ -91,25 +155,44 @@ export default function OscSettingsModal() {

-
OSC Input (control)
-
- - OSC In Port - -
- Open port for 3rd party control over OSC - Default 8888 -
-
- - handleChange('port', parseInt(event.target.value)) - } - style={{ width: '6em', textAlign: 'center' }} - /> +
+ OSC Input (Control ontime over OSC) +
+
+ + + OSC Enable + +
+ Enable / Disable control +
+
+ handleChange('enabled', !formData.enabled)} + onClick={() => console.log('yay')} + /> +
+ + + OSC In Port + +
+ Port - Default 8888 +
+
+ + handleChange('port', parseInt(event.target.value)) + } + style={{ width: '6em', textAlign: 'center' }} + /> +
OSC Output (feedback)
@@ -155,6 +238,48 @@ export default function OscSettingsModal() { />
+
+ + + OSC Feedback messages + + + In future OSC feedback will be user defined.
+ For now this is the list of OSC messages sent from ontime +
+ + + + + + + + {oscCycleEndpoints.map((e) => ( + + + + + + ))} + + + + + + {oscTriggerEndpoints.map((e) => ( + + + + + + ))} + +
+ Cycle + MessageValue (example | type)
{e.title}{e.message}{e.value}
+ Trigger + MessageValue (example | type)
{e.title}{e.message}{e.value}
+
- - - - - - - -
- ); -} diff --git a/client/src/features/viewers/PreviewContainer.module.css b/client/src/features/viewers/PreviewContainer.module.css deleted file mode 100644 index 5e2e6c9ec..000000000 --- a/client/src/features/viewers/PreviewContainer.module.css +++ /dev/null @@ -1,25 +0,0 @@ -.previewContainer { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - align-content: center; - gap: 1em; -} - -.previewItem { - width: 45%; -} - -.label { - padding: 0.1em 4em; -} - -a::after { - content: ' \2197'; - color: #ff7597; -} - -a:hover { - color: #ff7597; -} diff --git a/client/src/features/viewers/ViewWrapper.jsx b/client/src/features/viewers/ViewWrapper.jsx index aa7ae85f8..7f47cfd77 100644 --- a/client/src/features/viewers/ViewWrapper.jsx +++ b/client/src/features/viewers/ViewWrapper.jsx @@ -4,16 +4,12 @@ import { fetchEvent } from 'app/api/eventApi'; import { useSocket } from 'app/context/socketContext'; import { stringFromMillis } from 'ontime-utils/time'; import { useFetch } from 'app/hooks/useFetch'; -import { EVENTS_TABLE, EVENT_TABLE } from 'app/api/apiConstants'; +import { EVENT_TABLE, EVENTS_TABLE } from 'app/api/apiConstants'; const withSocket = (Component) => { - const WrappedComponent = (props) => { - const { - data: eventsData, - } = useFetch(EVENTS_TABLE, fetchAllEvents); - const { - data: genData, - } = useFetch(EVENT_TABLE, fetchEvent); + return (props) => { + const { data: eventsData } = useFetch(EVENTS_TABLE, fetchAllEvents); + const { data: genData } = useFetch(EVENT_TABLE, fetchEvent); const [publicEvents, setPublicEvents] = useState([]); const [backstageEvents, setBackstageEvents] = useState([]); @@ -70,8 +66,8 @@ const withSocket = (Component) => { useEffect(() => { if (socket == null) return; - // Handle presenter messages - socket.on('messages-presenter', (data) => { + // Handle timer messages + socket.on('messages-timer', (data) => { setPres({ ...data }); }); @@ -121,14 +117,14 @@ const withSocket = (Component) => { socket.emit('get-messages'); // Ask for up to data - socket.emit('get-presenter'); + socket.emit('get-timer'); // ask for timer socket.emit('get-timer'); // ask for playstate socket.emit('get-playstate'); - socket.emit('get-onAir') + socket.emit('get-onAir'); // Ask for up titles socket.emit('get-titles'); @@ -141,7 +137,7 @@ const withSocket = (Component) => { // Clear listeners return () => { socket.off('messages-public'); - socket.off('messages-presenter'); + socket.off('messages-timer'); socket.off('messages-lower'); socket.off('timer'); socket.off('playstate'); @@ -255,8 +251,6 @@ const withSocket = (Component) => { /> ); }; - - return WrappedComponent; }; export default withSocket; diff --git a/client/src/features/viewers/presenter/PresenterSimple.jsx b/client/src/features/viewers/presenter/PresenterSimple.jsx deleted file mode 100644 index 4520588df..000000000 --- a/client/src/features/viewers/presenter/PresenterSimple.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import style from './PresenterView.module.css'; - -export default function PresenterSimple() { - return ( -
- {/*
-
Remember to smile
-
*/} - -
-
01:03
-
- -
-
-
- - {/*
-
TIME UP
-
*/} - -
-
Time Now
-
11:00:23
-
-
- ); -} diff --git a/client/src/features/viewers/production/lower/LowerWrapper.jsx b/client/src/features/viewers/production/lower/LowerWrapper.jsx index ab22633d0..cc1e6661e 100644 --- a/client/src/features/viewers/production/lower/LowerWrapper.jsx +++ b/client/src/features/viewers/production/lower/LowerWrapper.jsx @@ -7,7 +7,7 @@ const isEqual = require('react-fast-compare'); const areEqual = (prevProps, nextProps) => { return ( isEqual(prevProps.title, nextProps.title) && - isEqual(prevProps.lower && nextProps.lower) + isEqual(prevProps.lower, nextProps.lower) ); }; @@ -56,6 +56,7 @@ const Lower = (props) => { clearTimeout(timeout); } }; + // eslint-disable-next-line }, [title.titleNow, title.subtitleNow, title.presenterNow]); // TODO: sanitize data diff --git a/client/src/features/viewers/timer/MinimalTimer.jsx b/client/src/features/viewers/timer/MinimalTimer.jsx new file mode 100644 index 000000000..30d32a4f1 --- /dev/null +++ b/client/src/features/viewers/timer/MinimalTimer.jsx @@ -0,0 +1,37 @@ +import { useEffect } from 'react'; +import { formatDisplay } from '../../../common/utils/dateConfig'; +import NavLogo from '../../../common/components/nav/NavLogo'; +import style from './MinimalTimer.module.scss'; + +export default function MinimalTimer(props) { + const { pres, time } = props; + + // Set window title + useEffect(() => { + document.title = 'ontime - Minimal Timer'; + }, []); + + const showOverlay = pres.text !== '' && pres.visible; + const isPlaying = time.playstate !== 'pause'; + const timer = formatDisplay(time.running, true); + const clean = timer.replaceAll(':', ''); + + return ( +
+
+
{pres.text}
+
+ +
+ {time.running < 0 ? `-${timer}` : timer} +
+
+ ); +} diff --git a/client/src/features/viewers/timer/MinimalTimer.module.scss b/client/src/features/viewers/timer/MinimalTimer.module.scss new file mode 100644 index 000000000..6714e521a --- /dev/null +++ b/client/src/features/viewers/timer/MinimalTimer.module.scss @@ -0,0 +1,72 @@ +@use '../../../styles/main' as *; + +.container, +.containerFinished { + background: $bg-black; + height: 100vh; + color: $title-white; + display: grid; + place-content: center; + gap: 1vw; + border: 1vw solid $bg-black; +} + +.containerFinished { + border: 1vw solid $ontime-pink-variant; + color: $ontime-pink-variant; + transition: 0.3s; +} + +.timer, +.timerPaused { + font-family: "Arial Black", sans-serif; + font-size: 20vw; + color: inherit; + opacity: 1; + transition: 0.5s; + transition-property: opacity; +} + +.timerPaused { + opacity: 0.6; + transition: 0.5s; +} + +/* =================== OVERLAY ===================*/ + +.messageOverlay, +.messageOverlayActive { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.85); + z-index: -1; + opacity: 0; + transition: 0.5s; +} + +.messageOverlayActive { + opacity: 1; + transition: 0.5s; + transition-property: opacity; + z-index: 2; +} + +.message { + width: inherit; + padding: 2vw; + position: absolute; + top: 50%; + left: 50%; + color: white; + transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + font-size: 15vw; + line-height: 30vh; + text-align: center; + font-weight: 600; +} diff --git a/client/src/features/viewers/presenter/PresenterView.jsx b/client/src/features/viewers/timer/Timer.jsx similarity index 79% rename from client/src/features/viewers/presenter/PresenterView.jsx rename to client/src/features/viewers/timer/Timer.jsx index 7e185b1e1..fbf92b04f 100644 --- a/client/src/features/viewers/presenter/PresenterView.jsx +++ b/client/src/features/viewers/timer/Timer.jsx @@ -1,19 +1,35 @@ import { AnimatePresence, motion } from 'framer-motion'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; import Countdown from 'common/components/countdown/Countdown'; import MyProgressBar from 'common/components/myProgressBar/MyProgressBar'; import NavLogo from 'common/components/nav/NavLogo'; import TitleCard from 'common/components/views/TitleCard'; -import style from './PresenterView.module.css'; +import style from './Timer.module.scss'; -export default function PresenterView(props) { +export default function Timer(props) { const { general, pres, title, time } = props; + const [elapsed, setElapsed] = useState(true); + const [searchParams] = useSearchParams(); // Set window title useEffect(() => { - document.title = 'ontime - Presenter Screen'; + document.title = 'ontime - Timer'; }, []); + // eg. http://localhost:3000/timer?progress=up + // Check for user options + useEffect(() => { + // progress: selector + // Should be 'up' or 'down' + const progress = searchParams.get('progress'); + if (progress === 'up') { + setElapsed(true); + } else if (progress === 'down') { + setElapsed(false); + } + }, [searchParams]); + const showOverlay = pres.text !== '' && pres.visible; const isPlaying = time.playstate !== 'pause'; const normalisedTime = Math.max(time.running, 0); @@ -79,7 +95,11 @@ export default function PresenterView(props) { isPlaying ? style.progressContainer : style.progressContainerPaused } > - +
)} diff --git a/client/src/features/viewers/presenter/PresenterView.module.css b/client/src/features/viewers/timer/Timer.module.scss similarity index 80% rename from client/src/features/viewers/presenter/PresenterView.module.css rename to client/src/features/viewers/timer/Timer.module.scss index 4e443e8a6..7f569ac38 100644 --- a/client/src/features/viewers/presenter/PresenterView.module.css +++ b/client/src/features/viewers/timer/Timer.module.scss @@ -1,3 +1,5 @@ +@use '../../../styles/main' as *; + .container__gray, .container__grayFinished { margin: 0; @@ -5,9 +7,9 @@ overflow: hidden; width: 100%; /* restrict the page width to viewport */ - background: radial-gradient(circle, #202020 0%, #121212 80%); + background: radial-gradient(circle, $bg-black-gradient 0%, $bg-black 80%); height: 100vh; - color: #fffd; + color: $title-white; display: grid; grid-template-columns: 1fr 1fr 5vw 1fr 1fr; grid-template-rows: auto 1fr auto minmax(25vh, auto); @@ -20,25 +22,11 @@ padding: 1vw; } -.container__graySimple, -.container__grayFinishedSimple { - background: radial-gradient(circle, #202020 0%, #121212 80%); - height: 100vh; - color: #fffd; - display: grid; - grid-template-columns: 1fr 1fr 5vw 1fr 1fr; - grid-template-rows: auto 1fr 30vh; - grid-template-areas: - ' clck .... .... .... ....' - ' timr timr timr timr timr' - ' prog prog prog prog prog'; - gap: 1vw; -} - .label { font-size: 1.3vw; - color: #ff7597; + color: $ontime-pink; } + /* =================== TITLES ===================*/ .nowContainer, @@ -52,6 +40,7 @@ .nowContainer { grid-area: now; } + .nextContainer { grid-area: next; } @@ -71,7 +60,7 @@ font-size: 12vw; line-height: 18vw; font-weight: 600; - color: #ff6969; + color: $ontime-pink-variant; padding: 0; } @@ -95,7 +84,7 @@ } .container__grayFinished { - border: 1vw solid #ff6969; + border: 1vw solid $ontime-pink-variant; } /* =================== OVERLAY ===================*/ diff --git a/client/src/styles/_main.scss b/client/src/styles/_main.scss index e8667bcd2..c4ad530e9 100644 --- a/client/src/styles/_main.scss +++ b/client/src/styles/_main.scss @@ -2,6 +2,7 @@ $ontime-accent: #4bffabcc; $ontime-pink: #ff7597; +$ontime-pink-variant: #ff6969; $ontime-roll: #2b6cb0; $notes-color: #d69e2e; @@ -16,6 +17,11 @@ $light-text: #2b6cb022; $error-red: #E53E3E; +$title-white: #fffd; +$bg-black: #121212; +$bg-black-gradient: #202020; + + //////////////////////////////////// general app element overriders // no decoration on lists @@ -34,6 +40,7 @@ a { content: ' \2197'; color: $ontime-pink; } + &:hover { color: $ontime-pink; } diff --git a/client/src/styles/_mixins.scss b/client/src/styles/_mixins.scss index 834590e6e..3a8572ee1 100644 --- a/client/src/styles/_mixins.scss +++ b/client/src/styles/_mixins.scss @@ -1,5 +1,11 @@ //////////////////////////////////// general app elements +@mixin main-container { + background-color: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.05); + border-radius: 4px; +} + @mixin container-bg { background-color: rgba(0, 0, 0, 0.13); border-radius: 2px; @@ -7,3 +13,9 @@ margin: 0 0.5em; } +@mixin osc-label { + color: #4bffabcc; + font-size: 0.8em; + -webkit-user-select: text; + user-select: text; +} diff --git a/client/yarn.lock b/client/yarn.lock index ccb9ade69..291e4695f 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -9002,6 +9002,15 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" diff --git a/server/.eslintrc b/server/.eslintrc index 3017bfbf3..77ab129a1 100644 --- a/server/.eslintrc +++ b/server/.eslintrc @@ -13,7 +13,8 @@ "rules": { "prettier/prettier": ["error", { "endOfLine": "auto", - "singleQuote": true + "singleQuote": true, + "printWidth": 100 }] } } diff --git a/server/cypress.json b/server/cypress.json new file mode 100644 index 000000000..f247843b4 --- /dev/null +++ b/server/cypress.json @@ -0,0 +1,5 @@ +{ + "viewportWidth": 1920, + "viewportHeight": 1080, + "video": false +} diff --git a/server/cypress/integration/navigation.spec.js b/server/cypress/integration/navigation.spec.js new file mode 100644 index 000000000..19bff66fb --- /dev/null +++ b/server/cypress/integration/navigation.spec.js @@ -0,0 +1,48 @@ +// go trough routes and make sure things render as expected +describe('validate routes', () => { + it('viewer routes', () => { + cy.visit('http://localhost:4001/'); + cy.contains('Time Now'); + + cy.visit('http://localhost:4001/timer'); + cy.contains('Time Now'); + + cy.visit('http://localhost:4001/presenter'); + cy.contains('Time Now'); + + cy.visit('http://localhost:4001/speaker'); + cy.contains('Time Now'); + + cy.visit('http://localhost:4001/stage'); + cy.contains('Time Now'); + + cy.visit('http://localhost:4001/backstage'); + cy.contains('Today'); + cy.contains('Time Now'); + cy.contains('Info'); + + cy.visit('http://localhost:4001/sm'); + cy.contains('Today'); + cy.contains('Time Now'); + cy.contains('Info'); + + cy.visit('http://localhost:4001/public'); + cy.contains('Today'); + cy.contains('Time Now'); + cy.contains('Info'); + + cy.visit('http://localhost:4001/pip'); + cy.contains('Today'); + cy.contains('Info'); + + cy.visit('http://localhost:4001/studio'); + cy.contains('ON AIR'); + }); + it('editor routes', () => { + cy.visit('http://localhost:4001/editor'); + cy.contains('Event List'); + cy.contains('Timer Control'); + cy.contains('Display Messages'); + cy.contains('Info'); + }); +}); diff --git a/server/cypress/plugins/index.js b/server/cypress/plugins/index.js new file mode 100644 index 000000000..59b2bab6e --- /dev/null +++ b/server/cypress/plugins/index.js @@ -0,0 +1,22 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +// eslint-disable-next-line no-unused-vars +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/server/cypress/support/commands.js b/server/cypress/support/commands.js new file mode 100644 index 000000000..119ab03f7 --- /dev/null +++ b/server/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) diff --git a/server/cypress/support/index.js b/server/cypress/support/index.js new file mode 100644 index 000000000..d68db96df --- /dev/null +++ b/server/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/server/main.js b/server/main.js index 42cb37b97..fc7f9dd76 100644 --- a/server/main.js +++ b/server/main.js @@ -23,9 +23,7 @@ const nodePath = (async () => { try { - const { startServer, startOSCServer } = await import( - nodePath - ); + const { startServer, startOSCServer } = await import(nodePath); // Start express server loaded = await startServer(); @@ -61,7 +59,6 @@ if (!lock) { 'An instance if the App is already running.' ); app.quit(); - return; } else { app.on('second-instance', (event, commandLine, workingDirectory) => { // Someone tried to run a second instance, we should focus our window. @@ -191,6 +188,15 @@ app.whenReady().then(() => { const trayContextMenu = Menu.buildFromTemplate(trayMenuTemplate); tray.setContextMenu(trayContextMenu); + + // on tray click event, show main window + tray.on('click', function (e) { + if (!win.isVisible()) { + win.show(); + } + win.focus(); + }); + }); // unregister shortcuts before quitting diff --git a/server/package.json b/server/package.json index 9206ebbf4..a39c91c4f 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "ontime", - "version": "0.5.1", + "version": "0.6.0", "author": "Carlos Valente", "description": "Time keeping for live events", "repository": "https://github.com/cpvalente/ontime", @@ -12,22 +12,30 @@ "license": "AGPL-3.0-only", "main": "main.js", "devDependencies": { + "cypress": "^9.2.1", "electron": "^13.6.1", "electron-builder": "^22.14.5", "eslint": "^8.5.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", "jest": "^27.4.5", - "prettier": "^2.5.1" + "nodemon": "^2.0.15", + "prettier": "^2.5.1", + "start-server-and-test": "^1.14.0", + "supertest": "^6.1.6" }, "scripts": { "nodestart": "NODE_ENV=development node src/app.js", + "make": "mkdir -p src/data", "setdb": "cp data/db.json src/data/db.json", "clean": "rm -rf ../client/build/ && rm -rf ../client/node_modules && rm -rf src/node_modules && rm -rf ./node_modules && rm -rf ./dist", "cheat": "rm src/node_modules/ontime-utils && cp -R utils src/node_modules/ontime-utils", "prep": "yarn clean && yarn setdb", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", "start": "NODE_ENV=development electron .", + "start:server": "nodemon --experimental-modules --es-module-specifier-resolution=node src/app.js", + "cy:run": "cypress run", + "cypress": "start-server-and-test start http://localhost:4001 cy:run", "pack": "electron-builder --dir", "dist": "electron-builder", "dist-win": "electron-builder --publish=never --x64 --win", @@ -38,7 +46,8 @@ "testEnvironment": "node", "testRunner": "jasmine2", "testPathIgnorePatterns": [ - "dist" + "dist", + "cypress" ] }, "build": { @@ -74,7 +83,9 @@ "**/*", "assets/", "!**/{yarn.lock,yarn-error.log}", - "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" + "!**/{test,tests,__test__,__tests__}", + "!**/{mock,mocks,__mock__,__mocks__}", + "!*{.spec.js,*.test.js}" ], "directories": { "buildResources": "./assets/" @@ -86,7 +97,8 @@ "filter": [ "**/*", "!**/{yarn.lock,yarn-error.log}", - "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" + "!**/{test,tests,__test__,__tests__}", + "!**/{mock,mocks,__mock__,__mocks__}" ] }, { @@ -95,7 +107,8 @@ "filter": [ "**/*", "!**/{yarn.lock,yarn-error.log}", - "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" + "!**/{test,tests,__test__,__tests__}", + "!**/{mock,mocks,__mock__,__mocks__}" ] }, { @@ -103,8 +116,11 @@ "to": "extraResources/utils", "filter": [ "**/*", + "!cypress/", "!**/{yarn.lock,yarn-error.log}", - "!**/{test,tests,__test__,__tests__,mock,mocks,__mock__,__mocks__}" + "!**/{test,tests,__test__,__tests__}", + "!**/{mock,mocks,__mock__,__mocks__}", + "!*{.spec.js,*.test.js}" ] } ] diff --git a/server/src/app.js b/server/src/app.js index 57fd001f7..c81a97142 100644 --- a/server/src/app.js +++ b/server/src/app.js @@ -123,6 +123,7 @@ 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 oscInEnabled = osc?.enabled !== undefined ? osc.enabled : config.osc.inputEnabled; const serverPort = data.settings.serverPort || config.server.port; @@ -130,14 +131,19 @@ const serverPort = data.settings.serverPort || config.server.port; import { initiateOSC, shutdownOSCServer } from './controllers/OscController.js'; export const startOSCServer = async (overrideConfig = null) => { + + if (!oscInEnabled) { + global.timer.info('RX', 'OSC Input Disabled') + return; + } + // Setup default port const oscSettings = { port: overrideConfig?.port || oscInPort, - ipOut: oscIP, - portOut: oscOutPort, }; // Start OSC Server + global.timer.info('RX', `Starting OSC Server on port: ${oscInPort}`) initiateOSC(oscSettings); }; @@ -156,7 +162,7 @@ export const startServer = async (overrideConfig = null) => { // Start server const returnMessage = `Ontime is listening on port ${port}`; - server.listen(port, '0.0.0.0', () => console.log(returnMessage)); + server.listen(port, '0.0.0.0'); // OSC Config const oscConfig = { @@ -167,7 +173,7 @@ export const startServer = async (overrideConfig = null) => { // init timer global.timer = new EventTimer(server, config.timer, oscConfig, data.http); global.timer.setupWithEventList(data.events); - + global.timer.info('SERVER', returnMessage); return returnMessage; }; @@ -185,3 +191,5 @@ export const shutdown = async () => { // shutdown timer global.timer.shutdown(); }; + +export { server, app }; \ No newline at end of file diff --git a/server/src/classes/EventTimer.js b/server/src/classes/EventTimer.js index 03485c883..4684ed7ff 100644 --- a/server/src/classes/EventTimer.js +++ b/server/src/classes/EventTimer.js @@ -306,8 +306,11 @@ export class EventTimer extends Timer { // _finish at is only set when an event is loaded if (this._finishAt > 0) { this.sendOsc(this.osc.implemented.play); + this.sendOsc( + this.osc.implemented.eventNumber, + this.selectedEventIndex || 0 + ); } - // check integrations - http if (h?.onLoad?.enabled) { if (h?.onLoad?.url != null || h?.onStart?.url !== '') { @@ -335,6 +338,10 @@ export class EventTimer extends Timer { this.osc.implemented.title, this.titles?.titleNow || '' ); + this.sendOsc( + this.osc.implemented.presenter, + this.titles?.presenterNow || '' + ); } } @@ -504,13 +511,13 @@ export class EventTimer extends Timer { switch (action) { /*******************************************/ // Presenter message - case 'set-presenter-text': + case 'set-timer-text': this.presenter.text = payload; - this.broadcastThis('messages-presenter', this.presenter); + this.broadcastThis('messages-timer', this.presenter); break; - case 'set-presenter-visible': + case 'set-timer-visible': this.presenter.visible = payload; - this.broadcastThis('messages-presenter', this.presenter); + this.broadcastThis('messages-timer', this.presenter); break; /*******************************************/ @@ -679,22 +686,22 @@ export class EventTimer extends Timer { // Messages socket.on('get-messages', () => { - this.broadcastThis('messages-presenter', this.presenter); + this.broadcastThis('messages-timer', this.presenter); this.broadcastThis('messages-public', this.public); this.broadcastThis('messages-lower', this.lower); }); // Presenter message - socket.on('set-presenter-text', (data) => { - this._setTitles('set-presenter-text', data); + socket.on('set-timer-text', (data) => { + this._setTitles('set-timer-text', data); }); - socket.on('set-presenter-visible', (data) => { - this._setTitles('set-presenter-visible', data); + socket.on('set-timer-visible', (data) => { + this._setTitles('set-timer-visible', data); }); - socket.on('get-presenter', () => { - this.broadcastThis('messages-presenter', this.presenter); + socket.on('get-timer', () => { + this.broadcastThis('messages-timer', this.presenter); }); /*******************************************/ // Public message @@ -1390,6 +1397,7 @@ export class EventTimer extends Timer { } /****************************************************************************/ + /** * Logger logic * ------------- @@ -1453,6 +1461,7 @@ export class EventTimer extends Timer { } /****************************************************************************/ + /** * Integrations * ------------- @@ -1473,4 +1482,19 @@ export class EventTimer extends Timer { this.error('TX', reply.message); } } + + /** + * Builds sync object + * @returns {{running: number, timer: (null|string|*), presenter: null, playback: string, clock: null, title: null}} + */ + poll() { + return { + clock: this.clock, + running: Timer.toSeconds(this.current), + timer: this.timeTag, + playback: this.state, + title: this.titles.titleNow, + presenter: this.titles.presenterNow, + }; + } } diff --git a/server/src/classes/__tests__/classUtils.test.js b/server/src/classes/__tests__/classUtils.test.js index f5b1f4f30..42474b7c6 100644 --- a/server/src/classes/__tests__/classUtils.test.js +++ b/server/src/classes/__tests__/classUtils.test.js @@ -4,26 +4,26 @@ import { replacePlaceholder, normaliseEndTime, sortArrayByProperty, - updateRoll + 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'); @@ -32,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'); @@ -393,78 +393,76 @@ 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" + $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) + 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) + 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) + 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) + 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) + 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) + 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) + const s = replacePlaceholder(test, values); expect(s).toBe(expected); }); }); - // test getSelectionByRoll() on issue #58 describe('test that roll behaviour multi day event edge cases', () => { - it('if the start time is the day after end time, and start time is earlier than now', () => { - const now = 66600000; // 19:30 + const now = 66600000; // 19:30 const eventlist = [ { id: 1, - timeStart: 66000000, // 19:20 - timeEnd: 54600000, // 16:10 + timeStart: 66000000, // 19:20 + timeEnd: 54600000, // 16:10 isPublic: false, - } + }, ]; const expected = { nowIndex: 0, @@ -486,14 +484,14 @@ describe('test that roll behaviour multi day event edge cases', () => { }); it('if the start time is the day after end time, and both are later than now', () => { - const now = 66840000; // 19:34 + const now = 66840000; // 19:34 const eventlist = [ { id: 1, - timeStart: 67200000, // 19:40 - timeEnd: 66900000, // 19:35 + timeStart: 67200000, // 19:40 + timeEnd: 66900000, // 19:35 isPublic: false, - } + }, ]; const expected = { nowIndex: null, @@ -512,11 +510,10 @@ describe('test that roll behaviour multi day event edge cases', () => { // test normaliseEndTime() on issue #58 test('test typical scenarios', () => { - const t1 = { start: 10, end: 20, - } + }; const t1_expected = 20; expect(normaliseEndTime(t1.start, t1.end)).toBe(t1_expected); @@ -524,7 +521,7 @@ test('test typical scenarios', () => { const t2 = { start: 10 + DAY_TO_MS, end: 20, - } + }; const t2_expected = 20 + DAY_TO_MS; expect(normaliseEndTime(t2.start, t2.end)).toBe(t2_expected); @@ -532,16 +529,14 @@ test('test typical scenarios', () => { const t3 = { start: 10, end: 10, - } + }; const t3_expected = 10; expect(normaliseEndTime(t3.start, t3.end)).toBe(t3_expected); }); - // test updateRoll() describe('typical scenarios', () => { - it('it updates running events correctly', () => { const timers = { selectedEventId: 1, diff --git a/server/src/classes/integrations/Osc.js b/server/src/classes/integrations/Osc.js index 0752700f2..9fe58c646 100644 --- a/server/src/classes/integrations/Osc.js +++ b/server/src/classes/integrations/Osc.js @@ -24,6 +24,7 @@ export class OSCIntegration { time: 'time', overtime: 'overtime', title: 'title', + eventNumber: 'eventNumber', presenter: 'presenter', }; } @@ -36,6 +37,15 @@ export class OSCIntegration { */ init(oscConfig) { const { ip, port } = oscConfig; + const validateType = typeof ip !== 'string' || typeof port !== 'number'; + const validateNull = ip == null || port == null; + + if (validateType || validateNull) { + return { + success: false, + message: `Config options incorrect`, + }; + } try { this.oscClient = new Client(ip, port); return { @@ -45,7 +55,7 @@ export class OSCIntegration { } catch (error) { this.oscClient = null; return { - success: true, + success: false, message: `Failed initialising OSC Client: ${error}`, }; } @@ -101,9 +111,24 @@ export class OSCIntegration { } break; + case 'eventNumber': + if (payload != null && payload !== '') { + // Send event number of current event + this.oscClient.send(`${this.ADDRESS}/eventNumber`, payload, (err) => { + if (err) { + reply.success = false; + reply.message = err; + } + }); + } else { + reply.success = false; + reply.message = 'Missing message data'; + } + break; + case 'presenter': if (payload != null && payload !== '') { - // Send presenter data on current event + // Send timer data on current event this.oscClient.send(`${this.ADDRESS}/presenter`, payload, (err) => { if (err) { reply.success = false; diff --git a/server/src/classes/integrations/__tests__/Osc.test.js b/server/src/classes/integrations/__tests__/Osc.test.js new file mode 100644 index 000000000..411902b9d --- /dev/null +++ b/server/src/classes/integrations/__tests__/Osc.test.js @@ -0,0 +1,162 @@ +import { OSCIntegration } from '../Osc'; +import { Server } from 'node-osc'; + +test('Class initialises correctly', () => { + const osc = new OSCIntegration(); + expect(osc.ADDRESS).toBe('/ontime'); + expect(osc.oscClient).toBe(null); + + // defined objects + expect(osc.implemented.play).toBeDefined(); + expect(osc.implemented.pause).toBeDefined(); + expect(osc.implemented.stop).toBeDefined(); + expect(osc.implemented.previous).toBeDefined(); + expect(osc.implemented.next).toBeDefined(); + expect(osc.implemented.reload).toBeDefined(); + expect(osc.implemented.finished).toBeDefined(); + expect(osc.implemented.time).toBeDefined(); + expect(osc.implemented.overtime).toBeDefined(); + expect(osc.implemented.title).toBeDefined(); + expect(osc.implemented.eventNumber).toBeDefined(); + expect(osc.implemented.presenter).toBeDefined(); + + // initialise client succeeds + const { ip, port } = { ip: '127.0.0.1', port: 12345 }; + const init = osc.init({ ip, port }); + expect(init.message).toBe(`Initialised OSC Client at ${ip}:${port}`); + expect(init.success).toBe(true); + expect(osc.oscClient).not.toBe(null); + + // object shutdown as expected + osc.shutdown(); + expect(osc.oscClient).toBe(null); +}); + +describe('OSC fails to initialise when incorrect data is given', () => { + test('IP of wrong type', () => { + const osc = new OSCIntegration(); + const init = osc.init({ ip: 123, port: 8888 }); + expect(init.message).toBe('Config options incorrect'); + expect(init.success).toBe(false); + expect(osc.oscClient).toBe(null); + }); + + test('IP is null', () => { + const osc = new OSCIntegration(); + const init = osc.init({ ip: null, port: 8888 }); + expect(init.message).toBe('Config options incorrect'); + expect(init.success).toBe(false); + expect(osc.oscClient).toBe(null); + }); + + test('Port of wrong type', () => { + const osc = new OSCIntegration(); + const init = osc.init({ ip: 'localhost', port: 'test' }); + expect(init.message).toBe('Config options incorrect'); + expect(init.success).toBe(false); + expect(osc.oscClient).toBe(null); + }); + + test('Port is null', () => { + const osc = new OSCIntegration(); + const init = osc.init({ ip: 'localhost', port: null }); + expect(init.message).toBe('Config options incorrect'); + expect(init.success).toBe(false); + expect(osc.oscClient).toBe(null); + }); +}); + +test('Test messages sending', async () => { + const testPort = 9999; + const testIP = 'localhost'; + const testPayload = 'test'; + const osc = new OSCIntegration(); + + const messages = []; + + // prepare dummy server to receive messages + const oscServer = new Server(testPort, testIP); + + oscServer.on('message', (m) => { + messages.push({ yay: m }); + }); + + // try and send a message before initialising + const test = await osc.send('test'); + expect(test.success).toBe(false); + expect(test.message).toBe('Client not initialised'); + + // initialise osc + osc.init({ ip: testIP, port: testPort }); + + // try and send unrecognised message + const test2 = await osc.send('test'); + expect(test2.success).toBe(true); + + // send play message + const playAddress = osc.implemented.play; + const playSent = await osc.send(playAddress); + expect(playSent.success).toBe(true); + + // send pause message + const pauseAddress = osc.implemented.pause; + const pauseSent = await osc.send(pauseAddress); + expect(pauseSent.success).toBe(true); + + // send stop message + const stopAddress = osc.implemented.stop; + const stopSent = await osc.send(stopAddress); + expect(stopSent.success).toBe(true); + + // send previous message + const previousAddress = osc.implemented.previous; + const previousSent = await osc.send(previousAddress); + expect(previousSent.success).toBe(true); + + // send next message + const nextAddress = osc.implemented.next; + const nextSent = await osc.send(nextAddress); + expect(nextSent.success).toBe(true); + + // send reload message + const reloadAddress = osc.implemented.reload; + const reloadSent = await osc.send(reloadAddress); + expect(reloadSent.success).toBe(true); + + // send finished message + const finishedAddress = osc.implemented.finished; + const finishedSent = await osc.send(finishedAddress); + expect(finishedSent.success).toBe(true); + + // send time message + const timeAddress = osc.implemented.time; + const timeSent = await osc.send(timeAddress); + expect(timeSent.success).toBe(true); + + // send overtime message + const overtimeAddress = osc.implemented.overtime; + const overtimeSent = await osc.send(overtimeAddress, testPayload); + expect(overtimeSent.success).toBe(true); + + // send title message + const titleAddress = osc.implemented.title; + const titleSent = await osc.send(titleAddress, testPayload); + expect(titleSent.success).toBe(true); + + // send eventNumber message + const eventNumberAddress = osc.implemented.eventNumber; + const eventNumberSent = await osc.send(eventNumberAddress, testPayload); + expect(eventNumberSent.success).toBe(true); + + // send timer message + const presenterAddress = osc.implemented.presenter; + const presenterSent = await osc.send(presenterAddress, testPayload); + expect(presenterSent.success).toBe(true); + + // cleanup + await osc.shutdown(); + await oscServer.close(); + + // see messagesObject + // expect(messages.length).toBe(5); +}); diff --git a/server/src/config/config.js b/server/src/config/config.js index 848c76e80..292e19084 100644 --- a/server/src/config/config.js +++ b/server/src/config/config.js @@ -13,7 +13,7 @@ export const config = { port: 8888, portOut: 9999, targetIP: '127.0.0.1', - enabled: true, + inputEnabled: true, }, http: { user: '', diff --git a/server/src/controllers/eventsController.js b/server/src/controllers/eventsController.js index fc8eb52d0..d5e1aec76 100644 --- a/server/src/controllers/eventsController.js +++ b/server/src/controllers/eventsController.js @@ -1,12 +1,12 @@ // get database -import { db, data } from '../app.js'; +import { data, db } from '../app.js'; // utils import { generateId } from 'ontime-utils/generate_id.js'; import { - event as eventDef, - delay as delayDef, block as blockDef, + delay as delayDef, + event as eventDef, } from '../models/eventsDefinition.js'; async function _insertAt(entry, index) { @@ -73,9 +73,14 @@ export const eventsGetAll = async (req, res) => { // Create controller for GET request to '/events/:eventId' // Returns - export const eventsGetById = async (req, res) => { - const e = data.events.find({ id: req.params.eventId }).value(); - console.log('event by id', e); - res.json(e); + const id = req.params?.eventId; + + if (id == null) { + res.status(400).send(`No eventId found in request`); + } else { + const event = data.events.find((e) => e.id === id); + res.json(event); + } }; // Create controller for POST request to '/events/' @@ -89,23 +94,24 @@ export const eventsPost = async (req, res) => { // ensure structure let newEvent = {}; - req.body.id = generateId(); + let id = req.body.id; + if (data.events.find((e) => e.id === id)) { + id = generateId(); + } switch (req.body.type) { case 'event': - newEvent = { ...eventDef, ...req.body }; + newEvent = { ...eventDef, ...req.body, id }; break; case 'delay': - newEvent = { ...delayDef, ...req.body }; + newEvent = { ...delayDef, ...req.body, id }; break; case 'block': - newEvent = { ...blockDef, ...req.body }; + newEvent = { ...blockDef, ...req.body, id }; break; default: - res - .status(400) - .send(`Object type missing or unrecognised: ${req.body.type}`); + res.status(400).send(`Object type missing or unrecognised: ${req.body.type}`); break; } diff --git a/server/src/controllers/ontimeController.js b/server/src/controllers/ontimeController.js index 65d83e551..20edeed64 100644 --- a/server/src/controllers/ontimeController.js +++ b/server/src/controllers/ontimeController.js @@ -3,7 +3,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; // get database -import { db, data } from '../app.js'; +import { data, db } from '../app.js'; import { networkInterfaces } from 'os'; import { fileHandler } from '../utils/parser.js'; import { generateId } from 'ontime-utils/generate_id.js'; @@ -15,6 +15,19 @@ function getEventTitle() { return data.event.title; } +// Create controller for GET request to '/ontime/poll' +// Returns data for current state +export const poll = async (req, res) => { + try { + const s = global.timer.poll(); + res.status(200).send(s); + } catch (error) { + res.status(500).send({ + message: `Could not get sync data: ${error}`, + }); + } +}; + // Create controller for GET request to '/ontime/db' // Returns - export const dbDownload = async (req, res) => { diff --git a/server/src/controllers/playbackController.js b/server/src/controllers/playbackController.js index 54176421d..4014c3ace 100644 --- a/server/src/controllers/playbackController.js +++ b/server/src/controllers/playbackController.js @@ -1,7 +1,7 @@ // Create controller for GET request to '/playback' // Returns ACK message export const pbGet = async (req, res) => { - res.send(global.timer.playState); + res.send({ playback: global.timer.state }); }; // Create controller for GET request to '/playback/onAir' diff --git a/server/src/routes/__tests__/eventRouter.test.js b/server/src/routes/__tests__/eventRouter.test.js new file mode 100644 index 000000000..d0191679b --- /dev/null +++ b/server/src/routes/__tests__/eventRouter.test.js @@ -0,0 +1,22 @@ +import { server, shutdown, startServer } from '../../app.js'; +import supertest from 'supertest'; + +beforeAll(() => startServer()); +afterAll(() => shutdown()); + +describe('When a GET request request is sent', () => { + test('GET /event returns a valid object', async () => { + await supertest(server) + .get('/event') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body.title).toBe('string'); + expect(typeof response.body.url).toBe('string'); + expect(typeof response.body.publicInfo).toBe('string'); + expect(typeof response.body.backstageInfo).toBe('string'); + expect(typeof response.body.endMessage).toBe('string'); + }); + }); +}); diff --git a/server/src/routes/__tests__/eventsRouter.test.js b/server/src/routes/__tests__/eventsRouter.test.js new file mode 100644 index 000000000..56f41b682 --- /dev/null +++ b/server/src/routes/__tests__/eventsRouter.test.js @@ -0,0 +1,57 @@ +import { server, shutdown, startServer } from '../../app.js'; +import supertest from 'supertest'; + +beforeAll(() => startServer()); +afterAll(() => shutdown()); + +const testEvent = { + title: 'API test event', + subtitle: 'test title', + presenter: 'test presenter', + note: 'test note', + timeStart: 0, + timeEnd: 42, + isPublic: false, + type: 'event', + id: 'superSpecial12', +}; + +describe('When a POST request is sent', () => { + test('POST /event should return a 201', async () => { + await supertest(server) + .post('/events') + .send(testEvent) + .expect(201) + }); +}); + +describe('When a GET request request is sent', () => { + test('GET /events returns a valid object', async () => { + await supertest(server) + .get('/events') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body).toBe('object'); + }); + }); + test('GET /events/:eventId returns a valid object', async () => { + await supertest(server) + .get(`/events/${testEvent.id}`) + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(response.body.title).toBe(testEvent.title); + expect(response.body.subtitle).toBe(testEvent.subtitle); + expect(response.body.presenter).toBe(testEvent.presenter); + expect(response.body.note).toBe(testEvent.note); + expect(response.body.timeStart).toBe(testEvent.timeStart); + expect(response.body.timeEnd).toBe(testEvent.timeEnd); + expect(response.body.isPublic).toBe(testEvent.isPublic); + expect(response.body.type).toBe(testEvent.type); + expect(response.body.id).toBe(testEvent.id); + }); + }); +}); diff --git a/server/src/routes/__tests__/ontimeRouter.test.js b/server/src/routes/__tests__/ontimeRouter.test.js new file mode 100644 index 000000000..ddfb99630 --- /dev/null +++ b/server/src/routes/__tests__/ontimeRouter.test.js @@ -0,0 +1,98 @@ +import { server, shutdown, startServer } from '../../app.js'; +import supertest from 'supertest'; + +beforeAll(() => startServer()); +afterAll(() => shutdown()); + +describe('When a GET request request is sent', () => { + test('GET /ontime/poll returns a valid object', async () => { + await supertest(server) + .get('/ontime/poll') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body.clock).toBe('number'); + expect(typeof response.body.running).toBe('number'); + expect(typeof response.body.timer).toBe('string'); + expect(typeof response.body.playback).toBe('string'); + expect(typeof response.body.title).toBe('string'); + expect(typeof response.body.presenter).toBe('string'); + }); + }); + + test('GET /ontime/db returns a JSON object', async () => { + await supertest(server) + .get('/ontime/db') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(response.headers['content-type']).toContain('json'); + }); + }); + + test('GET /ontime/info returns a valid object', async () => { + await supertest(server) + .get('/ontime/info') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body.networkInterfaces).toBe('object'); + expect(typeof response.body.version).toBe('number'); + expect(typeof response.body.serverPort).toBe('number'); + expect(typeof response.body.osc).toBe('object'); + }); + }); + + test('GET /ontime/aliases returns a valid object', async () => { + await supertest(server) + .get('/ontime/aliases') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body).toBe('object'); + }); + }); + + test('GET /ontime/settings returns a valid object', async () => { + await supertest(server) + .get('/ontime/settings') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body.version).toBe('number'); + expect(typeof response.body.serverPort).toBe('number'); + expect(typeof response.body.pinCode).toBeDefined(); + }); + }); + + test('GET /ontime/osc returns a valid object', async () => { + await supertest(server) + .get('/ontime/osc') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body.port).toBe('number'); + expect(typeof response.body.portOut).toBe('number'); + expect(typeof response.body.targetIP).toBe('string'); + expect(typeof response.body.enabled).toBe('boolean'); + }); + }); +}); + +describe('Any other returns the app', () => { + test('GET / returns ontime app', async () => { + await supertest(server) + .get('/') + .expect(200) + .then((response) => { + expect(response.body).toBeDefined(); + expect(response.text.includes('')).toBe(true); + }); + }); +}); diff --git a/server/src/routes/__tests__/playbackRouter.tests.js b/server/src/routes/__tests__/playbackRouter.tests.js new file mode 100644 index 000000000..d3bbae9d7 --- /dev/null +++ b/server/src/routes/__tests__/playbackRouter.tests.js @@ -0,0 +1,129 @@ +import { server, shutdown, startServer } from '../../app.js'; +import supertest from 'supertest'; + +beforeAll(() => startServer()); +afterAll(() => shutdown()); + +describe('When a GET request request is sent', () => { + test('GET /playback returns a valid object', async () => { + await supertest(server) + .get('/playback') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + expect(response.body).toBeDefined(); + expect(typeof response.body.playback).toBe('string'); + expect(response.body.playback).toBe('stop' || 'start' || 'pause' || 'roll'); + }); + }); +}); + +describe('When a GET state change is sent', () => { + test('GET /playback/onAir returns 200', async () => { + await supertest(server) + .get('/playback/onAir') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/offAir returns 200', async () => { + await supertest(server) + .get('/playback/offAir') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/start returns 200', async () => { + await supertest(server) + .get('/playback/start') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/play returns 200', async () => { + await supertest(server) + .get('/playback/play') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/pause returns 200', async () => { + await supertest(server) + .get('/playback/pause') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/stop returns 200', async () => { + await supertest(server) + .get('/playback/stop') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/roll returns 200', async () => { + await supertest(server) + .get('/playback/roll') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/previous returns 200', async () => { + await supertest(server) + .get('/playback/previous') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/next returns 200', async () => { + await supertest(server) + .get('/playback/next') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/unload returns 200', async () => { + await supertest(server) + .get('/playback/unload') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET /playback/reload returns 200', async () => { + await supertest(server) + .get('/playback/reload') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(false); + }); + }); + + test('GET of unknown request returns app', async () => { + await supertest(server) + .get('/playback/madeup') + .expect(200) + .then((response) => { + expect(response.text.includes('')).toBe(true); + }); + }); +}); diff --git a/server/src/routes/ontimeRouter.js b/server/src/routes/ontimeRouter.js index b38b94d49..6681d66c5 100644 --- a/server/src/routes/ontimeRouter.js +++ b/server/src/routes/ontimeRouter.js @@ -15,8 +15,12 @@ import { postSettings, getAliases, postAliases, + poll, } from '../controllers/ontimeController.js'; +// create route between controller and '/ontime/sync' endpoint +router.get('/poll', poll); + // create route between controller and '/ontime/db' endpoint router.get('/db', dbDownload); diff --git a/server/src/routes/playbackRouter.js b/server/src/routes/playbackRouter.js index a4167fd12..dcad1b0bd 100644 --- a/server/src/routes/playbackRouter.js +++ b/server/src/routes/playbackRouter.js @@ -13,7 +13,7 @@ import { pbPrevious, pbNext, pbUnload, - pbReload + pbReload, } from '../controllers/playbackController.js'; // create route between controller and '/playback/' endpoint diff --git a/server/src/utils/parser.js b/server/src/utils/parser.js index 69b45e6b0..158b0add9 100644 --- a/server/src/utils/parser.js +++ b/server/src/utils/parser.js @@ -1,9 +1,9 @@ import fs from 'fs'; import xlsx from 'node-xlsx'; import { - event as eventDef, - delay as delayDef, block as blockDef, + delay as delayDef, + event as eventDef, } from '../models/eventsDefinition.js'; import { dbModelv1 } from '../models/dataModel.js'; import { generateId } from 'ontime-utils/generate_id.js'; @@ -183,7 +183,6 @@ export const parseExcel_v1 = async (excelData) => { events.push({ ...event, type: 'event' }); } }); - return { events, event: eventData, @@ -429,7 +428,7 @@ export const parseOsc_v1 = (data, enforce) => { 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; + if (s.enabled !== undefined) osc.enabled = s.enabled; // write to db newOsc = { diff --git a/server/utils/__tests__/time.tests.js b/server/utils/__tests__/time.tests.js index 52bb6204d..38b5735de 100644 --- a/server/utils/__tests__/time.tests.js +++ b/server/utils/__tests__/time.tests.js @@ -58,20 +58,6 @@ describe('test string to millis 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); - }); - it('handles an invalid date string', () => { const s = 'hello'; expect(excelDateStringToMillis(s)).toBe(0); diff --git a/server/utils/time.js b/server/utils/time.js index 1a121dfee..bdb529813 100644 --- a/server/utils/time.js +++ b/server/utils/time.js @@ -57,7 +57,7 @@ export const stringFromMillis = ( export const excelDateStringToMillis = (excelDate) => { const date = new Date(excelDate); if (date instanceof Date && !isNaN(date)) { - const h = date.getUTCHours(); + const h = date.getHours(); const m = date.getMinutes(); const s = date.getSeconds(); diff --git a/server/yarn.lock b/server/yarn.lock index 8ed51c2e7..fe18a5740 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -291,6 +291,38 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cypress/request@^2.88.10": + version "2.88.10" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce" + integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + http-signature "~1.3.6" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@develar/schema-utils@~2.6.5": version "2.6.5" resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz#3ece22c5838402419a6e0425f85742b961d9b6c6" @@ -341,6 +373,18 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@hapi/hoek@^9.0.0": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@humanwhocodes/config-array@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.2.tgz#68be55c737023009dfc5fe245d51181bb6476914" @@ -557,6 +601,23 @@ lodash "^4.17.15" tmp-promise "^3.0.2" +"@sideway/address@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" + integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -684,6 +745,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.0.tgz#62797cee3b8b497f6547503b2312254d4fe3c2bb" integrity sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw== +"@types/node@^14.14.31": + version "14.18.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.5.tgz#0dd636fe7b2c6055cbed0d4ca3b7fb540f130a96" + integrity sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A== + "@types/node@^14.6.2": version "14.18.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.1.tgz#459886b51f52aa923dc06b9ea81cb8b1d733e9d3" @@ -702,6 +768,16 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" integrity sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA== +"@types/sinonjs__fake-timers@^6.0.2": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d" + integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== + +"@types/sizzle@^2.3.2": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" + integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -731,11 +807,23 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.9.1": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== + dependencies: + "@types/node" "*" + abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -771,6 +859,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-keywords@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -798,7 +894,7 @@ ansi-colors@^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-escapes@^4.2.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -829,7 +925,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^3.0.3: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -873,6 +969,11 @@ app-builder-lib@22.14.5: semver "^7.3.5" temp-file "^3.4.0" +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -897,7 +998,14 @@ asar@^3.0.3: optionalDependencies: "@types/glob" "^7.1.1" -assert-plus@^1.0.0: +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= @@ -917,6 +1025,11 @@ async@0.9.x: resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= +async@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd" + integrity sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -927,6 +1040,23 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + babel-jest@^27.4.5: version "27.4.5" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.5.tgz#d38bd0be8ea71d8b97853a5fc9f76deeb095c709" @@ -998,6 +1128,23 @@ base64-js@^1.3.1, base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + bluebird-lst@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c" @@ -1005,7 +1152,7 @@ bluebird-lst@^1.0.9: dependencies: bluebird "^3.5.5" -bluebird@^3.5.0, bluebird@^3.5.5: +bluebird@3.7.2, bluebird@^3.5.0, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -1037,7 +1184,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1150,6 +1297,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cachedir@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + +call-bind@^1.0.0: + 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" @@ -1170,6 +1330,11 @@ caniuse-lite@^1.0.30001286: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz#08a8d2cfea0b2cf2e1d94dd795942d0daef6108c" integrity sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA== +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1192,6 +1357,26 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +check-more-types@2.24.0, check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + +chokidar@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chromium-pickle-js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" @@ -1212,11 +1397,32 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-table3@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" + integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== + dependencies: + string-width "^4.2.0" + optionalDependencies: + colors "1.4.0" + cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -1275,12 +1481,22 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" + integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== + colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -combined-stream@^1.0.8: +colors@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -1294,16 +1510,26 @@ commander@2.9.0: dependencies: graceful-readlink ">= 1.0.0" -commander@^5.0.0: +commander@^5.0.0, commander@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + compare-version@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA= +component-emitter@^1.3.0: + version "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" @@ -1346,6 +1572,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookiejar@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" + integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== + 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" @@ -1363,7 +1594,7 @@ crc@^3.8.0: dependencies: buffer "^5.1.0" -cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1394,6 +1625,60 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +cypress@^9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.2.1.tgz#47f2457e5ca7ede48be9a4176f20f30ccf3b3902" + integrity sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og== + dependencies: + "@cypress/request" "^2.88.10" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^14.14.31" + "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.1" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.2" + enquirer "^2.3.6" + eventemitter2 "^6.4.3" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.0" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.5" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" + request-progress "^3.0.0" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -1403,6 +1688,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dayjs@^1.10.4: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -1410,6 +1700,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: dependencies: ms "2.1.2" +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" + debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1417,6 +1714,13 @@ debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" +debug@^3.1.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" + decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -1555,6 +1859,19 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + ejs@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" @@ -1641,7 +1958,7 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enquirer@^2.3.5: +enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -1814,7 +2131,40 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^5.0.0: +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter2@^6.4.3: + version "6.4.5" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655" + integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw== + +execa@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@5.1.1, execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -1829,6 +2179,13 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -1846,6 +2203,22 @@ expect@^27.4.2: jest-message-util "^27.4.2" jest-regex-util "^27.4.0" +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + extract-zip@^1.0.3: version "1.7.0" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" @@ -1856,6 +2229,11 @@ extract-zip@^1.0.3: mkdirp "^0.5.4" yauzl "^2.10.0" +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + extsprintf@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" @@ -1881,6 +2259,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-safe-stringify@^2.0.7: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -1895,6 +2278,13 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + 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" @@ -1937,6 +2327,16 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== +follow-redirects@^1.14.0: + version "1.14.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd" + integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -1955,6 +2355,25 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +formidable@^1.2.2: + version "1.2.6" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168" + integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ== + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + fs-extra@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" @@ -1973,7 +2392,7 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.0, fs-extra@^9.0.1: +fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -1988,7 +2407,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -2013,6 +2432,15 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2: + 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-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2025,7 +2453,7 @@ get-stream@^4.1.0: dependencies: pump "^3.0.0" -get-stream@^5.1.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== @@ -2037,6 +2465,20 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + glob-parent@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -2044,6 +2486,13 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" +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.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -2141,6 +2590,11 @@ has-flag@^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: + 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-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -2186,6 +2640,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -2194,6 +2657,11 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -2226,6 +2694,11 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -2257,6 +2730,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2280,6 +2758,13 @@ ini@^1.3.4, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -2316,14 +2801,14 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: 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-installed-globally@^0.4.0: +is-installed-globally@^0.4.0, is-installed-globally@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== @@ -2361,11 +2846,16 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-typedarray@^1.0.0: +is-typedarray@^1.0.0, 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-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -2393,6 +2883,11 @@ isexe@^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= + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -2866,6 +3361,17 @@ jest@^27.4.5: import-local "^3.0.2" jest-cli "^27.4.5" +joi@^17.4.0: + version "17.5.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.5.0.tgz#7e66d0004b5045d971cf416a55fb61d33ac6e011" + integrity sha512-R7hR50COp7StzLnDi4ywOXHrBrgNXuUUfJWIR5lPY5Bm/pOD3jZaTwpluUXVLRWcoWZxkrHBBJ5hLxgnlehbdw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.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" @@ -2886,6 +3392,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" @@ -2934,12 +3445,17 @@ 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@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + 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: +json-stringify-safe@^5.0.1, 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= @@ -2967,6 +3483,16 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -2986,6 +3512,11 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" +lazy-ass@1.6.0, lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + lazy-val@^1.0.4, lazy-val@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.5.tgz#6cf3b9f5bc31cee7ee3e369c0832b7583dcd923d" @@ -3012,6 +3543,20 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -3024,11 +3569,34 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.10, lodash@^4.17.15, lodash@^4.7.0: +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -3060,6 +3628,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + matcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" @@ -3072,6 +3645,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +methods@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" @@ -3085,14 +3663,14 @@ mime-db@1.51.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== -mime-types@^2.1.12: +mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== dependencies: mime-db "1.51.0" -mime@^2.5.2: +mime@^2.4.6, mime@^2.5.2: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -3136,6 +3714,11 @@ 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== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3156,7 +3739,30 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -normalize-path@^3.0.0: +nodemon@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e" + integrity sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.8" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + update-notifier "^5.1.0" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -3174,7 +3780,7 @@ npm-conf@^1.1.3: config-chain "^1.1.11" pify "^3.0.0" -npm-run-path@^4.0.1: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -3186,6 +3792,11 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + object-keys@^1.0.12: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -3198,7 +3809,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -3229,6 +3840,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -3248,6 +3864,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -3295,11 +3918,23 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -3310,6 +3945,16 @@ picomatch@^2.0.4, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -3362,6 +4007,11 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-format@^27.4.2: version "27.4.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.2.tgz#e4ce92ad66c3888423d332b40477c87d1dac1fb8" @@ -3395,11 +4045,28 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -psl@^1.1.33: +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + +psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -3408,6 +4075,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -3420,6 +4092,23 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" +qs@^6.9.4: + version "6.10.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.2.tgz#c1431bea37fc5b24c5bdbafa20f16bdf2a4b9ffe" + integrity sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw== + dependencies: + side-channel "^1.0.4" + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -3459,6 +4148,22 @@ readable-stream@^2.2.2: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -3478,6 +4183,13 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -3520,6 +4232,19 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -3539,12 +4264,24 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +rxjs@^7.1.0, rxjs@^7.5.1: + version "7.5.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.1.tgz#af73df343cbcab37628197f43ea0c8256f54b157" + integrity sha512-KExVEeZWxMZnZhUZtsJcFwz8IvPvgu4G2Z2QyqjZQzUGr32KDYuSxrEYO4w3tFFNbfLozcrKUTvTPi+E9ywJkQ== + dependencies: + tslib "^2.1.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + 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" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -3580,6 +4317,11 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" +semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -3611,6 +4353,15 @@ shebang-regex@^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" + signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.6" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" @@ -3635,6 +4386,15 @@ slice-ansi@^3.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.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" + smart-buffer@^4.0.2: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -3663,6 +4423,13 @@ source-map@^0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + sprintf-js@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" @@ -3673,6 +4440,21 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +sshpk@^1.14.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -3680,11 +4462,31 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +start-server-and-test@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3" + integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw== + dependencies: + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.2" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "6.0.0" + stat-mode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465" integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg== +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -3702,6 +4504,13 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -3743,7 +4552,32 @@ sumchecker@^3.0.1: dependencies: debug "^4.1.0" -supports-color@^5.3.0: +superagent@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-6.1.0.tgz#09f08807bc41108ef164cfb4be293cebd480f4a6" + integrity sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.2" + debug "^4.1.1" + fast-safe-stringify "^2.0.7" + form-data "^3.0.0" + formidable "^1.2.2" + methods "^1.1.2" + mime "^2.4.6" + qs "^6.9.4" + readable-stream "^3.6.0" + semver "^7.3.2" + +supertest@^6.1.6: + version "6.1.6" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.1.6.tgz#6151c518f4c5ced2ac2aadb9f96f1bf8198174c8" + integrity sha512-0hACYGNJ8OHRg8CRITeZOdbjur7NLuNs0mBjVhdpxi7hP6t3QIbOzLON5RTUmZcy2I9riuII3+Pr2C7yztrIIg== + dependencies: + methods "^1.1.2" + superagent "^6.1.0" + +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -3757,7 +4591,7 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -3812,6 +4646,16 @@ throat@^6.0.1: resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + +through@2, through@^2.3.8, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + tmp-promise@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" @@ -3819,7 +4663,7 @@ tmp-promise@^3.0.2: dependencies: tmp "^0.2.0" -tmp@^0.2.0: +tmp@^0.2.0, tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -3848,6 +4692,13 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -3857,6 +4708,14 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -3871,11 +4730,28 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" +tslib@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + tunnel@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + 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" @@ -3922,6 +4798,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -3939,6 +4820,11 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + update-notifier@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" @@ -3973,16 +4859,29 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + utf8-byte-length@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E= -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + 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" @@ -3997,6 +4896,15 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + verror@^1.10.0: version "1.10.1" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" @@ -4020,6 +4928,17 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +wait-on@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7" + integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw== + dependencies: + axios "^0.21.1" + joi "^17.4.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.1.0" + walker@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -4077,6 +4996,15 @@ word-wrap@^1.2.3, word-wrap@~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== +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"