From 0a02ae8059dabed622f5b9144954433c26cb60cd Mon Sep 17 00:00:00 2001 From: cruizba Date: Wed, 1 Jul 2020 14:02:32 +0200 Subject: [PATCH] openvidu-server, openvidu-node-client, openvidu-java-client: Add COMPOSED_QUICK_START outputMode --- .../io/openvidu/java/client/OpenVidu.java | 3 +- .../io/openvidu/java/client/Recording.java | 11 +- .../java/client/RecordingProperties.java | 2 +- openvidu-node-client/src/OpenVidu.ts | 4 +- openvidu-node-client/src/Recording.ts | 6 +- .../openvidu-recording/scripts/composed.sh | 199 ++-- .../scripts/composed_quick_start.sh | 280 +++--- ...9.0.Dockerfile => ubuntu-16-04.Dockerfile} | 0 ...5.0.Dockerfile => ubuntu-20-04.Dockerfile} | 0 .../server/config/OpenviduConfig.java | 7 + .../openvidu/server/core/SessionManager.java | 12 +- .../kurento/core/KurentoSessionManager.java | 24 +- .../ComposedQuickStartRecordingService.java | 232 +++++ .../service/ComposedRecordingService.java | 29 +- .../recording/service/RecordingManager.java | 37 +- .../recording/service/RecordingService.java | 4 +- .../service/SingleStreamRecordingService.java | 2 +- .../server/rest/SessionRestController.java | 2 +- ...itional-spring-configuration-metadata.json | 6 + .../src/main/resources/application.properties | 1 + openvidu-testapp/package-lock.json | 940 +++++++----------- openvidu-testapp/package.json | 80 +- 22 files changed, 1036 insertions(+), 845 deletions(-) rename openvidu-server/docker/openvidu-recording/{2.9.0.Dockerfile => ubuntu-16-04.Dockerfile} (100%) rename openvidu-server/docker/openvidu-recording/{2.15.0.Dockerfile => ubuntu-20-04.Dockerfile} (100%) create mode 100644 openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java index ecc64c87..298c07ee 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java @@ -201,7 +201,8 @@ public class OpenVidu { json.addProperty("hasAudio", properties.hasAudio()); json.addProperty("hasVideo", properties.hasVideo()); - if (Recording.OutputMode.COMPOSED.equals(properties.outputMode()) && properties.hasVideo()) { + if ((Recording.OutputMode.COMPOSED.equals(properties.outputMode()) || (Recording.OutputMode.COMPOSED_QUICK_START.equals(properties.outputMode()))) + && properties.hasVideo()) { json.addProperty("resolution", properties.resolution()); json.addProperty("recordingLayout", (properties.recordingLayout() != null) ? properties.recordingLayout().name() : ""); diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java index 5db3e41a..455759b4 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java @@ -74,7 +74,14 @@ public class Recording { /** * Record each stream individually */ - INDIVIDUAL; + INDIVIDUAL, + + /** + * EXPERIMENTAL + * This option is intended to keep a recorder session openned for + * incoming recording requests to be recorded as fast as possible + */ + COMPOSED_QUICK_START; } private Recording.Status status; @@ -105,7 +112,7 @@ public class Recording { OutputMode outputMode = OutputMode.valueOf(json.get("outputMode").getAsString()); RecordingProperties.Builder builder = new RecordingProperties.Builder().name(json.get("name").getAsString()) .outputMode(outputMode).hasAudio(hasAudio).hasVideo(hasVideo); - if (OutputMode.COMPOSED.equals(outputMode) && hasVideo) { + if ((OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode)) && hasVideo) { builder.resolution(json.get("resolution").getAsString()); builder.recordingLayout(RecordingLayout.valueOf(json.get("recordingLayout").getAsString())); JsonElement customLayout = json.get("customLayout"); diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java index a264b7ed..d280b13a 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java @@ -52,7 +52,7 @@ public class RecordingProperties { * Builder for {@link io.openvidu.java.client.RecordingProperties} */ public RecordingProperties build() { - if (OutputMode.COMPOSED.equals(this.outputMode)) { + if (OutputMode.COMPOSED.equals(this.outputMode) || OutputMode.COMPOSED_QUICK_START.equals(this.outputMode)) { this.recordingLayout = this.recordingLayout != null ? this.recordingLayout : RecordingLayout.BEST_FIT; this.resolution = this.resolution != null ? this.resolution : "1920x1080"; if (RecordingLayout.CUSTOM.equals(this.recordingLayout)) { diff --git a/openvidu-node-client/src/OpenVidu.ts b/openvidu-node-client/src/OpenVidu.ts index 7e131bf2..f6d6a318 100644 --- a/openvidu-node-client/src/OpenVidu.ts +++ b/openvidu-node-client/src/OpenVidu.ts @@ -23,6 +23,7 @@ import { RecordingProperties } from './RecordingProperties'; import { Session } from './Session'; import { SessionProperties } from './SessionProperties'; import { RecordingLayout } from './RecordingLayout'; +import { RecordingMode } from 'RecordingMode'; /** * @hidden @@ -143,7 +144,8 @@ export class OpenVidu { hasAudio: !!(properties.hasAudio), hasVideo: !!(properties.hasVideo) }; - if (data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]) { + if (data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED] + || data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) { data.resolution = !!properties.resolution ? properties.resolution : '1920x1080'; data.recordingLayout = !!properties.recordingLayout ? properties.recordingLayout : RecordingLayout.BEST_FIT; if (data.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) { diff --git a/openvidu-node-client/src/Recording.ts b/openvidu-node-client/src/Recording.ts index 26a04886..ec2188d5 100644 --- a/openvidu-node-client/src/Recording.ts +++ b/openvidu-node-client/src/Recording.ts @@ -82,7 +82,8 @@ export class Recording { hasAudio: !!(json['hasAudio']), hasVideo: !!json['hasVideo'] }; - if (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]) { + if (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED] + || this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) { this.properties.resolution = !!(json['resolution']) ? json['resolution'] : '1920x1080'; this.properties.recordingLayout = !!(json['recordingLayout']) ? json['recordingLayout'] : RecordingLayout.BEST_FIT; if (this.properties.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) { @@ -140,6 +141,9 @@ export namespace Recording { * Record all streams in a grid layout in a single archive */ COMPOSED = 'COMPOSED', + + + COMPOSED_QUICK_START = 'COMPOSED_QUICK_START', /** * Record each stream individually diff --git a/openvidu-server/docker/openvidu-recording/scripts/composed.sh b/openvidu-server/docker/openvidu-recording/scripts/composed.sh index 3260d3ed..33ef270a 100644 --- a/openvidu-server/docker/openvidu-recording/scripts/composed.sh +++ b/openvidu-server/docker/openvidu-recording/scripts/composed.sh @@ -1,109 +1,130 @@ -#!/bin/bash -x +#!/bin/bash -### Variables ### +# DEBUG MODE +DEBUG_MODE=${DEBUG_MODE:-false} +if [[ ${DEBUG_MODE} == true ]]; then + DEBUG_CHROME_FLAGS="--enable-logging --v=1" +fi -URL=${URL:-https://www.youtube.com/watch?v=JMuzlEQz3uo} -ONLY_VIDEO=${ONLY_VIDEO:-false} -RESOLUTION=${RESOLUTION:-1920x1080} -FRAMERATE=${FRAMERATE:-25} -WIDTH="$(cut -d'x' -f1 <<< $RESOLUTION)" -HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)" -VIDEO_ID=${VIDEO_ID:-video} -VIDEO_NAME=${VIDEO_NAME:-video} -VIDEO_FORMAT=${VIDEO_FORMAT:-mp4} -RECORDING_JSON="${RECORDING_JSON}" +{ + ### Variables ### -export URL -export ONLY_VIDEO -export RESOLUTION -export FRAMERATE -export WIDTH -export HEIGHT -export VIDEO_ID -export VIDEO_NAME -export VIDEO_FORMAT -export RECORDING_JSON + URL=${URL:-https://www.youtube.com/watch?v=JMuzlEQz3uo} + ONLY_VIDEO=${ONLY_VIDEO:-false} + RESOLUTION=${RESOLUTION:-1920x1080} + FRAMERATE=${FRAMERATE:-25} + WIDTH="$(cut -d'x' -f1 <<< $RESOLUTION)" + HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)" + VIDEO_ID=${VIDEO_ID:-video} + VIDEO_NAME=${VIDEO_NAME:-video} + VIDEO_FORMAT=${VIDEO_FORMAT:-mp4} + RECORDING_JSON="${RECORDING_JSON}" -### Store Recording json data ### + export URL + export ONLY_VIDEO + export RESOLUTION + export FRAMERATE + export WIDTH + export HEIGHT + export VIDEO_ID + export VIDEO_NAME + export VIDEO_FORMAT + export RECORDING_JSON + + echo "==== Loaded Environment Variables =======================" + env + echo "=========================================================" -mkdir /recordings/$VIDEO_ID -chmod 777 /recordings/$VIDEO_ID -echo $RECORDING_JSON > /recordings/$VIDEO_ID/.recording.$VIDEO_ID + ### Store Recording json data ### -### Get a free display identificator ### + mkdir /recordings/$VIDEO_ID + chmod 777 /recordings/$VIDEO_ID + echo $RECORDING_JSON > /recordings/$VIDEO_ID/.recording.$VIDEO_ID -DISPLAY_NUM=99 -DONE="no" + ### Get a free display identificator ### -while [ "$DONE" == "no" ] -do - out=$(xdpyinfo -display :$DISPLAY_NUM 2>&1) - if [[ "$out" == name* ]] || [[ "$out" == Invalid* ]] - then - # Command succeeded; or failed with access error; display exists - (( DISPLAY_NUM+=1 )) - else - # Display doesn't exist - DONE="yes" + DISPLAY_NUM=99 + DONE="no" + + while [ "$DONE" == "no" ] + do + out=$(xdpyinfo -display :$DISPLAY_NUM 2>&1) + if [[ "$out" == name* ]] || [[ "$out" == Invalid* ]] + then + # Command succeeded; or failed with access error; display exists + (( DISPLAY_NUM+=1 )) + else + # Display doesn't exist + DONE="yes" + fi + done + + export DISPLAY_NUM + + echo "First available display -> :$DISPLAY_NUM" + echo "----------------------------------------" + + pulseaudio -D + + ### Start Chrome in headless mode with xvfb, using the display num previously obtained ### + + touch xvfb.log + chmod 777 xvfb.log + xvfb-run --server-num=${DISPLAY_NUM} --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --autoplay-policy=no-user-gesture-required $DEBUG_CHROME_FLAGS $URL &> xvfb.log & + touch stop + chmod 777 /recordings + sleep 2 + + ### Start recording with ffmpeg ### + + if [[ "$ONLY_VIDEO" == true ]] + then + # Do not record audio + <./stop ffmpeg -y -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" + else + # Record audio ("-f alsa -i pulse [...] -c:a aac") + <./stop ffmpeg -y -f alsa -i pulse -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:a aac -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" fi -done -export DISPLAY_NUM + ### Generate video report file ### + ffprobe -v quiet -print_format json -show_format -show_streams /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT > /recordings/$VIDEO_ID/$VIDEO_ID.info -echo "First available display -> :$DISPLAY_NUM" -echo "----------------------------------------" + ### Update Recording json data ### -pulseaudio -D + TMP=$(mktemp /recordings/$VIDEO_ID/.$VIDEO_ID.XXXXXXXXXXXXXXXXXXXXXXX.json) + INFO=$(cat /recordings/$VIDEO_ID/$VIDEO_ID.info | jq '.') + HAS_AUDIO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "audio")') + if [ -z "$HAS_AUDIO_AUX" ]; then HAS_AUDIO=false; else HAS_AUDIO=true; fi + HAS_VIDEO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "video")') + if [ -z "$HAS_VIDEO_AUX" ]; then HAS_VIDEO=false; else HAS_VIDEO=true; fi + SIZE=$(echo $INFO | jq '.format.size | tonumber') + DURATION=$(echo $INFO | jq '.format.duration | tonumber') -### Start Chrome in headless mode with xvfb, using the display num previously obtained ### + if [[ "$HAS_AUDIO" == false && "$HAS_VIDEO" == false ]] + then + STATUS="failed" + else + STATUS="stopped" + fi -touch xvfb.log -chmod 777 xvfb.log -xvfb-run --server-num=${DISPLAY_NUM} --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --autoplay-policy=no-user-gesture-required $URL &> xvfb.log & -touch stop -chmod 777 /recordings -sleep 2 + jq -c -r ".hasAudio=$HAS_AUDIO | .hasVideo=$HAS_VIDEO | .duration=$DURATION | .size=$SIZE | .status=\"$STATUS\"" "/recordings/$VIDEO_ID/.recording.$VIDEO_ID" > $TMP && mv $TMP /recordings/$VIDEO_ID/.recording.$VIDEO_ID -### Start recording with ffmpeg ### + ### Generate video thumbnail ### -if [[ "$ONLY_VIDEO" == true ]] - then - # Do not record audio - <./stop ffmpeg -y -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" - else - # Record audio ("-f alsa -i pulse [...] -c:a aac") - <./stop ffmpeg -y -f alsa -i pulse -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:a aac -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" + MIDDLE_TIME=$(ffmpeg -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F ':' '{print ($3+$2*60+$1*3600)/2}') + THUMBNAIL_HEIGHT=$((480*$HEIGHT/$WIDTH)) + ffmpeg -ss $MIDDLE_TIME -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT -vframes 1 -s 480x$THUMBNAIL_HEIGHT /recordings/$VIDEO_ID/$VIDEO_ID.jpg + + ### Change permissions to all generated files ### + + sudo chmod -R 777 /recordings/$VIDEO_ID + +} 2>&1 | tee -a /tmp/container.log + +if [[ ${DEBUG_MODE} == "true" ]]; then + [[ -f /tmp/container.log ]] && cp /tmp/container.log /recordings/$VIDEO_ID/$VIDEO_ID-container.log || echo "/tmp/container.log not found" + [[ -f ~/.config/google-chrome/chrome_debug.log ]] && cp ~/.config/google-chrome/chrome_debug.log /recordings/$VIDEO_ID/chrome_debug.log || echo "~/.config/google-chrome/chrome_debug.log" fi -### Generate video report file ### -ffprobe -v quiet -print_format json -show_format -show_streams /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT > /recordings/$VIDEO_ID/$VIDEO_ID.info - -### Update Recording json data ### - -TMP=$(mktemp /recordings/$VIDEO_ID/.$VIDEO_ID.XXXXXXXXXXXXXXXXXXXXXXX.json) -INFO=$(cat /recordings/$VIDEO_ID/$VIDEO_ID.info | jq '.') -HAS_AUDIO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "audio")') -if [ -z "$HAS_AUDIO_AUX" ]; then HAS_AUDIO=false; else HAS_AUDIO=true; fi -HAS_VIDEO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "video")') -if [ -z "$HAS_VIDEO_AUX" ]; then HAS_VIDEO=false; else HAS_VIDEO=true; fi -SIZE=$(echo $INFO | jq '.format.size | tonumber') -DURATION=$(echo $INFO | jq '.format.duration | tonumber') - -if [[ "$HAS_AUDIO" == false && "$HAS_VIDEO" == false ]] - then - STATUS="failed" - else - STATUS="stopped" -fi - -jq -c -r ".hasAudio=$HAS_AUDIO | .hasVideo=$HAS_VIDEO | .duration=$DURATION | .size=$SIZE | .status=\"$STATUS\"" "/recordings/$VIDEO_ID/.recording.$VIDEO_ID" > $TMP && mv $TMP /recordings/$VIDEO_ID/.recording.$VIDEO_ID - -### Generate video thumbnail ### - -MIDDLE_TIME=$(ffmpeg -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F ':' '{print ($3+$2*60+$1*3600)/2}') -THUMBNAIL_HEIGHT=$((480*$HEIGHT/$WIDTH)) -ffmpeg -ss $MIDDLE_TIME -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT -vframes 1 -s 480x$THUMBNAIL_HEIGHT /recordings/$VIDEO_ID/$VIDEO_ID.jpg - ### Change permissions to all generated files ### - sudo chmod -R 777 /recordings/$VIDEO_ID \ No newline at end of file diff --git a/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh b/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh index 564e5e89..5ce3344c 100644 --- a/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh +++ b/openvidu-server/docker/openvidu-recording/scripts/composed_quick_start.sh @@ -1,141 +1,183 @@ -#!/bin/bash -x - -### Global variables ### -RESOLUTION=${RESOLUTION:-1920x1080} -WIDTH="$(cut -d'x' -f1 <<< $RESOLUTION)" -HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)" -export RESOLUTION -export WIDTH -export HEIGHT +#!/bin/bash +# DEBUG MODE +# If debug mode +DEBUG_MODE=${DEBUG_MODE:-false} +if [[ ${DEBUG_MODE} == true ]]; then + DEBUG_CHROME_FLAGS="--enable-logging --v=1" +fi # QUICK_START_ACTION indicates wich action to perform when COMPOSED_QUICK_START mode is executed # Possible values are: # - Without parameters: Just execute all necessary configuration for xfvb and start chrome, waiting forever with a session openned # - --start-recording: Executes ffmpeg to record a session but don't stop chrome -# - --stop-recording: Stops ffmpeg recording -COMPOSED_QUICK_START_ACTION=$1 -export COMPOSED_QUICK_START_ACTION +# - --process-recording: Process ffmpeg video and generates a metadata +export COMPOSED_QUICK_START_ACTION=$1 if [[ -z "${COMPOSED_QUICK_START_ACTION}" ]]; then + { + ### Variables ### + export RESOLUTION=${RESOLUTION:-1920x1080} + export URL=${URL:-https://www.youtube.com/watch?v=JMuzlEQz3uo} + export VIDEO_ID=${VIDEO_ID:-video} + export WIDTH="$(cut -d'x' -f1 <<< $RESOLUTION)" + export HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)" + export RECORDING_MODE=${RECORDING_MODE} + + ### Get a free display identificator ### + + DISPLAY_NUM=99 + DONE="no" + + echo "====== Loaded Environment Variables - Start Chrome ======" + env + echo "=========================================================" + + while [ "$DONE" == "no" ] + do + out=$(xdpyinfo -display :$DISPLAY_NUM 2>&1) + if [[ "$out" == name* ]] || [[ "$out" == Invalid* ]] + then + # Command succeeded; or failed with access error; display exists + (( DISPLAY_NUM+=1 )) + else + # Display doesn't exist + DONE="yes" + fi + done + + export DISPLAY_NUM + echo "First available display -> :$DISPLAY_NUM" + echo "----------------------------------------" + + pulseaudio -D + + ### Start Chrome in headless mode with xvfb, using the display num previously obtained ### + + touch xvfb.log + chmod 777 xvfb.log + xvfb-run --server-num=${DISPLAY_NUM} --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --autoplay-policy=no-user-gesture-required --enable-logging --v=1 $DEBUG_CHROME_FLAGS $URL &> xvfb.log & + chmod 777 /recordings - ### Variables ### - URL=${URL:-https://www.youtube.com/watch?v=JMuzlEQz3uo} - export URL + # Save Global Environment variables + echo "export DISPLAY_NUM=$DISPLAY_NUM" > /tmp/display_num - ### Get a free display identificator ### - - DISPLAY_NUM=99 - DONE="no" - - while [ "$DONE" == "no" ] - do - out=$(xdpyinfo -display :$DISPLAY_NUM 2>&1) - if [[ "$out" == name* ]] || [[ "$out" == Invalid* ]] - then - # Command succeeded; or failed with access error; display exists - (( DISPLAY_NUM+=1 )) - else - # Display doesn't exist - DONE="yes" - fi - done + } 2>&1 | tee -a /tmp/container-start.log - export DISPLAY_NUM - # Save DISPLAY_NUM in a temp file to be accessible for other scripts - echo "export DISPLAY_NUM=${DISPLAY_NUM}" >> /tmp/DISPLAY_NUM - - echo "First available display -> :$DISPLAY_NUM" - echo "----------------------------------------" - - pulseaudio -D - - ### Start Chrome in headless mode with xvfb, using the display num previously obtained ### - - touch xvfb.log - chmod 777 xvfb.log - xvfb-run --server-num=${DISPLAY_NUM} --server-args="-ac -screen 0 ${RESOLUTION}x24 -noreset" google-chrome --kiosk --start-maximized --test-type --no-sandbox --disable-infobars --disable-gpu --disable-popup-blocking --window-size=$WIDTH,$HEIGHT --window-position=0,0 --no-first-run --ignore-certificate-errors --autoplay-policy=no-user-gesture-required $URL &> xvfb.log & - chmod 777 /recordings sleep infinity - + elif [[ "${COMPOSED_QUICK_START_ACTION}" == "--start-recording" ]]; then + { + export $(cat /tmp/display_num | xargs) + rm -f /tmp/global_environment_vars + + # Remove possible stop file from previous recordings + [ -e stop ] && rm stop + # Create stop file + touch stop - source /tmp/DISPLAY_NUM - - # Remove possible stop file from previous recordings - [ -e stop ] && rm stop - # Create stop file - touch stop + # Variables + export RESOLUTION=${RESOLUTION:-1920x1080} + export WIDTH="$(cut -d'x' -f1 <<< $RESOLUTION)" + export HEIGHT="$(cut -d'x' -f2 <<< $RESOLUTION)" + export ONLY_VIDEO=${ONLY_VIDEO:-false} + export FRAMERATE=${FRAMERATE:-25} + export VIDEO_ID=${VIDEO_ID:-video} + export VIDEO_NAME=${VIDEO_NAME:-video} + export VIDEO_FORMAT=${VIDEO_FORMAT:-mp4} + export RECORDING_JSON="${RECORDING_JSON}" + + echo "==== Loaded Environment Variables - Start Recording =====" + env + echo "=========================================================" + + ### Store Recording json data ### - # Variables - ONLY_VIDEO=${ONLY_VIDEO:-false} - FRAMERATE=${FRAMERATE:-25} - VIDEO_ID=${VIDEO_ID:-video} - VIDEO_NAME=${VIDEO_NAME:-video} - VIDEO_FORMAT=${VIDEO_FORMAT:-mp4} - RECORDING_JSON="${RECORDING_JSON}" - - export ONLY_VIDEO - export FRAMERATE - export VIDEO_ID - export VIDEO_NAME - export VIDEO_FORMAT - export RECORDING_JSON - - ### Store Recording json data ### + mkdir /recordings/$VIDEO_ID + echo $RECORDING_JSON > /recordings/$VIDEO_ID/.recording.$VIDEO_ID + chmod 777 -R /recordings/$VIDEO_ID + + # Save Global Environment variables + env > /tmp/global_environment_vars + + ### Start recording with ffmpeg ### - mkdir /recordings/$VIDEO_ID - chmod 777 /recordings/$VIDEO_ID - echo $RECORDING_JSON > /recordings/$VIDEO_ID/.recording.$VIDEO_ID - - ### Start recording with ffmpeg ### - - if [[ "$ONLY_VIDEO" == true ]] - then - # Do not record audio - <./stop ffmpeg -y -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" - else - # Record audio ("-f alsa -i pulse [...] -c:a aac") - <./stop ffmpeg -y -f alsa -i pulse -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:a aac -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" - fi - - ### Generate video report file ### - ffprobe -v quiet -print_format json -show_format -show_streams /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT > /recordings/$VIDEO_ID/$VIDEO_ID.info - - ### Update Recording json data ### - - TMP=$(mktemp /recordings/$VIDEO_ID/.$VIDEO_ID.XXXXXXXXXXXXXXXXXXXXXXX.json) - INFO=$(cat /recordings/$VIDEO_ID/$VIDEO_ID.info | jq '.') - HAS_AUDIO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "audio")') - if [ -z "$HAS_AUDIO_AUX" ]; then HAS_AUDIO=false; else HAS_AUDIO=true; fi - HAS_VIDEO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "video")') - if [ -z "$HAS_VIDEO_AUX" ]; then HAS_VIDEO=false; else HAS_VIDEO=true; fi - SIZE=$(echo $INFO | jq '.format.size | tonumber') - DURATION=$(echo $INFO | jq '.format.duration | tonumber') - - if [[ "$HAS_AUDIO" == false && "$HAS_VIDEO" == false ]] - then - STATUS="failed" - else - STATUS="stopped" - fi - - jq -c -r ".hasAudio=$HAS_AUDIO | .hasVideo=$HAS_VIDEO | .duration=$DURATION | .size=$SIZE | .status=\"$STATUS\"" "/recordings/$VIDEO_ID/.recording.$VIDEO_ID" > $TMP && mv $TMP /recordings/$VIDEO_ID/.recording.$VIDEO_ID - - ### Generate video thumbnail ### - - MIDDLE_TIME=$(ffmpeg -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F ':' '{print ($3+$2*60+$1*3600)/2}') - THUMBNAIL_HEIGHT=$((480*$HEIGHT/$WIDTH)) - ffmpeg -ss $MIDDLE_TIME -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT -vframes 1 -s 480x$THUMBNAIL_HEIGHT /recordings/$VIDEO_ID/$VIDEO_ID.jpg - - ### Change permissions to all generated files ### - - sudo chmod -R 777 /recordings/$VIDEO_ID + if [[ "$ONLY_VIDEO" == true ]] + then + # Do not record audio + <./stop ffmpeg -y -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" + else + # Record audio ("-f alsa -i pulse [...] -c:a aac") + <./stop ffmpeg -y -f alsa -i pulse -f x11grab -draw_mouse 0 -framerate $FRAMERATE -video_size $RESOLUTION -i :$DISPLAY_NUM -c:a aac -c:v libx264 -preset ultrafast -crf 28 -refs 4 -qmin 4 -pix_fmt yuv420p -filter:v fps=$FRAMERATE "/recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" + fi + + } 2>&1 | tee -a /tmp/container-start-recording.log elif [[ "${COMPOSED_QUICK_START_ACTION}" == "--stop-recording" ]]; then - - echo 'q' > stop + { + # Load global variables saved before + export $(cat /tmp/global_environment_vars | xargs) + + if [[ -f /recordings/$VIDEO_ID/$VIDEO_ID.jpg ]]; then + echo "Video already recorded" + exit 0 + fi + + # Stop and wait ffmpeg process to be stopped + FFMPEG_PID=$(pgrep ffmpeg) + echo 'q' > stop && tail --pid=$FFMPEG_PID -f /dev/null + + + ### Generate video report file ### + ffprobe -v quiet -print_format json -show_format -show_streams /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT > /recordings/$VIDEO_ID/$VIDEO_ID.info + + ### Change permissions to all generated files ### + sudo chmod -R 777 /recordings/$VIDEO_ID + + ### Update Recording json data ### + TMP=$(mktemp /recordings/$VIDEO_ID/.$VIDEO_ID.XXXXXXXXXXXXXXXXXXXXXXX.json) + INFO=$(cat /recordings/$VIDEO_ID/$VIDEO_ID.info | jq '.') + HAS_AUDIO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "audio")') + if [ -z "$HAS_AUDIO_AUX" ]; then HAS_AUDIO=false; else HAS_AUDIO=true; fi + HAS_VIDEO_AUX=$(echo $INFO | jq '.streams[] | select(.codec_type == "video")') + if [ -z "$HAS_VIDEO_AUX" ]; then HAS_VIDEO=false; else HAS_VIDEO=true; fi + SIZE=$(echo $INFO | jq '.format.size | tonumber') + DURATION=$(echo $INFO | jq '.format.duration | tonumber') + + if [[ "$HAS_AUDIO" == false && "$HAS_VIDEO" == false ]] + then + STATUS="failed" + else + STATUS="stopped" + fi + + jq -c -r ".hasAudio=$HAS_AUDIO | .hasVideo=$HAS_VIDEO | .duration=$DURATION | .size=$SIZE | .status=\"$STATUS\"" "/recordings/$VIDEO_ID/.recording.$VIDEO_ID" > $TMP && mv $TMP /recordings/$VIDEO_ID/.recording.$VIDEO_ID + rm -f $TMP + + ### Change permissions to metadata file ### + sudo chmod 777 /recordings/$VIDEO_ID/.recording.$VIDEO_ID + + echo "Recording finished /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT" + + ### Generate video thumbnail ### + + MIDDLE_TIME=$(ffmpeg -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F ':' '{print ($3+$2*60+$1*3600)/2}') + THUMBNAIL_HEIGHT=$((480*$HEIGHT/$WIDTH)) + ffmpeg -ss $MIDDLE_TIME -i /recordings/$VIDEO_ID/$VIDEO_NAME.$VIDEO_FORMAT -vframes 1 -s 480x$THUMBNAIL_HEIGHT /recordings/$VIDEO_ID/$VIDEO_ID.jpg &> /tmp/ffmpeg-thumbnail.log + + } 2>&1 | tee -a /tmp/container-stop-recording.log fi +if [[ ${DEBUG_MODE} == "true" ]]; then + [[ -f /tmp/container-start.log ]] && cp /tmp/container-start.log /recordings/$VIDEO_ID/$VIDEO_ID-container-start.log || echo "/tmp/container-start.log not found" + [[ -f /tmp/container-start-recording.log ]] && cp /tmp/container-start-recording.log /recordings/$VIDEO_ID/$VIDEO_ID-container-start-recording.log || echo "/tmp/container-start-recording.log not found" + [[ -f /tmp/container-stop-recording.log ]] && cp /tmp/container-stop-recording.log /recordings/$VIDEO_ID/$VIDEO_ID-container-stop-recording.log || echo "/tmp/container-stop-recording.log not found" + [[ -f ~/.config/google-chrome/chrome_debug.log ]] && cp ~/.config/google-chrome/chrome_debug.log /recordings/$VIDEO_ID/chrome_debug.log || echo "~/.config/google-chrome/chrome_debug.log" +fi + +### Change permissions to all generated files ### +sudo chmod -R 777 /recordings/$VIDEO_ID + + diff --git a/openvidu-server/docker/openvidu-recording/2.9.0.Dockerfile b/openvidu-server/docker/openvidu-recording/ubuntu-16-04.Dockerfile similarity index 100% rename from openvidu-server/docker/openvidu-recording/2.9.0.Dockerfile rename to openvidu-server/docker/openvidu-recording/ubuntu-16-04.Dockerfile diff --git a/openvidu-server/docker/openvidu-recording/2.15.0.Dockerfile b/openvidu-server/docker/openvidu-recording/ubuntu-20-04.Dockerfile similarity index 100% rename from openvidu-server/docker/openvidu-recording/2.15.0.Dockerfile rename to openvidu-server/docker/openvidu-recording/ubuntu-20-04.Dockerfile diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java index 0ae530ab..1beb6a41 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java +++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java @@ -118,6 +118,8 @@ public class OpenviduConfig { private boolean openviduRecording; + private boolean openViduRecordingDebug; + private boolean openviduRecordingPublicAccess; private Integer openviduRecordingAutostopTimeout; @@ -224,6 +226,10 @@ public class OpenviduConfig { return this.openviduRecording; } + public boolean isOpenViduRecordingDebug() { + return openViduRecordingDebug; + } + public String getOpenViduRecordingPath() { return this.openviduRecordingPath; } @@ -476,6 +482,7 @@ public class OpenviduConfig { : asFileSystemPath("OPENVIDU_CDR_PATH"); openviduRecording = asBoolean("OPENVIDU_RECORDING"); + openViduRecordingDebug = asBoolean("OPENVIDU_RECORDING_DEBUG"); openviduRecordingPath = openviduRecording ? asWritableFileSystemPath("OPENVIDU_RECORDING_PATH") : asFileSystemPath("OPENVIDU_RECORDING_PATH"); openviduRecordingPublicAccess = asBoolean("OPENVIDU_RECORDING_PUBLIC_ACCESS"); diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index 339a4ed7..6e0dedb5 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import io.openvidu.java.client.Recording; import org.apache.commons.lang3.RandomStringUtils; import org.kurento.jsonrpc.message.Request; import org.slf4j.Logger; @@ -545,10 +546,19 @@ public abstract class SessionManager { if (openviduConfig.isRecordingModuleEnabled() && stopRecording && this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) { try { - recordingManager.stopRecording(session, null, RecordingManager.finalReason(reason)); + recordingManager.stopRecording(session, null, RecordingManager.finalReason(reason), true); } catch (OpenViduException e) { log.error("Error stopping recording of session {}: {}", session.getSessionId(), e.getMessage()); } + } else if(openviduConfig.isRecordingModuleEnabled() && stopRecording + && !this.recordingManager.sessionIsBeingRecorded(session.getSessionId()) + && session.getSessionProperties().defaultOutputMode().equals(Recording.OutputMode.COMPOSED_QUICK_START) + && this.recordingManager.getStartedRecording(session.getSessionId()) != null) { + try { + this.recordingManager.stopComposedQuickStartContainer(session, reason); + } catch (OpenViduException e) { + log.error("Error stopping COMPOSED_QUICK_START container of session {}", session.getSessionId()); + } } final String mediaNodeId = session.getMediaNodeId(); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index 73426070..662b0c2f 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import io.openvidu.java.client.*; import org.apache.commons.lang3.RandomStringUtils; import org.kurento.client.GenericMediaElement; import org.kurento.client.IceCandidate; @@ -47,11 +48,6 @@ import com.google.gson.JsonObject; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; -import io.openvidu.java.client.MediaMode; -import io.openvidu.java.client.RecordingLayout; -import io.openvidu.java.client.RecordingMode; -import io.openvidu.java.client.RecordingProperties; -import io.openvidu.java.client.SessionProperties; import io.openvidu.server.core.EndReason; import io.openvidu.server.core.FinalUser; import io.openvidu.server.core.IdentifierPrefixes; @@ -144,6 +140,12 @@ public class KurentoSessionManager extends SessionManager { } } + // If Recording default layout is COMPOSED_QUICK_START + Recording.OutputMode defaultOutputMode = kSession.getSessionProperties().defaultOutputMode(); + if (defaultOutputMode.equals(Recording.OutputMode.COMPOSED_QUICK_START)) { + recordingManager.startComposedQuickStartContainer(kSession); + } + if (kSession.isClosed()) { log.warn("'{}' is trying to join session '{}' but it is closing", participant.getParticipantPublicId(), sessionId); @@ -214,12 +216,12 @@ public class KurentoSessionManager extends SessionManager { Participant p = sessionidParticipantpublicidParticipant.get(sessionId) .remove(participant.getParticipantPublicId()); - if (this.openviduConfig.isTurnadminAvailable()) { + if (p != null && this.openviduConfig.isTurnadminAvailable()) { this.coturnCredentialsService.deleteUser(p.getToken().getTurnCredentials().getUsername()); } // TODO: why is this necessary?? - if (insecureUsers.containsKey(p.getParticipantPrivateId())) { + if (p != null && insecureUsers.containsKey(p.getParticipantPrivateId())) { boolean stillParticipant = false; for (Session s : sessions.values()) { if (!s.isClosed() @@ -295,6 +297,14 @@ public class KurentoSessionManager extends SessionManager { "Last participant left. Starting {} seconds countdown for stopping recording of session {}", this.openviduConfig.getOpenviduRecordingAutostopTimeout(), sessionId); recordingManager.initAutomaticRecordingStopThread(session); + + } else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled() + && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) + && session.getSessionProperties().defaultOutputMode().equals(Recording.OutputMode.COMPOSED_QUICK_START) + && ProtocolElements.RECORDER_PARTICIPANT_PUBLICID + .equals(remainingParticipants.iterator().next().getParticipantPublicId())) { + // If no recordings are active in COMPOSED_QUICK_START output mode, stop container + recordingManager.stopComposedQuickStartContainer(session, reason); } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java new file mode 100644 index 00000000..56ccd83a --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java @@ -0,0 +1,232 @@ +package io.openvidu.server.recording.service; + +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Volume; +import io.openvidu.client.OpenViduException; +import io.openvidu.java.client.RecordingProperties; +import io.openvidu.server.cdr.CallDetailRecord; +import io.openvidu.server.config.OpenviduConfig; +import io.openvidu.server.core.EndReason; +import io.openvidu.server.core.Session; +import io.openvidu.server.recording.Recording; +import io.openvidu.server.recording.RecordingDownloader; +import io.openvidu.server.utils.QuarantineKiller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class ComposedQuickStartRecordingService extends ComposedRecordingService { + + private static final Logger log = LoggerFactory.getLogger(ComposedRecordingService.class); + + public ComposedQuickStartRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader, OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) { + super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller); + } + + public void stopRecordingContainer(Session session, EndReason reason) { + log.info("Stopping COMPOSED_QUICK_START of session {}. Reason: {}", + session.getSessionId(), RecordingManager.finalReason(reason)); + + String containerId = this.sessionsContainers.get(session.getSessionId()); + + try { + dockerManager.removeDockerContainer(containerId, true); + } catch (Exception e) { + log.error("Can't remove COMPOSED_QUICK_START recording container from session {}", session.getSessionId()); + } + + containers.remove(containerId); + sessionsContainers.remove(session.getSessionId()); + } + + @Override + protected Recording startRecordingWithVideo(Session session, Recording recording, RecordingProperties properties) + throws OpenViduException { + + log.info("Starting COMPOSED_QUICK_START ({}) recording {} of session {}", + properties.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId()); + + List envs = new ArrayList<>(); + + envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug()); + envs.add("RESOLUTION=" + properties.resolution()); + envs.add("ONLY_VIDEO=" + !properties.hasAudio()); + envs.add("FRAMERATE=30"); + envs.add("VIDEO_ID=" + recording.getId()); + envs.add("VIDEO_NAME=" + properties.name()); + envs.add("VIDEO_FORMAT=mp4"); + envs.add("RECORDING_JSON='" + recording.toJson().toString() + "'"); + + String containerId = this.sessionsContainers.get(session.getSessionId()); + try { + String recordExecCommand = ""; + for(int i = 0; i < envs.size(); i++) { + if (i > 0) { + recordExecCommand += "&& "; + } + recordExecCommand += "export " + envs.get(i) + " "; + } + recordExecCommand += "&& ./composed_quick_start.sh --start-recording > /var/log/ffmpeg.log 2>&1 &"; + dockerManager.runCommandInContainer(containerId, recordExecCommand, 0); + } catch (Exception e) { + this.cleanRecordingMaps(recording); + throw this.failStartRecording(session, recording, + "Couldn't initialize recording container. Error: " + e.getMessage()); + } + + this.sessionsContainers.put(session.getSessionId(), containerId); + + try { + this.waitForVideoFileNotEmpty(recording); + } catch (OpenViduException e) { + this.cleanRecordingMaps(recording); + throw this.failStartRecording(session, recording, + "Couldn't initialize recording container. Error: " + e.getMessage()); + } + + return recording; + } + + @Override + protected Recording stopRecordingWithVideo(Session session, Recording recording, EndReason reason, boolean hasSessionEnded) { + log.info("Stopping COMPOSED_QUICK_START ({}) recording {} of session {}. Reason: {}", + recording.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId(), + RecordingManager.finalReason(reason)); + log.info("Container for session {} still being ready for new recordings", session.getSessionId()); + + String containerId = this.sessionsContainers.get(recording.getSessionId()); + + if (session == null) { + log.warn( + "Existing recording {} does not have an active session associated. This usually means a custom recording" + + " layout did not join a recorded participant or the recording has been automatically" + + " stopped after last user left and timeout passed", + recording.getId()); + } + + if (hasSessionEnded) { + // Gracefully stop ffmpeg process + try { + dockerManager.runCommandInContainer(containerId, "./composed_quick_start.sh --stop-recording", 10); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + + try { + dockerManager.removeDockerContainer(containerId, true); + } catch (Exception e) { + failRecordingCompletion(recording, containerId, new OpenViduException(OpenViduException.Code.RECORDING_COMPLETION_ERROR_CODE, + "Can't remove COMPOSED_QUICK_START recording container from session" + session.getSessionId())); + } + + containers.remove(containerId); + sessionsContainers.remove(recording.getSessionId()); + } else { + try { + dockerManager.runCommandInContainer(containerId, "./composed_quick_start.sh --stop-recording", 10); + } catch (InterruptedException e1) { + cleanRecordingMaps(recording); + log.error("Error stopping recording for session id: {}", session.getSessionId()); + e1.printStackTrace(); + } + } + + recording = updateRecordingAttributes(recording); + + final String folderPath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"; + final String metadataFilePath = folderPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId(); + this.sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(), + metadataFilePath); + cleanRecordingMaps(recording); + + final long timestamp = System.currentTimeMillis(); + this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus()); + + if (session != null && reason != null) { + this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason); + } + + // Decrement active recordings + // ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet(); + + return recording; + } + + public void runComposedQuickStartContainer(Session session) { + // Start recording container if output mode=COMPOSED_QUICK_START + Session recorderSession = session; + io.openvidu.java.client.Recording.OutputMode defaultOutputMode = recorderSession.getSessionProperties().defaultOutputMode(); + if (io.openvidu.java.client.Recording.OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode) + && sessionsContainers.get(recorderSession.getSessionId()) == null) { + // Retry to run if container is launched for the same session quickly after close it + int secondsToRetry = 10; + int secondsBetweenRetries = 1; + int seconds = 0; + boolean launched = false; + while (!launched && seconds < secondsToRetry) { + try { + log.info("Launching COMPOSED_QUICK_START recording container for session: {}", recorderSession.getSessionId()); + runContainer(recorderSession, new RecordingProperties.Builder().name("") + .outputMode(recorderSession.getSessionProperties().defaultOutputMode()) + .recordingLayout(recorderSession.getSessionProperties().defaultRecordingLayout()) + .customLayout(recorderSession.getSessionProperties().defaultCustomLayout()).build()); + log.info("COMPOSED_QUICK_START recording container launched for session: {}", recorderSession.getSessionId()); + launched = true; + } catch (Exception e) { + log.warn("Failed to launch COMPOSED_QUICK_START recording container for session {}. Trying again in {} seconds", recorderSession.getSessionId(), secondsBetweenRetries); + try { + Thread.sleep(secondsBetweenRetries * 1000); + } catch (InterruptedException e2) {} + seconds++; + } finally { + if (seconds == secondsToRetry && !launched) { + log.error("Error launchaing COMPOSED_QUICK_ªSTART recording container for session {}", recorderSession.getSessionId()); + } + } + } + } + } + + private String runContainer(Session session, RecordingProperties properties) throws Exception { + log.info("Starting COMPOSED_QUICK_START container for session id: {}", session.getSessionId()); + + Recording recording = new Recording(session.getSessionId(), null, properties); + String layoutUrl = this.getLayoutUrl(recording); + + List envs = new ArrayList<>(); + envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug()); + envs.add("RECORDING_TYPE=COMPOSED_QUICK_START"); + envs.add("RESOLUTION=" + properties.resolution()); + envs.add("URL=" + layoutUrl); + + log.info("Recorder connecting to url {}", layoutUrl); + + String containerId = null; + try { + final String container = RecordingManager.IMAGE_NAME + ":" + RecordingManager.IMAGE_TAG; + final String containerName = "recording_" + session.getSessionId(); + Volume volume1 = new Volume("/recordings"); + List volumes = new ArrayList<>(); + volumes.add(volume1); + Bind bind1 = new Bind(openviduConfig.getOpenViduRecordingPath(), volume1); + List binds = new ArrayList<>(); + binds.add(bind1); + containerId = dockerManager.runContainer(container, containerName, null, volumes, binds, "host", envs, null, + properties.shmSize(), false, null); + containers.put(containerId, containerName); + this.sessionsContainers.put(session.getSessionId(), containerId); + } catch (Exception e) { + if (containerId != null) { + dockerManager.removeDockerContainer(containerId, true); + containers.remove(containerId); + sessionsContainers.remove(session.getSessionId()); + } + log.error("Error while launchig container for COMPOSED_QUICK_START: ({})", e.getMessage()); + throw e; + } + return containerId; + } + +} diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java index 92125374..7958ebe1 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java @@ -63,11 +63,11 @@ public class ComposedRecordingService extends RecordingService { private static final Logger log = LoggerFactory.getLogger(ComposedRecordingService.class); - private Map containers = new ConcurrentHashMap<>(); - private Map sessionsContainers = new ConcurrentHashMap<>(); + protected Map containers = new ConcurrentHashMap<>(); + protected Map sessionsContainers = new ConcurrentHashMap<>(); private Map composites = new ConcurrentHashMap<>(); - private DockerManager dockerManager; + protected DockerManager dockerManager; public ComposedRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader, OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) { @@ -102,18 +102,18 @@ public class ComposedRecordingService extends RecordingService { } @Override - public Recording stopRecording(Session session, Recording recording, EndReason reason) { + public Recording stopRecording(Session session, Recording recording, EndReason reason, boolean hasSessionEnded) { recording = this.sealRecordingMetadataFileAsStopped(recording); if (recording.hasVideo()) { - return this.stopRecordingWithVideo(session, recording, reason); + return this.stopRecordingWithVideo(session, recording, reason, hasSessionEnded); } else { return this.stopRecordingAudioOnly(session, recording, reason, 0); } } - public Recording stopRecording(Session session, Recording recording, EndReason reason, long kmsDisconnectionTime) { + public Recording stopRecording(Session session, Recording recording, EndReason reason, long kmsDisconnectionTime, boolean hasSessionEnded) { if (recording.hasVideo()) { - return this.stopRecordingWithVideo(session, recording, reason); + return this.stopRecordingWithVideo(session, recording, reason, hasSessionEnded); } else { return this.stopRecordingAudioOnly(session, recording, reason, kmsDisconnectionTime); } @@ -142,7 +142,7 @@ public class ComposedRecordingService extends RecordingService { compositeWrapper.disconnectPublisherEndpoint(streamId); } - private Recording startRecordingWithVideo(Session session, Recording recording, RecordingProperties properties) + protected Recording startRecordingWithVideo(Session session, Recording recording, RecordingProperties properties) throws OpenViduException { log.info("Starting composed ({}) recording {} of session {}", @@ -152,6 +152,7 @@ public class ComposedRecordingService extends RecordingService { String layoutUrl = this.getLayoutUrl(recording); + envs.add("DEBUG_MODE=" + openviduConfig.isOpenViduRecordingDebug()); envs.add("URL=" + layoutUrl); envs.add("ONLY_VIDEO=" + !properties.hasAudio()); envs.add("RESOLUTION=" + properties.resolution()); @@ -227,7 +228,7 @@ public class ComposedRecordingService extends RecordingService { return recording; } - private Recording stopRecordingWithVideo(Session session, Recording recording, EndReason reason) { + protected Recording stopRecordingWithVideo(Session session, Recording recording, EndReason reason, boolean hasSessionEnded) { log.info("Stopping composed ({}) recording {} of session {}. Reason: {}", recording.hasAudio() ? "video + audio" : "audio-only", recording.getId(), recording.getSessionId(), @@ -389,7 +390,7 @@ public class ComposedRecordingService extends RecordingService { return finalRecordingArray[0]; } - private void stopAndRemoveRecordingContainer(Recording recording, String containerId, int secondsOfWait) { + protected void stopAndRemoveRecordingContainer(Recording recording, String containerId, int secondsOfWait) { // Gracefully stop ffmpeg process try { dockerManager.runCommandInContainer(containerId, "echo 'q' > stop", 0); @@ -411,7 +412,7 @@ public class ComposedRecordingService extends RecordingService { containers.remove(containerId); } - private Recording updateRecordingAttributes(Recording recording) { + protected Recording updateRecordingAttributes(Recording recording) { try { RecordingInfoUtils infoUtils = new RecordingInfoUtils(this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/" + recording.getId() + ".info"); @@ -436,7 +437,7 @@ public class ComposedRecordingService extends RecordingService { } } - private void waitForVideoFileNotEmpty(Recording recording) throws OpenViduException { + protected void waitForVideoFileNotEmpty(Recording recording) throws OpenViduException { boolean isPresent = false; int i = 1; int timeout = 150; // Wait for 150*150 = 22500 = 22.5 seconds @@ -459,7 +460,7 @@ public class ComposedRecordingService extends RecordingService { } } - private void failRecordingCompletion(Recording recording, String containerId, OpenViduException e) + protected void failRecordingCompletion(Recording recording, String containerId, OpenViduException e) throws OpenViduException { recording.setStatus(io.openvidu.java.client.Recording.Status.failed); dockerManager.removeDockerContainer(containerId, true); @@ -467,7 +468,7 @@ public class ComposedRecordingService extends RecordingService { throw e; } - private String getLayoutUrl(Recording recording) throws OpenViduException { + protected String getLayoutUrl(Recording recording) throws OpenViduException { String secret = openviduConfig.getOpenViduSecret(); // Check if "customLayout" property defines a final URL diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java index 5b15b18b..c8b5e011 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java @@ -76,12 +76,14 @@ import io.openvidu.server.utils.CustomFileManager; import io.openvidu.server.utils.DockerManager; import io.openvidu.server.utils.JsonUtils; import io.openvidu.server.utils.QuarantineKiller; +import org.springframework.http.ResponseEntity; public class RecordingManager { private static final Logger log = LoggerFactory.getLogger(RecordingManager.class); private ComposedRecordingService composedRecordingService; + private ComposedQuickStartRecordingService composedQuickStartRecordingService; private SingleStreamRecordingService singleStreamRecordingService; private DockerManager dockerManager; @@ -159,6 +161,8 @@ public class RecordingManager { this.dockerManager = new DockerManager(); this.composedRecordingService = new ComposedRecordingService(this, recordingDownloader, openviduConfig, cdr, quarantineKiller); + this.composedQuickStartRecordingService = new ComposedQuickStartRecordingService(this, recordingDownloader, openviduConfig, cdr, + quarantineKiller); this.singleStreamRecordingService = new SingleStreamRecordingService(this, recordingDownloader, openviduConfig, cdr, quarantineKiller); @@ -231,6 +235,14 @@ public class RecordingManager { this.checkRecordingPaths(openviduRecordingPath, openviduRecordingCustomLayout); } + public void startComposedQuickStartContainer(Session session) { + this.composedQuickStartRecordingService.runComposedQuickStartContainer(session); + } + + public void stopComposedQuickStartContainer(Session session, EndReason reason) { + this.composedQuickStartRecordingService.stopRecordingContainer(session, reason); + } + public Recording startRecording(Session session, RecordingProperties properties) throws OpenViduException { try { if (session.recordingLock.tryLock(15, TimeUnit.SECONDS)) { @@ -245,6 +257,9 @@ public class RecordingManager { case COMPOSED: recording = this.composedRecordingService.startRecording(session, properties); break; + case COMPOSED_QUICK_START: + recording = this.composedQuickStartRecordingService.startRecording(session, properties); + break; case INDIVIDUAL: recording = this.singleStreamRecordingService.startRecording(session, properties); break; @@ -287,7 +302,7 @@ public class RecordingManager { } } - public Recording stopRecording(Session session, String recordingId, EndReason reason) { + public Recording stopRecording(Session session, String recordingId, EndReason reason, boolean hasSessionEnded) { Recording recording; if (session == null) { recording = this.startedRecordings.get(recordingId); @@ -301,10 +316,13 @@ public class RecordingManager { switch (recording.getOutputMode()) { case COMPOSED: - recording = this.composedRecordingService.stopRecording(session, recording, reason); + recording = this.composedRecordingService.stopRecording(session, recording, reason, hasSessionEnded); + break; + case COMPOSED_QUICK_START: + recording = this.composedQuickStartRecordingService.stopRecording(session, recording, reason, hasSessionEnded); break; case INDIVIDUAL: - recording = this.singleStreamRecordingService.stopRecording(session, recording, reason); + recording = this.singleStreamRecordingService.stopRecording(session, recording, reason, hasSessionEnded); break; } this.abortAutomaticRecordingStopThread(session, reason); @@ -316,7 +334,16 @@ public class RecordingManager { recording = this.sessionsRecordings.get(session.getSessionId()); switch (recording.getOutputMode()) { case COMPOSED: - recording = this.composedRecordingService.stopRecording(session, recording, reason, kmsDisconnectionTime); + recording = this.composedRecordingService.stopRecording(session, recording, reason, kmsDisconnectionTime, true); + if (recording.hasVideo()) { + // Evict the recorder participant if composed recording with video + this.sessionManager.evictParticipant( + session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null, + null); + } + break; + case COMPOSED_QUICK_START: + recording = this.composedQuickStartRecordingService.stopRecording(session, recording, reason, kmsDisconnectionTime, true); if (recording.hasVideo()) { // Evict the recorder participant if composed recording with video this.sessionManager.evictParticipant( @@ -531,7 +558,7 @@ public class RecordingManager { log.info( "Automatic stopping recording {}. There are users connected to session {}, but no one is publishing", recordingId, session.getSessionId()); - this.stopRecording(session, recordingId, EndReason.automaticStop); + this.stopRecording(session, recordingId, EndReason.automaticStop, true); } } finally { if (!alreadyUnlocked) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java index 10661536..4fbba93c 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java @@ -58,7 +58,7 @@ public abstract class RecordingService { public abstract Recording startRecording(Session session, RecordingProperties properties) throws OpenViduException; - public abstract Recording stopRecording(Session session, Recording recording, EndReason reason); + public abstract Recording stopRecording(Session session, Recording recording, EndReason reason, boolean hasSessionEnded); /** * Generates metadata recording file (".recording.RECORDING_ID" JSON file to @@ -177,7 +177,7 @@ public abstract class RecordingService { recording.setStatus(io.openvidu.java.client.Recording.Status.failed); this.recordingManager.startingRecordings.remove(recording.getId()); this.recordingManager.sessionsRecordingsStarting.remove(session.getSessionId()); - this.stopRecording(session, recording, null); + this.stopRecording(session, recording, null, true); return new OpenViduException(Code.RECORDING_START_ERROR_CODE, errorMessage); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java index 0e246e34..cff12bdd 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java @@ -138,7 +138,7 @@ public class SingleStreamRecordingService extends RecordingService { } @Override - public Recording stopRecording(Session session, Recording recording, EndReason reason) { + public Recording stopRecording(Session session, Recording recording, EndReason reason, boolean hasSessionEnded) { recording = this.sealRecordingMetadataFileAsStopped(recording); return this.stopRecording(session, recording, reason, 0); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index 58d3e63c..f9950990 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -597,7 +597,7 @@ public class SessionRestController { Session session = sessionManager.getSession(recording.getSessionId()); Recording stoppedRecording = this.recordingManager.stopRecording(session, recording.getId(), - EndReason.recordingStoppedByServer); + EndReason.recordingStoppedByServer, false); session.recordingManuallyStopped.set(true); diff --git a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 700e38be..9448757d 100644 --- a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -46,6 +46,12 @@ "description": "Whether to start OpenVidu Server with recording module service available or not (a Docker image will be downloaded during the first execution). Apart from setting this param to true, it is also necessary to explicitly configure sessions to be recorded", "defaultValue": false }, + { + "name": "OPENVIDU_RECORDING_DEBUG", + "type": "java.lang.Boolean", + "description": "If true, start recording service in debug mode", + "defaultValue": false + }, { "name": "OPENVIDU_RECORDING_PATH", "type": "java.lang.String", diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index 93e3f2f6..bd900455 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -25,6 +25,7 @@ OPENVIDU_WEBHOOK_HEADERS=[] OPENVIDU_WEBHOOK_EVENTS=["sessionCreated","sessionDestroyed","participantJoined","participantLeft","webrtcConnectionCreated","webrtcConnectionDestroyed","recordingStatusChanged","filterEventDispatched","mediaNodeStatusChanged"] OPENVIDU_RECORDING=false +OPENVIDU_RECORDING_DEBUG=false OPENVIDU_RECORDING_VERSION=2.9.0 OPENVIDU_RECORDING_PATH=/opt/openvidu/recordings OPENVIDU_RECORDING_PUBLIC_ACCESS=false diff --git a/openvidu-testapp/package-lock.json b/openvidu-testapp/package-lock.json index b4f038e9..4af7eef5 100644 --- a/openvidu-testapp/package-lock.json +++ b/openvidu-testapp/package-lock.json @@ -1,6 +1,6 @@ { "name": "openvidu-testapp", - "version": "2.11.0", + "version": "2.14.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -7066,46 +7066,46 @@ } }, "openvidu-browser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/openvidu-browser/-/openvidu-browser-2.11.0.tgz", - "integrity": "sha512-dXnzHA9gf0Fhl88YijmPbFT/d7ERUHOMrqGObVeDc5NvG/7y2HCFafb/42O/mZ86IkBZnmqSrl6Q63aNlUZ2iA==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/openvidu-browser/-/openvidu-browser-2.14.0.tgz", + "integrity": "sha512-MImYBxShx/dAj3tW6GjGV1NZbibB4aNlGaQyJGj6lQiI2aT9+RmsjSHtUchiOMV62Q6frovkevJcy1j9PMFokQ==", "requires": { - "@types/node": "12.6.8", + "@types/node": "13.13.2", "@types/platform": "1.3.2", "freeice": "2.2.2", "hark": "1.2.3", "platform": "1.3.5", - "uuid": "3.3.2", - "wolfy87-eventemitter": "5.2.6" + "uuid": "7.0.3", + "wolfy87-eventemitter": "5.2.9" }, "dependencies": { "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" } }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" + }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "requires": { + "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" - }, "@types/node": { - "version": "12.6.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", - "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==" + "version": "13.13.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz", + "integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==" }, "@types/platform": { "version": "1.3.2", @@ -7127,40 +7127,29 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "acorn": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz", - "integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==" - }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==" }, "acorn-node": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.7.0.tgz", - "integrity": "sha512-XhahLSsCB6X6CJbe+uNu3Mn9sJBNFxtBN9NLgAOQovfS6Kh0lDUtmlclhjn9CvEK7A7YyRU13PXlNcpSiLI9Yw==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", "requires": { - "acorn": "^6.1.1", - "acorn-dynamic-import": "^4.0.0", - "acorn-walk": "^6.1.1", - "xtend": "^4.0.1" + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" } }, "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==" }, "ansi-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", - "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", @@ -7229,26 +7218,11 @@ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" }, - "array-filter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", - "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=" - }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" }, - "array-map": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", - "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=" - }, - "array-reduce": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", - "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=" - }, "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", @@ -7313,25 +7287,6 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, - "autoprefixer-core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-5.2.1.tgz", - "integrity": "sha1-5kDEFK5Bmq4hwa1DyOoPPbgqVm0=", - "requires": { - "browserslist": "~0.4.0", - "caniuse-db": "^1.0.30000214", - "num2fraction": "^1.1.0", - "postcss": "~4.1.12" - } - }, - "backbone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz", - "integrity": "sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==", - "requires": { - "underscore": ">=1.8.3" - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -7388,15 +7343,24 @@ } }, "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -7483,16 +7447,16 @@ } }, "browserify": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.3.0.tgz", - "integrity": "sha512-BWaaD7alyGZVEBBwSTYx4iJF5DswIGzK17o8ai9w4iKRbYpk3EOiprRHMRRA8DCZFmFeOdx7A385w2XdFvxWmg==", + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.5.1.tgz", + "integrity": "sha512-EQX0h59Pp+0GtSRb5rL6OTfrttlzv+uyaUVlK6GX3w11SQ0jKPKyjC/54RhPR2ib2KmfcELM06e8FxcI5XNU2A==", "requires": { "JSONStream": "^1.0.3", "assert": "^1.4.0", "browser-pack": "^6.0.1", "browser-resolve": "^1.11.0", "browserify-zlib": "~0.2.0", - "buffer": "^5.0.2", + "buffer": "~5.2.1", "cached-path-relative": "^1.0.0", "concat-stream": "^1.6.0", "console-browserify": "^1.1.0", @@ -7510,7 +7474,7 @@ "inherits": "~2.0.1", "insert-module-globals": "^7.0.0", "labeled-stream-splicer": "^2.0.0", - "mkdirp": "^0.5.0", + "mkdirp-classic": "^0.5.2", "module-deps": "^6.0.0", "os-browserify": "~0.3.0", "parents": "^1.0.1", @@ -7524,7 +7488,7 @@ "shasum": "^1.0.0", "shell-quote": "^1.6.1", "stream-browserify": "^2.0.0", - "stream-http": "^2.0.0", + "stream-http": "^3.0.0", "string_decoder": "^1.1.1", "subarg": "^1.0.0", "syntax-error": "^1.1.1", @@ -7602,14 +7566,6 @@ "pako": "~1.0.5" } }, - "browserslist": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-0.4.0.tgz", - "integrity": "sha1-O9SrkZncG5FQ1NbbpNnTqrvIbdQ=", - "requires": { - "caniuse-db": "^1.0.30000153" - } - }, "buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", @@ -7679,11 +7635,6 @@ "map-obj": "^1.0.0" } }, - "caniuse-db": { - "version": "1.0.30000986", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000986.tgz", - "integrity": "sha512-8SKJ12AFwG0ReMjPwRH+keFsX/ucw2bi6LC7upeXBvxjgrMqHaTxgYhkRGm+eOwUWvVcqXDgqM7QNlRJMhvXZg==" - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -7695,9 +7646,9 @@ } }, "chokidar": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", - "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.1", @@ -7797,9 +7748,9 @@ } }, "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "component-emitter": { "version": "1.3.0", @@ -7823,12 +7774,9 @@ } }, "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "requires": { - "date-now": "^0.1.4" - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" }, "constants-browserify": { "version": "1.0.0", @@ -7954,11 +7902,6 @@ "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==" }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" - }, "dateformat": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", @@ -8029,20 +7972,20 @@ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, "deps-sort": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", - "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", "requires": { "JSONStream": "^1.0.3", - "shasum": "^1.0.0", + "shasum-object": "^1.0.0", "subarg": "^1.0.0", "through2": "^2.0.0" } }, "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", "requires": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -8077,9 +8020,9 @@ } }, "diff": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.3.2.tgz", - "integrity": "sha1-/Qeh8fiRUZ2ZBaTJqJ3PWnC2YDc=" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" }, "diffie-hellman": { "version": "5.0.3", @@ -8110,9 +8053,9 @@ } }, "elliptic": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", - "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -8124,12 +8067,11 @@ } }, "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", "requires": { - "string-template": "~0.2.1", - "xtend": "~4.0.0" + "string-template": "~0.2.1" } }, "error-ex": { @@ -8141,9 +8083,9 @@ } }, "es6-promise": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz", - "integrity": "sha1-lu258v2wGZWCKyY92KratnSBgbw=" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-0.1.2.tgz", + "integrity": "sha1-8RLCn+paCZhTn8tqL9IUQ9KPBfc=" }, "escape-string-regexp": { "version": "1.0.5", @@ -8155,11 +8097,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, "eventemitter2": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", @@ -8307,6 +8244,11 @@ } } }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -8329,6 +8271,12 @@ "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=" }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -8443,13 +8391,14 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", "optional": true, "requires": { + "bindings": "^1.5.0", "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "node-pre-gyp": "*" }, "dependencies": { "abbrev": { @@ -8491,7 +8440,7 @@ } }, "chownr": { - "version": "1.1.1", + "version": "1.1.4", "bundled": true, "optional": true }, @@ -8516,7 +8465,7 @@ "optional": true }, "debug": { - "version": "4.1.1", + "version": "3.2.6", "bundled": true, "optional": true, "requires": { @@ -8539,11 +8488,11 @@ "optional": true }, "fs-minipass": { - "version": "1.2.5", + "version": "1.2.7", "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -8567,7 +8516,7 @@ } }, "glob": { - "version": "7.1.3", + "version": "7.1.6", "bundled": true, "optional": true, "requires": { @@ -8593,7 +8542,7 @@ } }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "optional": true, "requires": { @@ -8610,7 +8559,7 @@ } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true, "optional": true }, @@ -8641,12 +8590,12 @@ } }, "minimist": { - "version": "0.0.8", + "version": "1.2.5", "bundled": true, "optional": true }, "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, "optional": true, "requires": { @@ -8655,38 +8604,38 @@ } }, "minizlib": { - "version": "1.2.1", + "version": "1.3.3", "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.9.0" } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.3", "bundled": true, "optional": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { - "version": "2.1.1", + "version": "2.1.2", "bundled": true, "optional": true }, "needle": { - "version": "2.3.0", + "version": "2.3.3", "bundled": true, "optional": true, "requires": { - "debug": "^4.1.0", + "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.12.0", + "version": "0.14.0", "bundled": true, "optional": true, "requires": { @@ -8699,11 +8648,11 @@ "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4" + "tar": "^4.4.2" } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "optional": true, "requires": { @@ -8712,17 +8661,26 @@ } }, "npm-bundled": { - "version": "1.0.6", + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", "bundled": true, "optional": true }, "npm-packlist": { - "version": "1.4.1", + "version": "1.4.8", "bundled": true, "optional": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npmlog": { @@ -8779,7 +8737,7 @@ "optional": true }, "process-nextick-args": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "optional": true }, @@ -8792,17 +8750,10 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } } }, "readable-stream": { - "version": "2.3.6", + "version": "2.3.7", "bundled": true, "optional": true, "requires": { @@ -8816,7 +8767,7 @@ } }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "optional": true, "requires": { @@ -8839,7 +8790,7 @@ "optional": true }, "semver": { - "version": "5.7.0", + "version": "5.7.1", "bundled": true, "optional": true }, @@ -8885,17 +8836,17 @@ "optional": true }, "tar": { - "version": "4.4.8", + "version": "4.4.13", "bundled": true, "optional": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "yallist": "^3.0.3" } }, "util-deprecate": { @@ -8917,7 +8868,7 @@ "optional": true }, "yallist": { - "version": "3.0.3", + "version": "3.1.1", "bundled": true, "optional": true } @@ -8957,9 +8908,9 @@ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=" }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -9001,24 +8952,24 @@ } }, "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", + "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", "requires": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "~4.17.12", "minimatch": "~3.0.2" } }, "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "grunt": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz", - "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.1.0.tgz", + "integrity": "sha512-+NGod0grmviZ7Nzdi9am7vuRS/h76PcWDsV635mEXF0PEQMUV6Kb+OjTdsVxbi0PZmfQOjCMKb3w8CVZcqsn1g==", "requires": { "coffeescript": "~1.10.0", "dateformat": "~1.0.12", @@ -9031,9 +8982,9 @@ "grunt-legacy-log": "~2.0.0", "grunt-legacy-util": "~1.1.1", "iconv-lite": "~0.4.13", - "js-yaml": "~3.13.0", + "js-yaml": "~3.13.1", "minimatch": "~3.0.2", - "mkdirp": "~0.5.1", + "mkdirp": "~1.0.3", "nopt": "~3.0.6", "path-is-absolute": "~1.0.0", "rimraf": "~2.6.2" @@ -9070,41 +9021,6 @@ } } }, - "grunt-autoprefixer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/grunt-autoprefixer/-/grunt-autoprefixer-3.0.4.tgz", - "integrity": "sha1-/kLiR7z6ucKSoSwGLa1PNb3pAsU=", - "requires": { - "autoprefixer-core": "^5.1.7", - "chalk": "~1.0.0", - "diff": "~1.3.0", - "postcss": "^4.1.11" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.0.0.tgz", - "integrity": "sha1-s89O0P9Tl8mcdbj2edsvUoMfltw=", - "requires": { - "ansi-styles": "^2.0.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^1.0.3", - "strip-ansi": "^2.0.1", - "supports-color": "^1.3.0" - } - }, - "supports-color": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz", - "integrity": "sha1-FXWN8J2P87SswwdTn6vicJXhBC0=" - } - } - }, "grunt-cli": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz", @@ -9118,9 +9034,9 @@ }, "dependencies": { "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "requires": { "abbrev": "1", "osenv": "^0.1.4" @@ -9137,11 +9053,6 @@ "file-sync-cmp": "^0.1.0" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -9159,22 +9070,6 @@ "supports-color": "^2.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -9194,11 +9089,6 @@ "which": "^1.0.5" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -9221,22 +9111,6 @@ "supports-color": "^2.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -9315,6 +9189,16 @@ "which": "~1.3.0" } }, + "grunt-postcss": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.9.0.tgz", + "integrity": "sha512-lglLcVaoOIqH0sFv7RqwUKkEFGQwnlqyAKbatxZderwZGV1nDyKHN7gZS9LUiTx1t5GOvRBx0BEalHMyVwFAIA==", + "requires": { + "chalk": "^2.1.0", + "diff": "^3.0.0", + "postcss": "^6.0.11" + } + }, "grunt-string-replace": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/grunt-string-replace/-/grunt-string-replace-1.3.1.tgz", @@ -9324,11 +9208,6 @@ "chalk": "^1.0.0" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -9354,22 +9233,6 @@ "supports-color": "^2.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -9395,11 +9258,6 @@ "strip-bom": "^2.0.0" }, "dependencies": { - "es6-promise": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-0.1.2.tgz", - "integrity": "sha1-8RLCn+paCZhTn8tqL9IUQ9KPBfc=" - }, "rimraf": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", @@ -9416,14 +9274,15 @@ } }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", "requires": { + "minimist": "^1.2.5", "neo-async": "^2.6.0", - "optimist": "^0.6.1", "source-map": "^0.6.1", - "uglify-js": "^3.1.4" + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" }, "dependencies": { "source-map": { @@ -9450,12 +9309,11 @@ } }, "has-ansi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-1.0.3.tgz", - "integrity": "sha1-wLWxYV2eOCsP9nFp2We0JeSMpTg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "^1.1.0", - "get-stdin": "^4.0.1" + "ansi-regex": "^2.0.0" } }, "has-flag": { @@ -9511,9 +9369,9 @@ } }, "highlight.js": { - "version": "9.15.8", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.8.tgz", - "integrity": "sha512-RrapkKQWwE+wKdF73VsOa2RQdIoO3mxwJ4P8mhbI6KYJUraUHRKM5w5zQQKXNk0xNL4UVRdulV9SBJcmzJNzVA==" + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==" }, "hmac-drbg": { "version": "1.0.1", @@ -9539,9 +9397,9 @@ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=" }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" }, "htmlescape": { "version": "1.1.1", @@ -9719,12 +9577,9 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" }, "is-glob": { "version": "3.1.0", @@ -9801,16 +9656,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "jquery": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", - "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" - }, - "js-base64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", - "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9857,9 +9702,9 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "labeled-stream-splicer": { "version": "2.0.2", @@ -9940,9 +9785,9 @@ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" }, "lunr": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.6.tgz", - "integrity": "sha512-swStvEyDqQ85MGpABCMBclZcLI/pBIlu8FFDtmX197+oEgKloJ67QnB+Tidh0340HmLMs39c4GrkPY3cmkXp6Q==" + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", + "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==" }, "make-iterator": { "version": "1.0.1", @@ -9971,9 +9816,9 @@ } }, "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==" }, "maxmin": { "version": "2.1.0", @@ -9986,11 +9831,6 @@ "pretty-bytes": "^3.0.0" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -10008,22 +9848,6 @@ "supports-color": "^2.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -10106,9 +9930,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixin-deep": { "version": "1.3.2", @@ -10130,31 +9954,26 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mkdirp-classic": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", + "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==" }, "module-deps": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.1.tgz", - "integrity": "sha512-UnEn6Ah36Tu4jFiBbJVUtt0h+iXqxpLqDvPS8nllbw5RZFmNJ1+Mz5BjYnM9ieH80zyxHkARGLnMIHlPK5bu6A==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.2.tgz", + "integrity": "sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w==", "requires": { "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", "cached-path-relative": "^1.0.2", "concat-stream": "~1.6.0", "defined": "^1.0.0", - "detective": "^5.0.2", + "detective": "^5.2.0", "duplexer2": "^0.1.2", "inherits": "^2.0.1", "parents": "^1.0.0", @@ -10172,9 +9991,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", "optional": true }, "nanomatch": { @@ -10234,11 +10053,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -10321,22 +10135,6 @@ "wrappy": "1" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" - } - } - }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -10362,9 +10160,9 @@ } }, "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "parents": { "version": "1.0.1", @@ -10375,9 +10173,9 @@ } }, "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", "requires": { "asn1.js": "^4.0.0", "browserify-aes": "^1.0.0", @@ -10512,22 +10310,19 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-4.1.16.tgz", - "integrity": "sha1-TESbTIr53zyvbTf44eV10DYXWNw=", + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "requires": { - "es6-promise": "~2.3.0", - "js-base64": "~2.1.8", - "source-map": "~0.4.2" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" }, "dependencies": { "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -10573,9 +10368,9 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" }, "querystring": { "version": "0.2.0", @@ -10648,9 +10443,9 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -10736,9 +10531,9 @@ } }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "requires": { "path-parse": "^1.0.6" } @@ -10808,9 +10603,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "set-value": { "version": "2.0.1", @@ -10851,17 +10646,19 @@ "sha.js": "~2.4.4" } }, - "shell-quote": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", - "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", "requires": { - "array-filter": "~0.0.0", - "array-map": "~0.0.0", - "array-reduce": "~0.0.0", - "jsonify": "~0.0.0" + "fast-safe-stringify": "^2.0.7" } }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, "shelljs": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", @@ -10873,9 +10670,9 @@ } }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "simple-concat": { "version": "1.0.0", @@ -10985,17 +10782,33 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", "urix": "^0.1.0" } }, + "source-map-support": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.18.tgz", + "integrity": "sha512-9luZr/BZ2QeU6tO2uG8N2aZpVSli4TSAOAqFOyTO51AJcD9P99c0K1h6dD6r6qo5dyT44BR5exweOaLLeldTkQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -11011,9 +10824,9 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { "version": "3.0.0", @@ -11080,15 +10893,26 @@ } }, "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.0.tgz", + "integrity": "sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw==", "requires": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", + "readable-stream": "^3.0.6", "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "stream-splicer": { @@ -11106,26 +10930,19 @@ "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=" }, "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "safe-buffer": "~5.2.0" } }, "strip-ansi": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", - "integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^1.0.0" + "ansi-regex": "^2.0.0" } }, "strip-bom": { @@ -11173,6 +10990,23 @@ "acorn-node": "^1.2.0" } }, + "terser": { + "version": "4.6.11", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz", + "integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -11223,11 +11057,6 @@ } } }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -11296,34 +11125,42 @@ } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" }, "tslint": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", - "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.1.tgz", + "integrity": "sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA==", "requires": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", "commander": "^2.12.1", - "diff": "^3.2.0", + "diff": "^4.0.1", "glob": "^7.1.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.3", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.8.0", + "tslib": "^1.10.0", "tsutils": "^2.29.0" }, "dependencies": { "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } } } }, @@ -11346,53 +11183,41 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typedoc": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.15.0.tgz", - "integrity": "sha512-NOtfq5Tis4EFt+J2ozhVq9RCeUnfEYMFKoU6nCXCXUULJz1UQynOM+yH3TkfZCPLzigbqB0tQYGVlktUWweKlw==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.4.tgz", + "integrity": "sha512-4Lotef1l6lNU5Fulpux809WPlF9CkmcXfv5QFyanrjYlxMFxSdARRdsy8Jv1OU3z0vjR4JsvUQT0YpiPqztcOA==", "requires": { - "@types/minimatch": "3.0.3", "fs-extra": "^8.1.0", - "handlebars": "^4.1.2", - "highlight.js": "^9.15.8", + "handlebars": "^4.7.6", + "highlight.js": "^9.18.1", "lodash": "^4.17.15", - "marked": "^0.7.0", + "lunr": "^2.3.8", + "marked": "0.8.2", "minimatch": "^3.0.0", "progress": "^2.0.3", "shelljs": "^0.8.3", - "typedoc-default-themes": "^0.6.0", - "typescript": "3.5.x" + "typedoc-default-themes": "^0.10.0" } }, "typedoc-default-themes": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.6.0.tgz", - "integrity": "sha512-MdTROOojxod78CEv22rIA69o7crMPLnVZPefuDLt/WepXqJwgiSu8Xxq+H36x0Jj3YGc7lOglI2vPJ2GhoOybw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.1.tgz", + "integrity": "sha512-SuqAQI0CkwhqSJ2kaVTgl37cWs733uy9UGUqwtcds8pkFK8oRF4rZmCq+FXTGIb9hIUOu40rf5Kojg0Ha6akeg==", "requires": { - "backbone": "^1.4.0", - "jquery": "^3.4.1", - "lunr": "^2.3.6", - "underscore": "^1.9.1" + "lunr": "^2.3.8" } }, "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==" + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz", + "integrity": "sha512-JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA==", "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "commander": "~2.20.3" } }, "umd": { @@ -11417,11 +11242,6 @@ "xtend": "^4.0.1" } }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, "underscore.string": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", @@ -11484,9 +11304,9 @@ } }, "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, "uri-path": { "version": "1.0.0", @@ -11540,9 +11360,9 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" }, "v8flags": { "version": "3.1.3", @@ -11562,9 +11382,9 @@ } }, "vm-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", - "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, "websocket-driver": { "version": "0.7.3", @@ -11595,14 +11415,14 @@ "integrity": "sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw==" }, "wolfy87-eventemitter": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.6.tgz", - "integrity": "sha512-n+bSucT1j9ZEoosxnfuH81bWqtZG4QEtZ9WEuiXz9YQAHEktGYKoSoMTKWTJEcYux8lWoqp1KqHPBpwvJKFFTw==" + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz", + "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==" }, "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" }, "wrappy": { "version": "1.0.2", @@ -11610,18 +11430,18 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", "requires": { "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "xmlbuilder": "~11.0.0" } }, "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" }, "xtend": { "version": "4.0.2", diff --git a/openvidu-testapp/package.json b/openvidu-testapp/package.json index 8a5e9fd0..372d8c59 100644 --- a/openvidu-testapp/package.json +++ b/openvidu-testapp/package.json @@ -1,48 +1,48 @@ { "dependencies": { - "@angular/animations": "8.2.14", - "@angular/cdk": "8.2.3", - "@angular/common": "8.2.14", - "@angular/compiler": "8.2.14", - "@angular/core": "8.2.14", - "@angular/flex-layout": "8.0.0-beta.27", - "@angular/forms": "8.2.14", - "@angular/http": "7.2.15", - "@angular/material": "8.2.3", - "@angular/platform-browser": "8.2.14", - "@angular/platform-browser-dynamic": "8.2.14", - "@angular/router": "8.2.14", - "colormap": "2.3.1", - "core-js": "3.4.7", - "hammerjs": "2.0.8", - "openvidu-browser": "2.14.0", - "openvidu-node-client": "2.11.0", - "rxjs": "6.5.3", + "@angular/animations": "8.2.14", + "@angular/cdk": "8.2.3", + "@angular/common": "8.2.14", + "@angular/compiler": "8.2.14", + "@angular/core": "8.2.14", + "@angular/flex-layout": "8.0.0-beta.27", + "@angular/forms": "8.2.14", + "@angular/http": "7.2.15", + "@angular/material": "8.2.3", + "@angular/platform-browser": "8.2.14", + "@angular/platform-browser-dynamic": "8.2.14", + "@angular/router": "8.2.14", + "colormap": "2.3.1", + "core-js": "3.4.7", + "hammerjs": "2.0.8", + "openvidu-browser": "2.14.0", + "openvidu-node-client": "2.11.0", + "rxjs": "6.5.3", "zone.js": "0.10.2" - }, + }, "devDependencies": { - "@angular-devkit/build-angular": "0.803.20", - "@angular/cli": "8.3.20", - "@angular/compiler-cli": "8.2.14", - "@angular/language-service": "8.2.14", - "@types/jasmine": "3.5.0", - "@types/jasminewd2": "2.0.8", - "@types/node": "12.12.14", - "codelyzer": "5.2.0", - "ts-node": "8.5.4", - "tslint": "5.20.1", + "@angular-devkit/build-angular": "0.803.20", + "@angular/cli": "8.3.20", + "@angular/compiler-cli": "8.2.14", + "@angular/language-service": "8.2.14", + "@types/jasmine": "3.5.0", + "@types/jasminewd2": "2.0.8", + "@types/node": "12.12.14", + "codelyzer": "5.2.0", + "ts-node": "8.5.4", + "tslint": "5.20.1", "typescript": "3.5.3" - }, - "license": "Apache-2.0", - "name": "openvidu-testapp", - "private": true, + }, + "license": "Apache-2.0", + "name": "openvidu-testapp", + "private": true, "scripts": { - "build": "ng build", - "e2e": "ng e2e", - "lint": "ng lint", - "ng": "ng", - "start": "ng serve", + "build": "ng build", + "e2e": "ng e2e", + "lint": "ng lint", + "ng": "ng", + "start": "ng serve", "test": "ng test" - }, + }, "version": "2.14.0" -} \ No newline at end of file +}