Merge branch 'master' into chore/open

This commit is contained in:
Carlos Valente
2021-06-14 13:45:56 +02:00
committed by GitHub
43 changed files with 3066 additions and 261 deletions
+43 -2
View File
@@ -15,8 +15,8 @@ on:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# build a file for windows
buildwin:
# The type of runner that the job will run on
runs-on: windows-latest
env:
@@ -55,3 +55,44 @@ jobs:
files: ./server/dist/ontime-win64.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# build a file for mac
buildmac:
# The type of runner that the job will run on
runs-on: macOS-latest
env:
CI: ''
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '14.x'
# install and build react
- name: Install React dependencies
run: yarn install
working-directory: ./client
- name: Build React App
run: yarn build
working-directory: ./client
# install and build electron
- name: Install Electron + nodejs dependencies
run: yarn install
working-directory: ./server
- name: Build Electron App
run: yarn dist-win
working-directory: ./server
# release
- name: Release
uses: softprops/action-gh-release@v1
with:
files: ./server/dist/ontime-macOS.dmg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1
View File
@@ -14,6 +14,7 @@ dist/
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
+4
View File
@@ -0,0 +1,4 @@
# Table of contents
* [Initial page](README.md)
+3
View File
@@ -10,4 +10,7 @@ html,
overflow: hidden;
overflow: clip;
height: 100vh;
-webkit-user-select: none;
user-select: none;
-webkit-app-region: drag;
}
+1
View File
@@ -1,6 +1,7 @@
export const NODE_PORT = 4001;
export const EVENT_TABLE = 'event';
export const EVENTS_TABLE = 'events';
export const APP_TABLE = 'appinfo';
const calculateServer = () => {
return window.location.origin.replace(window.location.port, `${NODE_PORT}/`);
+9
View File
@@ -1,6 +1,15 @@
import axios from 'axios';
import { ontimeURL } from './apiConstants';
export const ontimePlaceholderInfo = {
networkInterfaces: [],
};
export const getInfo = async () => {
const res = await axios.get(ontimeURL + '/info');
return res.data;
};
export const downloadEvents = async () => {
await axios({
url: ontimeURL + '/db',
@@ -1,16 +1,19 @@
import { IconButton } from '@chakra-ui/button';
import { Tooltip } from '@chakra-ui/tooltip';
import { FiChevronsDown } from 'react-icons/fi';
export default function ApplyIconBtn(props) {
const { clickhandler, ...rest } = props;
return (
<IconButton
size={props.size || 'xs'}
icon={<FiChevronsDown />}
colorScheme='orange'
onClick={clickhandler}
_focus={{ boxShadow: 'none' }}
{...rest}
/>
<Tooltip label='Apply delays'>
<IconButton
size={props.size || 'xs'}
icon={<FiChevronsDown />}
colorScheme='orange'
onClick={clickhandler}
_focus={{ boxShadow: 'none' }}
{...rest}
/>
</Tooltip>
);
}
@@ -6,13 +6,13 @@ export default function LockIconBtn(props) {
const { clickhandler, active, ref } = props;
return (
<Tooltip label='Lock cursor to current'>
<IconButton
<IconButton
ref={ref}
size={props.size || 'xs'}
icon={<FiTarget />}
color={active ? 'pink.100' : 'pink.300'}
borderColor={active ? undefined : 'pink.300'}
backgroundColor={active ? 'pink.300' : undefined}
backgroundColor={active ? 'pink.400' : undefined}
variant={active ? 'solid' : 'outline'}
onClick={clickhandler}
_focus={{ boxShadow: 'none' }}
@@ -0,0 +1,22 @@
import { IconButton } from '@chakra-ui/button';
import { Tooltip } from '@chakra-ui/tooltip';
import { FiUsers } from 'react-icons/fi';
export default function PublicIconBtn(props) {
const { actionHandler, active, ...rest } = props;
return (
<Tooltip label={active ? 'Make event private' : 'Make event public'}>
<IconButton
size={props.size || 'xs'}
icon={<FiUsers />}
colorScheme='blue'
variant={active ? 'solid' : 'outline'}
onClick={() =>
actionHandler('update', { field: 'isPublic', value: !active })
}
_focus={{ boxShadow: 'none' }}
{...rest}
/>
</Tooltip>
);
}
@@ -1,11 +0,0 @@
import { Flex, Text } from '@chakra-ui/layout';
import styles from './NumberedText.module.css';
export default function NumberedText({ number = 1, text = '' }) {
return (
<Flex>
<div className={styles.stylednumber}>{number}</div>
<Text>{text}</Text>
</Flex>
);
}
@@ -1,13 +0,0 @@
.stylednumber {
display: inline;
text-align: center;
color: white;
font-weight: 600;
background-color: #ff5b7e;
border: 1px solid rgba(255,255,255,0.17);
width: 1.5em;
border-radius: 1em;
margin-right: 0.5em;
}
+2 -1
View File
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
import style from './EditableText.module.css';
export default function EditableText(props) {
const { label, defaultValue, placeholder, submitHandler } = props;
const { label, defaultValue, placeholder, submitHandler, ...rest } = props;
const [text, setText] = useState(defaultValue || '');
useEffect(() => {
@@ -30,6 +30,7 @@ export default function EditableText(props) {
value={text}
placeholder={placeholder}
className={style.inline}
{...rest}
>
<EditablePreview
color={text === '' ? '#666' : 'inherit'}
@@ -8,9 +8,12 @@
.block {
display: 'block';
overflow-x: hidden;
overflow: hidden;
}
.inline {
display: inline;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
+11 -25
View File
@@ -1,10 +1,8 @@
import { lazy, useEffect } from 'react';
import { Heading } from '@chakra-ui/layout';
import { Box } from '@chakra-ui/layout';
import NumberedText from 'common/components/text/NumberedText';
import styles from './Editor.module.css';
import { useDisclosure } from '@chakra-ui/hooks';
import SettingsModal from '../modals/SettingsModal';
import styles from './Editor.module.css';
import SettingsModal from 'features/modals/SettingsModal';
import MenuBar from 'features/menu/MenuBar';
const EventListWrapper = lazy(() =>
@@ -12,6 +10,7 @@ const EventListWrapper = lazy(() =>
);
const PlaybackControl = lazy(() => import('features/control/PlaybackControl'));
const MessageControl = lazy(() => import('features/control/MessageControl'));
const Info = lazy(() => import('features/info/Info'));
export default function Editor() {
const { isOpen, onOpen, onClose } = useDisclosure();
@@ -31,44 +30,31 @@ export default function Editor() {
</Box>
<Box className={styles.editor}>
<Heading size='lg' paddingBottom={'0.25em'}>
Event List
</Heading>
<NumberedText number={1} text={'Manage events'} />
<h1>Event List</h1>
<div className={styles.content}>
<EventListWrapper />
</div>
</Box>
<Box className={styles.messages}>
<Heading size='lg' paddingBottom={'0.25em'}>
Display Messages
</Heading>
<NumberedText
number={3}
text={'Show realtime messages on different screens'}
/>
<h1>Display Messages</h1>
<div className={styles.content}>
<MessageControl />
</div>
</Box>
<Box className={styles.playback}>
<Heading size='lg' paddingBottom={'0.25em'}>
Time Control
</Heading>
<NumberedText number={2} text={'Control timers'} />
<h1>Timer Control</h1>
<div className={styles.content}>
<PlaybackControl />
</div>
</Box>
<Box className={styles.info} borderRadius='0.5em' overflowX='auto'>
<Heading size='lg' paddingBottom={'0.25em'}>
Info
</Heading>
<NumberedText number={4} text={'Running Info'} />
<div className={styles.content}></div>
<Box className={styles.info}>
<h1>Info</h1>
<div className={styles.content}>
<Info />
</div>
</Box>
</div>
</>
@@ -69,6 +69,12 @@
}
}
h1 {
font-size: 2.5vh;
color: rgba(255, 255, 255, 0.63);
padding-bottom: 0.25em;
}
.mainContainer > div {
border-radius: 0.5em;
height: 100%;
@@ -1,6 +1,6 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu';
import { IconButton } from '@chakra-ui/button';
import { FiZap, FiPlus, FiMinusCircle, FiClock } from 'react-icons/fi';
import { FiPlus, FiMinusCircle, FiClock } from 'react-icons/fi';
export default function ActionButtons(props) {
const { showAdd, showDelay, showBlock, actionHandler } = props;
@@ -16,7 +16,7 @@ export default function ActionButtons(props) {
as={IconButton}
aria-label='Options'
size='xs'
icon={<FiZap />}
icon={<FiPlus />}
_expanded={{ bg: 'orange.300', color: 'white' }}
_focus={{ boxShadow: 'none' }}
backgroundColor={'orange.200'}
@@ -6,7 +6,7 @@ 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 VisibleIconBtn from 'common/components/buttons/VisibleIconBtn';
import PublicIconBtn from 'common/components/buttons/PublicIconBtn';
import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn';
import { millisToMinutes } from 'common/dateConfig';
import style from './EventBlock.module.css';
@@ -77,6 +77,7 @@ const ExpandedBlock = (props) => {
label='Note'
defaultValue={data.note}
placeholder='Add Note'
style={{ color: '#d69e2e' }}
submitHandler={(v) =>
actionHandler('update', { field: 'note', value: v })
}
@@ -93,7 +94,7 @@ const ExpandedBlock = (props) => {
onClick={() => props.setCollapsed(true)}
/>
<div className={style.actionOverlay}>
<VisibleIconBtn actionHandler={actionHandler} active={data.isPublic} />
<PublicIconBtn actionHandler={actionHandler} active={data.isPublic} />
<ActionButtons
showAdd
showDelay
@@ -146,7 +147,7 @@ const CollapsedBlock = (props) => {
onClick={() => props.setCollapsed(false)}
/>
<div className={style.actionOverlay}>
<VisibleIconBtn actionHandler={actionHandler} active={data.isPublic} />
<PublicIconBtn actionHandler={actionHandler} active={data.isPublic} />
<ActionButtons
showAdd
showDelay
@@ -160,10 +161,6 @@ const CollapsedBlock = (props) => {
export default function EventBlock(props) {
const { data, selected, delay, index, eventIndex, actionHandler } = props;
// const [collapsed, setCollapsed] = useState(checkLocalStorage(data.id));
// const collapsed = useSelector(itemsAtom, (state) => state === data.id);
const [collapsed] = useAtom(
useMemo(() => SelectCollapse(data.id), [data.id])
);
@@ -30,7 +30,6 @@
grid-template-columns: 2em 2em auto auto 1fr auto 3.7em;
grid-template-areas: 'drag indi time time text more btns';
justify-content: center;
align-items: baseline;
}
.expanded {
@@ -152,6 +151,8 @@
font-size: 0.8em;
float: right;
padding-right: 1em;
-webkit-user-select: text;
user-select: text;
}
/* ================ MORE ================ */
+80
View File
@@ -0,0 +1,80 @@
import { useEffect } from 'react';
import { useSocket } from 'app/context/socketContext';
import style from './Info.module.css';
import InfoTitle from './InfoTitle';
import InfoLogger from './InfoLogger';
import InfoNif from './InfoNif';
import { useState } from 'react';
export default function Info() {
const socket = useSocket();
const [titles, setTitles] = useState({
titleNow: '',
subtitleNow: '',
presenterNow: '',
noteNow: '',
titleNext: '',
subtitleNext: '',
presenterNext: '',
noteNext: '',
});
const [selected, setSelected] = useState('-/-');
const logData = [];
// handle incoming messages
useEffect(() => {
if (socket == null) return;
// Ask for titles
socket.emit('get-titles');
// Handle titles
socket.on('titles', (data) => {
setTitles(data);
});
// Ask for selection data
socket.emit('get-selected');
// Handle selection data
socket.on('selected', (data) => {
const formatedCurrent = `Event ${
data.index != null ? data.index + 1 : '-'
}/${data.total != null ? data.total : '-'}`;
setSelected(formatedCurrent);
});
// Clear listener
return () => {
socket.off('titles');
socket.off('selected');
};
}, [socket]);
// TODO: Put this in use effect
// prepare data
const titlesNow = {
title: titles.titleNow,
subtitle: titles.subtitleNow,
presenter: titles.presenterNow,
note: titles.noteNow,
};
const titlesNext = {
title: titles.titleNext,
subtitle: titles.subtitleNext,
presenter: titles.presenterNext,
note: titles.noteNext,
};
return (
<>
<div className={style.main}>{selected}</div>
{/* <InfoLogger logData={logData} /> */}
<InfoNif />
<InfoTitle title={'Now'} data={titlesNow} />
<InfoTitle title={'Next'} data={titlesNext} />
</>
);
}
+82
View File
@@ -0,0 +1,82 @@
.container {
margin-top: 1em;
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px;
padding: 8px;
}
.main {
font-size: 0.9em;
text-align: right;
color: #ff7597;
}
.header {
padding: 0;
margin: 0;
font-size: 0.9em;
color: #aaa;
display: flex;
justify-content: space-between;
}
.label {
padding: 0;
margin: 0;
font-size: 0.9em;
color: #ccc;
}
.notes {
color: #d69e2e;
}
.if {
font-size: 0.8em;
color: #4bffabcc;
background-color: rgba(0, 0, 0, 0.13);
border-radius: 2px;
padding: 0 0.5em;
margin: 0 0.5em;
}
ul > li {
font-size: 0.9em;
color: #fff;
}
.log {
overflow-y: scroll;
height: 30vh;
}
.info {
color: #fff;
}
.error {
color: red;
}
.client {
color: lightblue;
}
.moreExpanded,
.moreCollapsed {
cursor: pointer;
color: #fff;
}
.moreExpanded {
transform: scaleY(-1);
transition: transform 0.3s;
}
.moreCollapsed {
transform: scaleY(1);
transition: transform 0.3s;
}
+35
View File
@@ -0,0 +1,35 @@
import { Icon } from '@chakra-ui/react';
import { useState } from 'react';
import { FiChevronUp } from 'react-icons/fi';
import style from './Info.module.css';
export default function InfoLogger(props) {
const [collapsed, setCollapsed] = useState(false);
const { logData } = props;
return (
<div className={style.container}>
<div className={style.header}>
Log
<Icon
className={collapsed ? style.moreCollapsed : style.moreExpanded}
as={FiChevronUp}
onClick={() => setCollapsed((c) => !c)}
/>
</div>
{!collapsed && (
<ul className={style.log}>
<li className={style.info}>10:35:23 [PLAYBACK] Next</li>
<li className={style.client}>
10:32:10 [CLIENT] New socket client (total: 3)
</li>
<li className={style.info}>10:28:23 [PLAYBACK] Next</li>
<li className={style.info}>10:25:23 [PLAYBACK] Play</li>
<li className={style.info}>10:23:13 [SERVER] Server Reconnected</li>
<li className={style.error}>10:23:10 [SERVER] Server Disconnected</li>
</ul>
)}
</div>
);
}
+56
View File
@@ -0,0 +1,56 @@
import { Icon } from '@chakra-ui/react';
import { useState } from 'react';
import { FiChevronUp } from 'react-icons/fi';
import { APP_TABLE } from 'app/api/apiConstants';
import { getInfo, ontimePlaceholderInfo } from 'app/api/ontimeApi';
import { useFetch } from 'app/hooks/useFetch';
import style from './Info.module.css';
export default function InfoNif() {
const { data, status } = useFetch(APP_TABLE, getInfo, {
placeholderData: ontimePlaceholderInfo,
});
const [collapsed, setCollapsed] = useState(false);
const isDev = process.env.NODE_ENV === 'development';
const baseURL = `http://__IP__:${isDev ? 3000 : 4001}`;
const handleLink = (url) => {
if (window.process.type === 'renderer') {
window.ipcRenderer.send('send-to-link', url);
}
};
return (
<div className={style.container}>
<div className={style.header}>
Network Info
<Icon
className={collapsed ? style.moreCollapsed : style.moreExpanded}
as={FiChevronUp}
onClick={() => setCollapsed((c) => !c)}
/>
</div>
{!collapsed && (
<div>
{status === 'success' && (
<>
{data?.networkInterfaces.map((e) => {
return (
<a
href='/'
onClick={() =>
handleLink(baseURL.replace('__IP__', e.address))
}
className={style.if}
>{`${e.name} - ${e.address}`}</a>
);
})}
</>
)}
</div>
)}
</div>
);
}
+43
View File
@@ -0,0 +1,43 @@
import { Icon } from '@chakra-ui/react';
import { useState } from 'react';
import { FiChevronUp } from 'react-icons/fi';
import style from './Info.module.css';
export default function InfoTitle(props) {
const [collapsed, setCollapsed] = useState(false);
const { title, data } = props;
return (
<div className={style.container}>
<div className={style.header}>
{title}
<Icon
className={collapsed ? style.moreCollapsed : style.moreExpanded}
as={FiChevronUp}
onClick={() => setCollapsed((c) => !c)}
/>
</div>
{!collapsed && (
<>
<div>
<span className={style.label}>Title: </span>
{data.title}
</div>
<div>
<span className={style.label}>Presenter: </span>
{data.presenter}
</div>
<div>
<span className={style.label}>Subtitle: </span>
{data.subtitle}
</div>
<div className={style.notes}>
<span className={style.label}>Note: </span>
{data.note}
</div>
</>
)}
</div>
);
}
@@ -1,12 +1,6 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu';
import { IconButton } from '@chakra-ui/button';
import {
FiTrash2,
FiZap,
FiPlus,
FiClock,
FiMinusCircle,
} from 'react-icons/fi';
import { FiTrash2, FiPlus, FiClock, FiMinusCircle } from 'react-icons/fi';
import { Divider } from '@chakra-ui/layout';
export default function MenuActionButtons(props) {
@@ -20,9 +14,9 @@ export default function MenuActionButtons(props) {
<Menu isLazy lazyBehavior='unmount'>
<MenuButton
as={IconButton}
aria-label='Options'
aria-label='Create Menu'
size={props.size || 'xs'}
icon={<FiZap />}
icon={<FiPlus />}
_expanded={{ bg: 'orange.300', color: 'white' }}
_focus={{ boxShadow: 'none' }}
backgroundColor={'orange.200'}
+18 -23
View File
@@ -1,9 +1,5 @@
import { useMutation, useQueryClient } from 'react-query';
import {
downloadEvents,
uploadEvents,
uploadEventsWithPath,
} from 'app/api/ontimeApi';
import { downloadEvents, uploadEvents } from 'app/api/ontimeApi';
import { EVENTS_TABLE } from 'app/api/apiConstants';
import DownloadIconBtn from './buttons/DownloadIconBtn';
import SettingsIconBtn from './buttons/SettingsIconBtn';
@@ -16,8 +12,6 @@ import HelpIconBtn from './buttons/HelpIconBtn';
import UploadIconBtn from './buttons/UploadIconBtn';
import { useRef } from 'react';
const { ipcRenderer } = window.require('electron');
export default function MenuBar(props) {
const { onOpen } = props;
const hiddenFileInput = useRef(null);
@@ -51,21 +45,23 @@ export default function MenuBar(props) {
};
const handleIPC = (action) => {
switch (action) {
case 'min':
ipcRenderer.send('set-window', 'to-tray');
break;
case 'max':
ipcRenderer.send('set-window', 'to-max');
break;
case 'shutdown':
ipcRenderer.send('shutdown', 'now');
break;
case 'help':
ipcRenderer.send('send-to-link', 'help');
break;
default:
break;
if (window.process.type === 'renderer') {
switch (action) {
case 'min':
window.ipcRenderer.send('set-window', 'to-tray');
break;
case 'max':
window.ipcRenderer.send('set-window', 'to-max');
break;
case 'shutdown':
window.ipcRenderer.send('shutdown', 'now');
break;
case 'help':
window.ipcRenderer.send('send-to-link', 'help');
break;
default:
break;
}
}
};
@@ -87,7 +83,6 @@ export default function MenuBar(props) {
style={{ fontSize: '1.5em' }}
size='lg'
clickhandler={() => handleIPC('help')}
disabled
/>
<SettingsIconBtn style={{ fontSize: '1.5em' }} size='lg' disabled />
<div className={style.gap} />
+16
View File
@@ -0,0 +1,16 @@
import { stringFromMillis } from '../../src/utils/time';
const t1 = { val: null, result: '...' };
test('test stringFromMillis() on null values', () => {
expect(stringFromMillis(t1.val)).toBe(t1.result);
});
const t2 = { val: 3600000, result: '01:00:00' };
test('test stringFromMillis() on valid millis', () => {
expect(stringFromMillis(t2.val)).toBe(t2.result);
});
const t3 = { val: -3600000, result: '-01:00:00' };
test('test stringFromMillis() on negative millis', () => {
expect(stringFromMillis(t3.val)).toBe(t3.result);
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

+1 -1
View File
@@ -1 +1 @@
window.require = require;
window.ipcRenderer = require('electron').ipcRenderer;
+16 -4
View File
@@ -9,6 +9,10 @@
<title>ontime</title>
<style>
body {
-webkit-user-select: none;
user-select: none;
-webkit-app-region: drag;
animation: fadein 0s;
background-color: #0005;
}
h1 {
@@ -29,10 +33,10 @@
}
.lds-ellipsis div {
position: absolute;
width: 13px;
height: 13px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #fffa;
background: #ff7597aa;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
.lds-ellipsis div:nth-child(1) {
@@ -51,6 +55,14 @@
left: 56px;
animation: lds-ellipsis3 0.6s infinite;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes lds-ellipsis1 {
0% {
transform: scale(0);
@@ -79,7 +91,7 @@
</head>
<body>
<div class="container">
<img src="../../assets/images/logosbwa/LOGO-192.png" />
<img src="../../assets/logo.png" />
<h1>ontime · event timers</h1>
<div class="lds-ellipsis">
<div></div>
+6 -2
View File
@@ -33,6 +33,7 @@ const nodePath =
// Start OSC Client (Feedback)
startOSCClient();
} catch (error) {
console.log(error);
loaded = error;
}
})();
@@ -41,10 +42,12 @@ const nodePath =
// TODO: Icons appear pixelated
const trayIcon = path.join(__dirname, './assets/images/logos/LOGO-512.png');
const appIcon = path.join(__dirname, './assets/images/logos/LOGO-512.png');
function showNotification(text) {
new Notification({
title: 'ontime',
body: text,
silent: true,
}).show();
}
@@ -120,7 +123,6 @@ function createWindow() {
app.whenReady().then(() => {
createWindow();
/* ======================================
* CONTEXT MENU CREATION ON REACT SIDE
// Create context menu
@@ -253,6 +255,8 @@ ipcMain.on('send-to-link', (event, arg) => {
// send to help URL
if (arg === 'help') {
shell.openExternal('http://blank');
shell.openExternal('https://cpvalente.gitbook.io/ontime/');
} else {
shell.openExternal(arg);
}
});
+12 -8
View File
@@ -17,28 +17,31 @@
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-simple-import-sort": "^7.0.0"
"eslint-plugin-simple-import-sort": "^7.0.0",
"jest": "^27.0.4"
},
"scripts": {
"nodestart": "NODE_ENV=development node app.js",
"nodestart": "NODE_ENV=development node src/app.js",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"start": "NODE_ENV=development electron .",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"dist-win": "electron-builder --publish=never --x64 --win",
"dist-mac": "electron-builder --publish=never --x64 --mac",
"startall": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\""
"dist-all": "electron-builder -mw"
},
"build": {
"productName": "ontime",
"appId": "no.lightdev.ontime",
"asar": true,
"dmg": {
},
"artifactName": "ontime-macOS.dmg",
"icon": "icon.icns"
},
"mac": {
"target": "dmg",
"category": "no.lightdev.ontime"
"target": "dmg",
"category": "public.app-category.productivity",
"icon": "icon.icns"
},
"win": {
"target": "nsis"
@@ -48,6 +51,7 @@
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"deleteAppDataOnUninstall": true,
"runAfterFinish": false,
"installerIcon": "icon.ico"
},
"files": [
+24 -4
View File
@@ -1,4 +1,9 @@
// get config
// get environment vars
import 'dotenv/config';
import { sessionId, user } from './utils/analytics.js';
user.screenview('Node service', 'ontime').send();
user.event('NODE', 'started', 'starting node service').send();
import { config } from './config/config.js';
// init database
@@ -16,10 +21,12 @@ const adapter = new JSONFile(file);
export const db = new Low(adapter);
// dependencies
import { Client } from 'node-osc';
import express from 'express';
import http from 'http';
import cors from 'cors';
import { dbModel } from './data/dataModel.js';
import ua from 'universal-analytics';
// Read data from JSON file, this will set db.data content
await db.read();
@@ -53,11 +60,12 @@ app.use(cors());
app.options('*', cors());
// Implement middleware
app.use('/uploads', express.static('uploads'));
app.use(ua.middleware(process.env.ANALYTICS_ID, sessionId));
app.use(express.urlencoded({ extended: true }));
app.use(express.json({ limit: '1mb' }));
app.use('/uploads', express.static('uploads'));
// Implement route endpoints
app.use('/events', eventsRouter);
app.use('/event', eventRouter);
@@ -87,6 +95,10 @@ app.use((err, req, res, next) => {
res.status(500).send(err.stack);
});
// Start OSC Client
// TODO: Move this to function
const oscClient = new Client('127.0.0.1', 9999);
// create HTTP server
const server = http.createServer(app);
@@ -99,7 +111,7 @@ export const startServer = (overrideConfig = null) => {
server.listen(serverPort, '0.0.0.0', () => console.log(returnMessage));
// init timer
global.timer = new EventTimer(server, config);
global.timer = new EventTimer(server, oscClient, config);
global.timer.setupWithEventList(data.events);
return returnMessage;
@@ -123,6 +135,8 @@ export const startOSCClient = (overrideConfig = null) => {
export const shutdown = () => {
console.log('Node service shutdown');
user.event('NODE', 'shutdown', 'requesting node shutfown').send();
// shutdown express server
server.close();
// shutdown OSC Server
@@ -132,3 +146,9 @@ export const shutdown = () => {
// shutdown timer
global.timer.shutdown();
};
// if (env == 'development') {
// startServer();
// startOSCServer();
// startOSCClient();
// }
+161 -30
View File
@@ -15,6 +15,10 @@ export class EventTimer extends Timer {
// Socket IO Object
io = null;
// OSC Client
oscClient = null;
_numClients = 0;
_interval = null;
@@ -44,9 +48,11 @@ export class EventTimer extends Timer {
titleNow: null,
subtitleNow: null,
presenterNow: null,
noteNow: null,
titleNext: null,
subtitleNext: null,
presenterNext: null,
noteNext: null,
};
selectedEventIndex = null;
@@ -57,7 +63,7 @@ export class EventTimer extends Timer {
numEvents = null;
_eventlist = null;
constructor(httpServer, config) {
constructor(httpServer, oscClient, config) {
// call super constructor
super();
@@ -79,6 +85,101 @@ export class EventTimer extends Timer {
// listen to new connections
this._listenToConnections();
// set oscClient
this.updateOSCClient(oscClient);
}
/**
* @description Updates the osc client used in the object
* @param {object} oscClient
*/
updateOSCClient(oscClient) {
this.oscClient = oscClient;
}
/**
* @description Sends osc value from predefined messages
* @param {string} event - message to be sent
*/
sendOSC(event) {
if (this.oscClient == null) return;
const add = '/ontime';
const play = 'play';
const pause = 'pause';
const stop = 'stop';
const prev = 'prev';
const next = 'next';
const reload = 'reload';
const finished = 'finished';
const time = this.timeTag;
// TODO: Should this be boolean?
const overtime = this.current > 0 ? 0 : 1;
const title = this.titles.titleNow;
switch (event) {
case 'time':
// Send Timetag Message
this.oscClient.send(add + '/time', time, (err) => {
if (err) console.error(err);
});
break;
case 'finished':
// Runs when timer reaches 0
this.oscClient.send(add, finished, (err) => {
if (err) console.error(err);
});
break;
case 'overtime':
// Whether timer is negative
this.oscClient.send(add + '/overtime', overtime, (err) => {
if (err) console.error(err);
});
break;
case 'title':
// Send Title of current event
this.oscClient.send(add + '/title', title, (err) => {
if (err) console.error(err);
});
break;
case 'play':
// Play Message
this.oscClient.send(add, play, (err) => {
if (err) console.error(err);
});
break;
case 'pause':
// Pause Message
this.oscClient.send(add, pause, (err) => {
if (err) console.error(err);
});
break;
case 'stop':
// Stop Message
this.oscClient.send(add, stop, (err) => {
if (err) console.error(err);
});
break;
case 'prev':
this.oscClient.send(add, prev, (err) => {
if (err) console.error(err);
});
break;
case 'next':
this.oscClient.send(add, next, (err) => {
if (err) console.error(err);
});
break;
case 'reload':
this.oscClient.send(add, reload, (err) => {
if (err) console.error(err);
});
break;
default:
break;
}
}
/**
@@ -91,7 +192,14 @@ export class EventTimer extends Timer {
// send current timer
broadcastTimer() {
// through websockets
this.io.emit('timer', this.getObject());
// through OSC, only if running
if (this.state === 'start' || this.state === 'roll') {
this.sendOSC('time');
this.sendOSC('overtime');
}
}
// broadcast state
@@ -101,6 +209,7 @@ export class EventTimer extends Timer {
this.io.emit('selected', {
id: this.selectedEventId,
index: this.selectedEventIndex,
total: this.numEvents,
});
this.io.emit('selected-id', this.selectedEventId);
this.io.emit('next-id', this.nextEventId);
@@ -116,44 +225,32 @@ export class EventTimer extends Timer {
}
update() {
// if there is nothing selected, no nothing
// if there is nothing selected, do nothing
if (this.selectedEventId == null && this.state !== 'roll') return;
// only implement roll here
if (this.state !== 'roll') {
super.update();
return;
}
// get current time
const now = this._getCurrentTime();
this.clock = now;
if (this.selectedEventId && this.current > 0) {
// update timer as usual
this.current = this._finishAt - now;
} else {
// look for event if none is loaded
if (this.current <= 0 || this.secondaryTimer <= 0) this.rollLoad();
// get current time
const now = this._getCurrentTime();
this.clock = now;
// count to next event
// TODO: replace with proper counter
if (this.secondaryTimer != null) this.secondaryTimer -= 1000;
if (this.selectedEventId && this.current > 0) {
// update timer as usual
this.current = this._finishAt - now;
} else {
// look for event if none is loaded
if (this.current <= 0 || this.secondaryTimer <= 0) this.rollLoad();
// count to next event
// TODO: replace with proper counter
if (this.secondaryTimer != null) this.secondaryTimer -= 1000;
}
}
}
start() {
// if there is nothing selected, no nothing
if (this.selectedEventId == null) return;
super.start();
this.broadcastState();
}
pause() {
// if there is nothing selected, no nothing
if (this.selectedEventId == null) return;
super.pause();
this.broadcastState();
// sendOSC on reaching 0
if (this.current === 0 && this.state === 'start') this.sendOSC('finished');
}
_setterManager(action, payload) {
@@ -297,6 +394,7 @@ export class EventTimer extends Timer {
socket.emit('selected', {
id: this.selectedEventId,
index: this.selectedEventIndex,
total: this.numEvents,
});
});
@@ -596,6 +694,7 @@ export class EventTimer extends Timer {
this.titles.titleNow = e.title;
this.titles.subtitleNow = e.subtitle;
this.titles.presenterNow = e.presenter;
this.titles.noteNow = e.note;
this.selectedEventId = e.id;
break;
@@ -609,6 +708,7 @@ export class EventTimer extends Timer {
this.titles.titleNow = e.title;
this.titles.subtitleNow = e.subtitle;
this.titles.presenterNow = e.presenter;
this.titles.noteNow = e.note;
this.selectedEventId = e.id;
break;
@@ -624,6 +724,7 @@ export class EventTimer extends Timer {
this.titles.titleNext = e.title;
this.titles.subtitleNext = e.subtitle;
this.titles.presenterNext = e.presenter;
this.titles.noteNext = e.note;
this.nextEventId = e.id;
break;
case 'next-public':
@@ -636,6 +737,7 @@ export class EventTimer extends Timer {
this.titles.titleNext = e.title;
this.titles.subtitleNext = e.subtitle;
this.titles.presenterNext = e.presenter;
this.titles.noteNext = e.note;
this.nextEventId = e.id;
break;
@@ -652,6 +754,7 @@ export class EventTimer extends Timer {
this.titles.titleNext = null;
this.titles.subtitleNext = null;
this.titles.presenterNext = null;
this.titles.noteNext = null;
this.nextEventId = null;
this.titlesPublic.titleNext = null;
@@ -690,9 +793,11 @@ export class EventTimer extends Timer {
titleNow: null,
subtitleNow: null,
presenterNow: null,
noteNow: null,
titleNext: null,
subtitleNext: null,
presenterNext: null,
noteNext: null,
};
this.publicTitles = {
@@ -739,9 +844,11 @@ export class EventTimer extends Timer {
Title Now = ${this.titles.titleNow}
Subtitle Now = ${this.titles.subtitleNow}
Presenter Now = ${this.titles.presenterNow}
Note Now = ${this.titles.noteNow}
Title Next = ${this.titles.titleNext}
Subtitle Next = ${this.titles.subtitleNext}
Presenter Next = ${this.titles.presenterNext}
Note Next = ${this.titles.noteNext}
Public Titles
------------------------------
@@ -779,19 +886,31 @@ export class EventTimer extends Timer {
}
start() {
// if there is nothing selected, no nothing
if (this.selectedEventId == null) return;
// call super
super.start();
// broadcast current state
this.broadcastState();
// send OSC
this.sendOSC('play');
}
pause() {
// if there is nothing selected, no nothing
if (this.selectedEventId == null) return;
// call super
super.pause();
// broadcast current state
this.broadcastState();
// send OSC
this.sendOSC('pause');
}
stop() {
@@ -800,6 +919,9 @@ export class EventTimer extends Timer {
// broadcast current state
this.broadcastState();
// send OSC
this.sendOSC('stop');
}
increment(amount) {
@@ -893,6 +1015,9 @@ export class EventTimer extends Timer {
return;
}
// send OSC
this.sendOSC('prev');
// change playstate
this.pause();
@@ -913,6 +1038,9 @@ export class EventTimer extends Timer {
return;
}
// send OSC
this.sendOSC('next');
// change playstate
this.pause();
@@ -940,6 +1068,9 @@ export class EventTimer extends Timer {
// reset playstate
this.pause();
// send OSC
this.sendOSC('reload');
// reload data
this.loadEvent(this.selectedEventIndex);
}
+8 -1
View File
@@ -4,10 +4,13 @@
*
*/
import { stringFromMillis } from '../utils/time.js';
export class Timer {
clock = null;
duration = null;
current = null;
timeTag = null;
secondaryTimer = null;
_finishAt = null;
_finishedAt = null;
@@ -133,8 +136,12 @@ export class Timer {
// getObject
getObject() {
// update timer
this.update();
// update timetag
this.timeTag = stringFromMillis(this.current);
return {
clock: this.clock,
running: Timer.toSeconds(this.current),
@@ -216,4 +223,4 @@ export class Timer {
this._finishedAt = null;
}
}
}
}
+2 -1
View File
@@ -11,6 +11,7 @@ export const config = {
},
osc: {
port: 8888,
portOut: 8889,
ipOut: '127.0.0.1',
portOut: 9999,
},
};
+1 -1
View File
@@ -29,7 +29,7 @@ export const initiateOSC = (config) => {
if (address !== 'ontime') return;
// get second part (command)
switch (path.toLocaleLowerCase()) {
switch (path.toLowerCase()) {
case 'start':
case 'play':
console.log('calling play');
+41 -2
View File
@@ -10,6 +10,7 @@ import {
block as blockDef,
} from '../data/eventsDefinition.js';
import { dbModel } from '../data/dataModel.js';
import { networkInterfaces } from 'os';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -119,8 +120,14 @@ const upload = async (file, req, res) => {
deleteFile(file);
// check version
if (uploadedJson.settings.version === 1) parsev1(uploadedJson);
else {
if (uploadedJson.settings.version === 1) {
try {
parsev1(uploadedJson);
global.timer.setupWithEventList(db.data.events);
} catch (error) {
res.status(400).send({ message: `Error parsing file: ${error}` });
}
} else {
res.status(400).send({ message: 'Error parsing file, version unknown' });
return;
}
@@ -132,6 +139,38 @@ const upload = async (file, req, res) => {
}
};
/**
* @description Gets information on IPV4 non internal interfaces
* @returns {array} - Array of objects {name: ip}
*/
const getNetworkInterfaces = () => {
const nets = networkInterfaces();
const results = [];
for (const name of Object.keys(nets)) {
for (const net of nets[name]) {
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
if (net.family === 'IPv4' && !net.internal) {
results.push({
name: name,
address: net.address,
});
}
}
}
return results;
};
// Create controller for POST request to '/ontime/info'
// Returns -
// TODO: Add version
export const getInfo = async (req, res) => {
const ni = getNetworkInterfaces();
res.status(200).send({
networkInterfaces: ni,
});
};
// Create controller for POST request to '/ontime/db'
// Returns -
export const dbUpload = async (req, res) => {
+3 -1
View File
@@ -10,7 +10,9 @@
"node-osc": "6.0.2",
"passport": "~0.4.1",
"passport-local": "~1.0.0",
"socket.io": "^4.0.0"
"socket.io": "^4.0.0",
"dotenv": "^10.0.0",
"universal-analytics": "^0.4.23"
},
"devDependencies": {
"eslint": "^7.26.0",
+4
View File
@@ -5,6 +5,7 @@ export const router = express.Router();
import {
dbDownload,
dbUpload,
getInfo,
dbPathToUpload,
} from '../controllers/ontimeController.js';
@@ -14,5 +15,8 @@ router.get('/db', dbDownload);
// create route between controller and '/ontime/db' endpoint
router.post('/db', uploadJson, dbUpload);
// create route between controller and '/ontime/info' endpoint
router.get('/info', uploadJson, getInfo);
// create route between controller and '/ontime/dbpath' endpoint
router.post('/dbpath', dbPathToUpload);
+14
View File
@@ -0,0 +1,14 @@
// get environment vars
import 'dotenv/config.js';
import ua from 'universal-analytics';
import { nanoid } from 'nanoid';
export const sessionId = nanoid();
export const user = ua(process.env.ANALYTICS_ID);
// Track session
user.set('uid', sessionId);
// Allows filtering by the 'Application?' field in GA
user.set('ds', 'app');
+30
View File
@@ -0,0 +1,30 @@
/**
* @description Converts milliseconds to string representing time
* @param {number} ms - time in milliseconds
* @param {boolean} showSeconds - wether to show the seconds
* @param {string} delim - character between HH MM SS
* @param {string} ifNull - what to return if value is null
* @returns {string} String representing time 00:12:02
*/
export const stringFromMillis = (
ms,
showSeconds = true,
delim = ':',
ifNull = '...'
) => {
if (ms === null || isNaN(ms)) return ifNull;
const isNegative = ms < 0 ? '-' : '';
const millis = Math.abs(ms);
const showWith0 = (value) => (value < 10 ? `0${value}` : value);
const hours = showWith0(Math.floor(((millis / (1000 * 60 * 60)) % 60) % 24));
const minutes = showWith0(Math.floor((millis / (1000 * 60)) % 60));
const seconds = showWith0(Math.floor((millis / 1000) % 60));
return showSeconds
? `${isNegative}${
parseInt(hours) ? `${hours}${delim}` : `00${delim}`
}${minutes}${delim}${seconds}`
: `${isNegative}${parseInt(hours) ? `${hours}` : '00'}${delim}${minutes}`;
};
+274 -6
View File
@@ -96,7 +96,7 @@ acorn@^7.4.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
ajv@^6.10.0, ajv@^6.12.4:
ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -195,6 +195,18 @@ array.prototype.flatmap@^1.2.4:
es-abstract "^1.18.0-next.1"
function-bind "^1.1.1"
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
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=
ast-types-flow@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
@@ -205,6 +217,21 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
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==
axe-core@^4.0.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.2.2.tgz#0c987d82c8b82b4b9b7a945f1b5ef0d8fed586ed"
@@ -230,6 +257,13 @@ base64id@2.0.0, base64id@~2.0.0:
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
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"
binpack@~0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/binpack/-/binpack-0.1.0.tgz#bd3d0974c3f2a0446e17df4f60b55a72a205a97e"
@@ -290,6 +324,11 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -331,6 +370,13 @@ 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==
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
component-emitter@~1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
@@ -388,7 +434,7 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.14.0.tgz#72bcfacba74a65ffce04bf94ae91d966e80ee553"
integrity sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==
core-util-is@~1.0.0:
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
@@ -415,6 +461,13 @@ damerau-levenshtein@^1.0.6:
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d"
integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
debug@2.6.9, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -448,6 +501,11 @@ define-properties@^1.1.3:
dependencies:
object-keys "^1.0.12"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
@@ -485,6 +543,19 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
dotenv@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
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"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -853,6 +924,21 @@ express@~4.17.1:
utils-merge "1.0.1"
vary "~1.1.2"
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==
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.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -908,6 +994,20 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
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@~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"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -942,6 +1042,13 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
has "^1.0.3"
has-symbols "^1.0.1"
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@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
@@ -973,6 +1080,19 @@ graceful-fs@^4.1.2:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.3:
version "5.1.5"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
dependencies:
ajv "^6.12.3"
har-schema "^2.0.0"
has-bigints@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
@@ -1027,6 +1147,15 @@ http-errors@~1.7.2:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -1165,6 +1294,11 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.2"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -1180,6 +1314,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=
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -1193,6 +1332,11 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
json-parse-better-errors@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -1208,11 +1352,21 @@ json-schema-traverse@^1.0.0:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
@@ -1220,6 +1374,16 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
@@ -1322,7 +1486,7 @@ mime-db@1.48.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d"
integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==
mime-types@~2.1.24:
mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24:
version "2.1.31"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b"
integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==
@@ -1419,6 +1583,11 @@ normalize-package-data@^2.3.2:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -1606,6 +1775,11 @@ pause@0.0.1:
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=
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=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
@@ -1657,7 +1831,12 @@ proxy-addr@~2.0.5:
forwarded "0.2.0"
ipaddr.js "1.9.1"
punycode@^2.1.0:
psl@^1.1.28:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
@@ -1667,6 +1846,11 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
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==
random-bytes@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
@@ -1750,6 +1934,32 @@ regexpp@^3.1.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
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"
har-validator "~5.1.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
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 "^3.3.2"
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -1788,12 +1998,12 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@5.2.1:
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3":
"safer-buffer@>= 2.1.2 < 3", 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==
@@ -1934,6 +2144,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.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
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"
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
@@ -2053,6 +2278,14 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
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"
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
@@ -2063,6 +2296,18 @@ tsconfig-paths@^3.9.0:
minimist "^1.2.0"
strip-bom "^3.0.0"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
dependencies:
safe-buffer "^5.0.1"
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"
@@ -2105,6 +2350,15 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
universal-analytics@^0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac"
integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==
dependencies:
debug "^4.1.1"
request "^2.88.2"
uuid "^3.0.0"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -2127,6 +2381,11 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
uuid@^3.0.0, uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v8-compile-cache@^2.0.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
@@ -2145,6 +2404,15 @@ vary@^1, vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
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"
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
+2009 -95
View File
File diff suppressed because it is too large Load Diff