├── .eslintrc.json ├── .gitignore ├── Dockerfile ├── README.md ├── deploy.sh ├── docker-compose.yml ├── images ├── VideoTest.gif └── WebcamTest.gif ├── package.json ├── src ├── index.js ├── kurento-pipeline.js └── server.js ├── webapp ├── Dockerfile ├── build_and_run.sh ├── nginx.conf ├── package.json └── static │ ├── assets │ ├── WebRTC.png │ ├── spinner.gif │ └── transparent-1px.png │ ├── bower.json │ ├── index.html │ ├── src │ └── index_tmpl.js │ └── style │ └── webrtcctv.css └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "parser": "babel-eslint", 9 | "rules": { 10 | "no-const-assign": "warn", 11 | "no-this-before-super": "warn", 12 | "no-undef": "warn", 13 | "no-unreachable": "warn", 14 | "no-unused-vars": "warn", 15 | "constructor-super": "warn", 16 | "valid-typeof": "warn" 17 | } 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node 2 | node_modules/ 3 | 4 | # Bower 5 | webapp/static/bower_components/ 6 | 7 | # npm 8 | npm-debug.log -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:7.10 2 | 3 | RUN mkdir -p /usr/src/app 4 | WORKDIR /usr/src/app 5 | 6 | 7 | EXPOSE 7000 8 | CMD [ "npm", "start" ] 9 | 10 | COPY package.json /usr/src/app/ 11 | RUN npm install 12 | COPY src /usr/src/app/src -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRTCCTV 2 | 3 | ## Introduction 4 | 5 | WebRTCCTV is a signaling server able to stream RTSP streams from cameras using WebRTC. It uses [Kurento](http://www.kurento.org/) as a signaling server. 6 | 7 |

8 |

9 | 10 | 11 | ## Explanations 12 | 13 | This repository contains a signaling server as well as a simple webapp example that use WebRTC to read RTSP streams. To use it in a production environment, you will also need a TURN server and you will need to run the services manually instead of with the provided `docker-compose.yml` file. See the [deployment script](deploy.sh) for an idea of how it can be deployed on a production environment. 14 | 15 | By using the `docker-compose.yml` file like explained below, you will have a local test environment with four containers running: 16 | 17 | - `kurento`: The WebRTC media server 18 | - `signaling`: The WebRTC signaling server (communication between client and media server) 19 | - `webapp`: The example webapp to start, pause and stop streams 20 | - `fake_camera`: An RTSP stream using [RTSPATT](https://github.com/EtixLabs/RTSPAllTheThings) 21 | 22 | The way all of this works is that the signaling server establishes a WebRTC connection between your web browser and the Kurento Media server, as well as creates the media pipeline that will be used for streaming video, and then your browser communicates directly via WebRTC with Kurento to get the stream. The signaling server is no longer needed once the connection is established. 23 | 24 | The reason why a TURN server is needed in case you want to deploy this system on a cloud is that your users will need to communicate with a media server that is behind a NAT or a firewall. The TURN server will help initiating connections by acting as a relay. Since the Kurento media server should NOT be accessible from the outside (it would be a security issue), you need a TURN server to be accessible publicly and to relay data between the user and the media server. 25 | 26 | ## How to build 27 | 28 | Just run `docker-compose build` in the root of this repository. 29 | 30 | ## How to run 31 | 32 | Just run `docker-compose up` in the root of this repository. 33 | 34 | ## How to test 35 | 36 | If you ran the previous command, you should now be able to access the webapp at [localhost:4242](http://localhost:4242). Just click the start button to launch the streaming. 37 | 38 | ## How to make it use your own camera 39 | 40 | For that, you'll have to change the RTSP_URL environment variable that is set in the `docker-compose.yml` file to put your RTSP URL instead. I might add features to the webapp later, but keep in mind that this web application is just a demonstration of the capabilities of the signaling server with Kurento. 41 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # VARIABLE INITIALIZATION 4 | 5 | set -e 6 | set -x 7 | 8 | usage() { 9 | echo "Usage: $(basename "$0") [-h] -c CLOUD_SSH_ADDR -u CLOUD_SSH_USER -a MEDIA_SERV_SSH_ADDR -m MEDIA_SERV_SSH_USER 10 | This script is used to deploy the WebRTCCTV in a cloud environment, using the first server 'MEDIA_SERV' as a media server and a the 'CLOUD' as the server that serves the web application, the signaling server and the TURN server. 11 | 12 | -h shows this help text 13 | -c set cloud SSH address 14 | -u set cloud SSH user 15 | -a set media server SSH address 16 | -m set media server SSH user" >&2 17 | } 18 | 19 | # COMMAND LINE PARSING 20 | 21 | while getopts ':hb:u:c:v:' option; do 22 | case "$option" in 23 | h) usage 24 | exit 25 | ;; 26 | c) CLOUD_SSH_ADDR=${OPTARG} 27 | ;; 28 | u) CLOUD_SSH_USER=${OPTARG} 29 | ;; 30 | a) MEDIA_SERV_SSH_ADDR=${OPTARG} 31 | ;; 32 | m) MEDIA_SERV_SSH_USER=${OPTARG} 33 | ;; 34 | :) printf "missing argument for -%s\n" "$OPTARG" >&2 35 | usage 36 | exit 1 37 | ;; 38 | \?) printf "illegal option: -%s\n" "$OPTARG" >&2 39 | usage 40 | exit 1 41 | ;; 42 | esac 43 | done 44 | shift $((OPTIND - 1)) 45 | 46 | ESC_SEQ="\x1b[" 47 | COL_RESET=$ESC_SEQ"39;49;00m" 48 | COL_RED=$ESC_SEQ"31;01m" 49 | COL_GREEN=$ESC_SEQ"32;01m" 50 | COL_YELLOW=$ESC_SEQ"33;01m" 51 | 52 | # INFO 53 | 54 | echo -e $COL_YELLOW"Cloud address "$CLOUD_SSH_ADDR$COL_RESET 55 | echo -e $COL_YELLOW"Cloud user "$CLOUD_SSH_USER$COL_RESET 56 | echo -e $COL_YELLOW"Media server address "$MEDIA_SERV_SSH_ADDR$COL_RESET 57 | echo -e $COL_YELLOW"Media server user "$MEDIA_SERV_SSH_USER$COL_RESET 58 | 59 | #################################### 60 | ###### SIGNALING SERVER DEPLOYMENT # 61 | #################################### 62 | 63 | echo "Starting signaling server..." 64 | ssh -o StrictHostKeyChecking=no -A $CLOUD_SSH_USER@$CLOUD_SSH_ADDR " 65 | git clone git@github.com:Ullaakut/WebRTCCTV.git 66 | cd WebRTCCTV 67 | ./docker-compose build 68 | ./docker-compose run -d -p 8443:8443 --name signaling --entrypoint bash signaling -c \"node server.js -k ws://" $MEDIA_SERV_SSH_ADDR ":8888/kurento -c " $CCTV_API_URL "\" signaling" 69 | 70 | echo "Starting webapp..." 71 | ssh -o StrictHostKeyChecking=no -A $CLOUD_SSH_USER@$CLOUD_SSH_ADDR " 72 | git clone git@github.com:Ullaakut/WebRTCCTV.git 73 | cd WebRTCCTV 74 | ./docker-compose build ; 75 | ./docker-compose run -d -p 80:80 -e SIGNALING_URI="$CLOUD_SSH_ADDR":8443 --name webapp webapp" 76 | 77 | echo "Starting TURN server" 78 | ssh -o StrictHostKeyChecking=no -A $CLOUD_SSH_USER@$CLOUD_SSH_ADDR "docker stop coturn; docker rm -f coturn ; docker run -d --net=host --name coturn ullaakut/dockurn ./turnserver -L0.0.0.0 --no-stun -v -f -a -r kurento.org -u kurento:kurento" 79 | 80 | ret=$? 81 | if [ "$ret" -ne "0" ]; then 82 | echo -e $COL_RED"The machine " $CLOUD_SSH_ADDR " was not accessible. The signaling server could not be deployed."$COL_RESET; 83 | exit 1; 84 | fi 85 | 86 | echo -e $COL_GREEN"The machine " $CLOUD_SSH_ADDR " was accessible and the signaling server should be started."$COL_RESET 87 | 88 | ################################### 89 | ######### MEDIA SERVER DEPLOYMENT # 90 | ################################### 91 | 92 | echo "Starting Kurento Media Server on $MEDIA_SERV_SSH_ADDR..." 93 | 94 | ssh -o StrictHostKeyChecking=no $MEDIA_SERV_SSH_USER@$MEDIA_SERV_SSH_ADDR " 95 | docker rm -f kurento; 96 | docker run -d --name kurento --cap-add=SETPCAP -e KMS_TURN_URL=kurento:kurento@"$CLOUD_SSH_ADDR":3478 -e OUTPUT_BITRATE=2048000 -p 8888:8888 ullaakut/kurento-custom-bitrate -e GST_DEBUG=Kurento*:5 97 | " 98 | 99 | ret=$? 100 | if [ "$ret" -ne "0" ]; then 101 | echo -e $COL_RED"The test server was not acessible. Kurento could not be deployed."$COL_RESET; 102 | exit 1; 103 | fi 104 | 105 | echo -e $COL_GREEN"The test server was acessible and Kurento should be started."$COL_RESET 106 | 107 | ssh $CLOUD_SSH_ADDR -l $CLOUD_SSH_USER "docker logs -ft signaling" -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.1" 2 | 3 | services: 4 | 5 | kurento: 6 | image: ullaakut/kurento-custom-bitrate 7 | environment: 8 | - OUTPUT_BITRATE=2048000 9 | ports: 10 | - 8888:8888 11 | depends_on: 12 | - fake_camera 13 | 14 | signaling: 15 | build: ./ 16 | ports: 17 | - 7000:7000 18 | depends_on: 19 | kurento: { condition: service_started } 20 | 21 | webapp: 22 | build: 23 | context: ./webapp 24 | dockerfile: Dockerfile 25 | ports: 26 | - 4242:80 27 | environment: 28 | - SIGNALING_URI=ws://localhost:7000 29 | depends_on: 30 | - signaling 31 | 32 | fake_camera: 33 | image: ullaakut/rtspatt 34 | # volumes: 35 | # - ./testvideo.mp4:/testvideo.mp4 36 | # environment: 37 | # - INPUT=/testvideo.mp4 38 | ports: 39 | - 8554:8554 -------------------------------------------------------------------------------- /images/VideoTest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ullaakut/WebRTCCTV/b2005a1ed4bfe85639259f068ab84847229f4456/images/VideoTest.gif -------------------------------------------------------------------------------- /images/WebcamTest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ullaakut/WebRTCCTV/b2005a1ed4bfe85639259f068ab84847229f4456/images/WebcamTest.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtcctv", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node src/index.js", 7 | "lint": "eslint ." 8 | }, 9 | "dependencies": { 10 | "babel-eslint": "^7.2.3", 11 | "eslint": "^3.19.0", 12 | "kurento-client": "Kurento/kurento-client-js", 13 | "node-fetch": "^1.7.0", 14 | "ws": "~0.7.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^3.19.0", 18 | "eslint-config-airbnb-base": "^11.2.0", 19 | "eslint-plugin-import": "^2.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ws = require('ws').Server; 4 | const Server = require('./server')(ws); 5 | const kurento = require('kurento-client'); 6 | const kurentoPipeline = require('./kurento-pipeline'); 7 | 8 | const kmsUrl = 'ws://kurento:8888/kurento'; 9 | 10 | console.log(`Using KMS WebSocket server at ${kmsUrl}`); 11 | 12 | // Establish connection with Kurento Media Server using its URL 13 | kurento(kmsUrl) 14 | .catch(error => { 15 | console.error( 16 | `Could not find media server at address ${kmsUrl}. Exiting:`, 17 | error 18 | ); 19 | process.exit(1); 20 | }) 21 | .then(kurentoClient => { 22 | const createPipeline = kurentoPipeline( 23 | kurentoClient, 24 | // We need to manually give this constructor to pipeline so it doesn't 25 | // have to import Kurento. 26 | kurento.register.complexTypes.IceCandidate 27 | ); 28 | 29 | const server = Server({ port: 7000 }); 30 | console.log('WS Server listening on port 7000'); 31 | 32 | server.onConnection(client => { 33 | const pipeline = createPipeline(client); 34 | 35 | client.onMessage(handleMessages(client, pipeline)); 36 | client.onError(handleError(pipeline)); 37 | client.onClose(handleClose(pipeline)); 38 | }); 39 | }); 40 | 41 | function handleMessages(client, pipeline) { 42 | return message => { 43 | switch (message.id) { 44 | case 'start': 45 | const { rtspUri, sdpOffer } = message; 46 | 47 | handleStart(client, pipeline)(rtspUri, sdpOffer); 48 | break; 49 | case 'stop': 50 | console.log('Client stopped stream'); 51 | pipeline.stop(); 52 | break; 53 | case 'onIceCandidate': 54 | pipeline.handleIceCandidate(message.candidate); 55 | break; 56 | default: 57 | client.send({ 58 | id: 'error', 59 | message: `Invalid message ID: ${message.id}`, 60 | }); 61 | break; 62 | } 63 | }; 64 | } 65 | 66 | function handleStart(client, pipeline) { 67 | return async (rtspUri, sdpOffer) => { 68 | console.log('START', rtspUri); 69 | 70 | try { 71 | console.log('Launching pipeline for RTSP URL:', rtspUri); 72 | pipeline.start(rtspUri, sdpOffer); 73 | } catch (error) { 74 | const [message, reason] = error; 75 | console.error(message); 76 | client.send({ 77 | id: 'error', 78 | error: reason, 79 | }); 80 | } 81 | }; 82 | } 83 | 84 | function handleError(pipeline) { 85 | return error => { 86 | console.error('WebSocket error:', error); 87 | pipeline.stop(); 88 | }; 89 | } 90 | 91 | function handleClose(pipeline) { 92 | return (code, reason) => { 93 | console.info(`WebSocket closed with code ${code}: ${reason}`); 94 | pipeline.stop(); 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /src/kurento-pipeline.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (kurentoClient, IceCandidate) => client => { 4 | let pipeline; 5 | let webRtcEndpoint; 6 | 7 | // Used to buffer ice candidates until webRtcEndpoint is ready to process them 8 | let iceCandidatesQueue = []; 9 | 10 | // Start an RTSP stream using client's offer 11 | function start(rtspUri, sdpOffer) { 12 | return startStream(rtspUri, sdpOffer).catch(pipelineError); 13 | } 14 | 15 | // Start the RTSP stream 16 | async function startStream(rtspUri, sdpOffer) { 17 | // Create the media pipeline 18 | const { playerEndpoint } = await createPipeline(rtspUri, sdpOffer); 19 | 20 | // Start the pipeline 21 | await playerEndpoint.play(); 22 | } 23 | 24 | // Create the kurento pipeline composed of a WebRTCEndpoint and a PlayerEndpoint 25 | // The PlayerEndpoint sends the stream into the pipeline 26 | // The WebRtcEndpoint forwards it to the browser 27 | async function createPipeline(rtspUri, sdpOffer) { 28 | console.log(`Creating KMS pipeline with RTSP stream: ${rtspUri}`); 29 | 30 | pipeline = await kurentoClient.create('MediaPipeline'); 31 | 32 | pipeline.on('Error', pipelineError); 33 | 34 | // Create the 2 endpoints in parallel 35 | const [playerEndpoint, webRtcEndpoint] = await Promise.all([ 36 | createPlayerEndpoint(rtspUri), 37 | createWebRtcEndpoint(sdpOffer), 38 | ]); 39 | 40 | // Connect the playerEndpoint to the webRtcEndpoint 41 | await playerEndpoint.connect(webRtcEndpoint, 'VIDEO'); 42 | 43 | return { 44 | playerEndpoint, 45 | webRtcEndpoint, 46 | pipeline, 47 | }; 48 | } 49 | 50 | // Create and start the player endpoint 51 | async function createPlayerEndpoint(rtspUri) { 52 | const playerOptions = { 53 | uri: rtspUri, 54 | useEncodedMedia: false, 55 | 56 | // Reduce the buffering in order to decrease latency to the minimum 57 | // Using 0 as the networkCache value could cause stability problems 58 | networkCache: 100, 59 | }; 60 | 61 | const playerEndpoint = await pipeline.create( 62 | 'PlayerEndpoint', 63 | playerOptions 64 | ); 65 | 66 | playerEndpoint.on('Error', pipelineError); 67 | 68 | return playerEndpoint; 69 | } 70 | 71 | // Create and setup the WebRTC endpoint 72 | async function createWebRtcEndpoint(sdpOffer) { 73 | webRtcEndpoint = await pipeline.create('WebRtcEndpoint'); 74 | 75 | webRtcEndpoint.on('Error', pipelineError); 76 | 77 | // If we already had ICE candidates queued, we add them to the WebRTC endpoint 78 | // We can safely assume there won't be candidates added to the queue while we empty it 79 | // since `webRtcEndpoint` has been set, so handleIceCandidate will directly send them to it 80 | await Promise.all( 81 | iceCandidatesQueue.map(candidate => 82 | webRtcEndpoint.addIceCandidate(candidate) 83 | ) 84 | ); 85 | 86 | // Ask Kurento to process the SDP offer in order to get an SDP answer 87 | const sdpAnswer = await webRtcEndpoint.processOffer(sdpOffer); 88 | 89 | // Send sdp answer to client 90 | client.send({ 91 | id: 'startResponse', 92 | sdpAnswer, 93 | }); 94 | 95 | // Start gathering local ICE candidates and send them to the client 96 | webRtcEndpoint.on('OnIceCandidate', event => { 97 | const candidate = IceCandidate(event.candidate); 98 | client.send({ 99 | id: 'iceCandidate', 100 | candidate, 101 | }); 102 | }); 103 | await webRtcEndpoint.gatherCandidates(); 104 | 105 | return webRtcEndpoint; 106 | } 107 | 108 | function handleIceCandidate(candidate) { 109 | const kurentoCandidate = IceCandidate(candidate); 110 | if (webRtcEndpoint) { 111 | console.info('Candidate received, forwarding to WebRTC endpoint'); 112 | webRtcEndpoint.addIceCandidate(kurentoCandidate); 113 | } else { 114 | console.info('Candidate received, queuing...'); 115 | // Push this IceCandidate into the queue 116 | iceCandidatesQueue.push(kurentoCandidate); 117 | } 118 | } 119 | 120 | // Release pipeline for this camera after a stream could not start 121 | function pipelineError(error) { 122 | console.error('Pipeline error:', error); 123 | client.send({ 124 | id: 'error', 125 | error: 'Pipeline error', 126 | }); 127 | stop(); 128 | } 129 | 130 | // Release pipeline 131 | function stop() { 132 | if (!pipeline) { 133 | return; 134 | } 135 | console.info('Releasing pipeline'); 136 | pipeline.release(); 137 | pipeline = null; 138 | webRtcEndpoint = null; 139 | iceCandidatesQueue = []; 140 | } 141 | 142 | return { 143 | start, 144 | stop, 145 | handleIceCandidate, 146 | }; 147 | }; 148 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = Server => options => { 4 | const { pingInterval = 10000, port = 8443 } = options; 5 | let connectionHandler; 6 | 7 | // Start the ws server 8 | const server = new Server({ port }); 9 | 10 | server.on('connection', ws => { 11 | if (!connectionHandler) { 12 | return; 13 | } 14 | 15 | console.log('New connection'); 16 | const client = createClient(ws); 17 | connectionHandler(client); 18 | }); 19 | 20 | function createClient(ws) { 21 | let messageHandler, errorHandler, closeHandler; 22 | 23 | // Starts pinging the client every `pingInterval`ms 24 | const stopKeepAlive = startKeepAlive(ws); 25 | 26 | ws.on('error', error => { 27 | if (errorHandler) { 28 | errorHandler(error); 29 | } 30 | }); 31 | 32 | ws.on('close', (code, reason) => { 33 | if (closeHandler) { 34 | closeHandler(code, reason); 35 | stopKeepAlive(); 36 | } 37 | }); 38 | 39 | ws.on('message', message => { 40 | if (messageHandler) { 41 | messageHandler(JSON.parse(message)); 42 | } 43 | }); 44 | 45 | return { 46 | onMessage(handler) { 47 | messageHandler = handler; 48 | }, 49 | onError(handler) { 50 | errorHandler = handler; 51 | }, 52 | onClose(handler) { 53 | closeHandler = handler; 54 | }, 55 | send(message) { 56 | ws.send(JSON.stringify(message)); 57 | }, 58 | }; 59 | } 60 | 61 | // Starts pinging a ws connection to make sure it stays alive 62 | function startKeepAlive(ws) { 63 | let isAlive = true; 64 | 65 | ws.on('pong', () => { 66 | isAlive = true; 67 | }); 68 | 69 | // Every `pingInterval` ms, check if socket is still alive and send a new ping 70 | const timer = setInterval(() => { 71 | if (!isAlive) { 72 | ws.terminate(); 73 | return; 74 | } 75 | isAlive = false; 76 | ws.ping(); 77 | }, pingInterval); 78 | 79 | // Return a function that stops the ping timer 80 | return () => { 81 | clearInterval(timer); 82 | }; 83 | } 84 | 85 | return { 86 | onConnection(handler) { 87 | connectionHandler = handler; 88 | }, 89 | }; 90 | }; -------------------------------------------------------------------------------- /webapp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | COPY nginx.conf /etc/nginx/nginx.conf 4 | COPY static /usr/share/nginx/html 5 | COPY build_and_run.sh /build_and_run.sh 6 | 7 | EXPOSE 80 8 | 9 | ENTRYPOINT ["/build_and_run.sh"] 10 | CMD ['/usr/sbin/nginx'] 11 | -------------------------------------------------------------------------------- /webapp/build_and_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -eo pipefail 5 | 6 | # Set default value of signaling URI if not in the environment 7 | if [ -z "$SIGNALING_URI" ]; then 8 | echo "No signaling URI defined. Using 0.0.0.0 by default." 9 | export SIGNALING_URI=0.0.0.0 10 | fi 11 | 12 | envsubst < /usr/share/nginx/html/src/index_tmpl.js > /usr/share/nginx/html/src/index.js 13 | 14 | /usr/sbin/nginx 15 | -------------------------------------------------------------------------------- /webapp/nginx.conf: -------------------------------------------------------------------------------- 1 | daemon off; 2 | 3 | events {} 4 | 5 | http { 6 | server { 7 | listen 80; 8 | server_name www.mysite.com mysite.com; 9 | error_log /var/log/nginx/error.log; 10 | 11 | root /usr/share/nginx/html; 12 | 13 | location / { 14 | include /etc/nginx/mime.types; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtcctv", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "postinstall": "cd static; bower install" 7 | }, 8 | "devDependencies": { 9 | "bower": "^1.4.1" 10 | } 11 | } -------------------------------------------------------------------------------- /webapp/static/assets/WebRTC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ullaakut/WebRTCCTV/b2005a1ed4bfe85639259f068ab84847229f4456/webapp/static/assets/WebRTC.png -------------------------------------------------------------------------------- /webapp/static/assets/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ullaakut/WebRTCCTV/b2005a1ed4bfe85639259f068ab84847229f4456/webapp/static/assets/spinner.gif -------------------------------------------------------------------------------- /webapp/static/assets/transparent-1px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ullaakut/WebRTCCTV/b2005a1ed4bfe85639259f068ab84847229f4456/webapp/static/assets/transparent-1px.png -------------------------------------------------------------------------------- /webapp/static/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtcctv", 3 | "description": "WebRTC signaling server and webapp that can stream RTSP cameras", 4 | "authors": [ 5 | "Brendan LE GLAUNEC - brendan.le-glaunec@epitech.eu" 6 | ], 7 | "main": "index.html", 8 | "moduleType": [ 9 | "globals" 10 | ], 11 | "private": true, 12 | "ignore": [ 13 | "**/.*", 14 | "node_modules", 15 | "bower_components", 16 | "test", 17 | "tests" 18 | ], 19 | "dependencies": { 20 | "adapter.js": "*", 21 | "bootstrap": "~3.3.0", 22 | "ekko-lightbox": "~3.3.0", 23 | "demo-console": "master", 24 | "kurento-utils": "master" 25 | } 26 | } -------------------------------------------------------------------------------- /webapp/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | WebRTCCTV 27 | 28 | 29 | 30 | 31 |
32 |
33 |
34 | 36 | 38 | 42 |
43 |
44 | 45 |
46 |
47 | 49 | 51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 |
59 |
    60 |
    61 |
    62 |
    63 | 64 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /webapp/static/src/index_tmpl.js: -------------------------------------------------------------------------------- 1 | var videoOutput; 2 | var webRtcPeer; 3 | var exitFullscreenButton; 4 | var fullscreenButton; 5 | var startButton; 6 | var stopButton; 7 | var pauseButton; 8 | var paused = false; 9 | var ws; 10 | 11 | // When document is loaded, initialize the script's variables 12 | // and event callbacks 13 | window.onload = function () { 14 | // Variables referencing elements 15 | exitFullscreenButton = $('#exitFullscreen'); 16 | startButton = $('#start'); 17 | stopButton = $('#stop'); 18 | fullscreenButton = $('#fullscreen'); 19 | pauseButton = $('#pause'); 20 | videoOutput = $('#videoOutput')[0]; 21 | 22 | // Create the console to write stuff in the interface 23 | // for easier debugging 24 | console = new Console(); 25 | 26 | // Bind event handlers to the buttons 27 | startButton.click(start); 28 | exitFullscreenButton.click(exitFullscreen); 29 | fullscreenButton.click(enterFullscreen); 30 | stopButton.click(stop); 31 | pauseButton.click(togglePause); 32 | 33 | document.getElementById('start').disabled = false; 34 | document.getElementById('stop').disabled = true; 35 | 36 | ws = new WebSocket('${SIGNALING_URI}'); 37 | 38 | // Event binder for when a message is received 39 | ws.onmessage = function (message) { 40 | var parsedMessage = JSON.parse(message.data); 41 | console.info('Received message: ' + message.data); 42 | 43 | switch (parsedMessage.id) { 44 | // The response the signaling server will send to a start request 45 | case 'startResponse': 46 | startResponse(parsedMessage); 47 | break; 48 | // The message the signaling server will send if an error occurs 49 | case 'error': 50 | onError('Error message from server: ' + parsedMessage.error); 51 | break; 52 | // The message the signaling server will send if a new iceCandidate is 53 | // received 54 | case 'iceCandidate': 55 | webRtcPeer.addIceCandidate(parsedMessage.candidate); 56 | break; 57 | // Unknown cases 58 | default: 59 | onError('Unrecognized message', parsedMessage); 60 | } 61 | }; 62 | 63 | // Create a webRtcPeer and initialize the media player 64 | // then proceeds to generate an SdpOffer that the signaling server 65 | // will forward to KMS 66 | function start() { 67 | showSpinner(videoOutput); 68 | 69 | console.log('Creating WebRtcPeer and generating local sdp offer ...'); 70 | var options = { 71 | remoteVideo: videoOutput, 72 | onicecandidate: onIceCandidate, 73 | mediaConstraints: { 74 | audio: false, 75 | video: true, 76 | }, 77 | }; 78 | 79 | webRtcPeer = 80 | kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, function (error) { 81 | if (error) return onError(error); 82 | console.info('Generating SDP offer...'); 83 | this.generateOffer(onOffer); 84 | console.info('SDP offer generated'); 85 | return null; 86 | }); 87 | 88 | document.getElementById('start').disabled = true; 89 | document.getElementById('stop').disabled = false; 90 | } 91 | 92 | // Send local candidates to the signaling server to be forwarded to KMS 93 | function onIceCandidate(candidate) { 94 | console.log('Local candidate' + JSON.stringify(candidate)); 95 | 96 | // force TURN candidates 97 | // if(candidate.candidate.indexOf('relay') === -1){ 98 | // return; 99 | // } 100 | 101 | var message = { 102 | id: 'onIceCandidate', 103 | candidate: candidate, 104 | }; 105 | sendMessage(message); 106 | } 107 | 108 | // Callback used to send the offer to the signaling server to be forwarded to 109 | // KMS 110 | function onOffer(error, offerSdp) { 111 | if (error) return onError(error); 112 | 113 | console.info('Invoking SDP offer callback function ' + location.host); 114 | var message = { 115 | id: 'start', 116 | rtspUri: 'rtsp://fake_camera:8554/live.sdp', 117 | sdpOffer: offerSdp, 118 | }; 119 | sendMessage(message); 120 | return null; 121 | } 122 | 123 | // Process the SDP answer from KMS in order to start playing the stream 124 | function startResponse(message) { 125 | console.log('SDP answer received from server. Processing ...'); 126 | webRtcPeer.processAnswer(message.sdpAnswer); 127 | } 128 | 129 | // Stops the stream and disconnects the WebRTCPeer from KMS 130 | function stop() { 131 | console.log('Stopping RTSP streaming...'); 132 | 133 | if (webRtcPeer) { 134 | webRtcPeer.dispose(); 135 | webRtcPeer = null; 136 | 137 | var message = { 138 | id: 'stop', 139 | }; 140 | sendMessage(message); 141 | } 142 | hideSpinner(videoOutput); 143 | 144 | document.getElementById('start').disabled = false; 145 | document.getElementById('stop').disabled = true; 146 | } 147 | 148 | // Helper to send messages through the WebSocket 149 | function sendMessage(message) { 150 | var jsonMessage = JSON.stringify(message); 151 | console.log('Sending message: ' + jsonMessage); 152 | ws.send(jsonMessage); 153 | } 154 | }; 155 | 156 | // Close the websocket when the window is closing 157 | window.onbeforeunload = function () { 158 | ws.close(); 159 | }; 160 | 161 | // Simply changes the CSS class of the media player to put it in fullscreen 162 | function enterFullscreen() { 163 | console.log('Starting fullscreen mode'); 164 | 165 | videoOutput.classList.add('fullscreen'); 166 | exitFullscreenButton.addClass('visible'); 167 | } 168 | 169 | // Simply resets the class of the media player to put it back in normal mode 170 | function exitFullscreen() { 171 | console.log('Stopping fullscreen mode'); 172 | 173 | videoOutput.classList.remove('fullscreen'); 174 | exitFullscreenButton.removeClass('visible'); 175 | } 176 | 177 | // Error callback 178 | function onError(error) { 179 | console.error(error); 180 | } 181 | 182 | // Hide the background of the video and shows a spinner instead 183 | // Currently we don't have a spinner 184 | function showSpinner() { 185 | for (var i = 0; i < arguments.length; i++) { 186 | arguments[i].poster = './assets/transparent-1px.png'; 187 | } 188 | $('.loader').show(); 189 | } 190 | 191 | // Shows the background again and hides the spinner 192 | function hideSpinner() { 193 | for (var i = 0; i < arguments.length; i++) { 194 | arguments[i].src = ''; 195 | arguments[i].poster = './assets/WebRTC.png'; 196 | } 197 | $('.loader').hide(); 198 | } 199 | 200 | // Pauses the media player from receiving the stream 201 | // Unpauses if it was paused. 202 | function togglePause() { 203 | if (!paused) { 204 | webRtcPeer.remoteVideo.pause(); 205 | console.info('Video paused.'); 206 | } else { 207 | webRtcPeer.remoteVideo.load(); 208 | console.info('Video unpaused.'); 209 | } 210 | paused = !paused; 211 | } 212 | -------------------------------------------------------------------------------- /webapp/static/style/webrtcctv.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 40px; 8 | background: #2c2c2c; 9 | color: #FFF; 10 | } 11 | 12 | video, 13 | #console { 14 | display: block; 15 | font-size: 14px; 16 | line-height: 1.42857143; 17 | border: 1px solid #ccc; 18 | border-radius: 4px; 19 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 20 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 21 | -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 22 | transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 23 | } 24 | 25 | .form-group { 26 | display: inline-block; 27 | margin: 0 0 0 25px; 28 | } 29 | 30 | .form-group label { 31 | margin-right: 10px; 32 | } 33 | 34 | .form-group select { 35 | display: inline-block; 36 | width: auto; 37 | } 38 | 39 | #console { 40 | min-height: 120px; 41 | max-height: 360px; 42 | background: #3c3c3c; 43 | } 44 | 45 | li.log span[data-type="string"] { 46 | color: #FFF; 47 | } 48 | 49 | li.info span[data-type="string"] { 50 | color: #4286f4; 51 | font-style: italic; 52 | } 53 | 54 | li.error span[data-type="string"] { 55 | color: #F00; 56 | font-weight: 900; 57 | } 58 | 59 | .col-md-2 { 60 | width: 80px; 61 | padding-top: 190px; 62 | } 63 | 64 | #exitFullscreen { 65 | display: none; 66 | position: absolute; 67 | top: 1px; 68 | right: 1px; 69 | z-index: 9999; 70 | } 71 | 72 | #videoOutput.fullscreen { 73 | width: 100%; 74 | height: 100%; 75 | position: absolute; 76 | top: 0; 77 | left: 0; 78 | z-index: 9998; 79 | background: #000; 80 | } 81 | 82 | #exitFullscreen.visible { 83 | display: block; 84 | } 85 | 86 | .btn-group .btn { 87 | margin-right: 2px !important; 88 | } 89 | 90 | #videoOutput:-moz-full-screen { 91 | position: fixed; 92 | display: inline; 93 | z-index: 1; 94 | } 95 | 96 | #videoOutput:-ms-full-screen { 97 | position: fixed; 98 | display: inline; 99 | z-index: 1; 100 | } 101 | 102 | #videoOutput:-webkit-full-screen { 103 | position: fixed; 104 | display: inline; 105 | z-index: 1; 106 | } 107 | 108 | .btn-outline { 109 | background-color: transparent; 110 | color: inherit; 111 | transition: all .5s; 112 | } 113 | 114 | .btn-primary.btn-outline { 115 | color: #428bca; 116 | } 117 | 118 | .btn-success.btn-outline { 119 | color: #5cb85c; 120 | } 121 | 122 | .btn-info.btn-outline { 123 | color: #5bc0de; 124 | } 125 | 126 | .btn-warning.btn-outline { 127 | color: #f0ad4e; 128 | } 129 | 130 | .btn-danger.btn-outline { 131 | color: #d9534f; 132 | } 133 | 134 | .btn-primary.btn-outline:hover, 135 | .btn-success.btn-outline:hover, 136 | .btn-info.btn-outline:hover, 137 | .btn-warning.btn-outline:hover, 138 | .btn-danger.btn-outline:hover { 139 | color: #fff; 140 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | acorn-jsx@^3.0.0: 6 | version "3.0.1" 7 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 8 | dependencies: 9 | acorn "^3.0.4" 10 | 11 | acorn@^3.0.4: 12 | version "3.3.0" 13 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 14 | 15 | acorn@^5.0.1: 16 | version "5.0.3" 17 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" 18 | 19 | ajv-keywords@^1.0.0: 20 | version "1.5.1" 21 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" 22 | 23 | ajv@^4.7.0: 24 | version "4.11.8" 25 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" 26 | dependencies: 27 | co "^4.6.0" 28 | json-stable-stringify "^1.0.1" 29 | 30 | ansi-escapes@^1.1.0: 31 | version "1.4.0" 32 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" 33 | 34 | ansi-regex@^2.0.0: 35 | version "2.1.1" 36 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 37 | 38 | ansi-styles@^2.2.1: 39 | version "2.2.1" 40 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 41 | 42 | argparse@^1.0.7: 43 | version "1.0.9" 44 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" 45 | dependencies: 46 | sprintf-js "~1.0.2" 47 | 48 | array-union@^1.0.1: 49 | version "1.0.2" 50 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 51 | dependencies: 52 | array-uniq "^1.0.1" 53 | 54 | array-uniq@^1.0.1: 55 | version "1.0.3" 56 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 57 | 58 | arrify@^1.0.0: 59 | version "1.0.1" 60 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 61 | 62 | asap@~2.0.3: 63 | version "2.0.5" 64 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" 65 | 66 | async@~2.0.1: 67 | version "2.0.1" 68 | resolved "https://registry.yarnpkg.com/async/-/async-2.0.1.tgz#b709cc0280a9c36f09f4536be823c838a9049e25" 69 | dependencies: 70 | lodash "^4.8.0" 71 | 72 | babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: 73 | version "6.22.0" 74 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" 75 | dependencies: 76 | chalk "^1.1.0" 77 | esutils "^2.0.2" 78 | js-tokens "^3.0.0" 79 | 80 | babel-eslint@^7.2.3: 81 | version "7.2.3" 82 | resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" 83 | dependencies: 84 | babel-code-frame "^6.22.0" 85 | babel-traverse "^6.23.1" 86 | babel-types "^6.23.0" 87 | babylon "^6.17.0" 88 | 89 | babel-messages@^6.23.0: 90 | version "6.23.0" 91 | resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" 92 | dependencies: 93 | babel-runtime "^6.22.0" 94 | 95 | babel-runtime@^6.22.0: 96 | version "6.23.0" 97 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" 98 | dependencies: 99 | core-js "^2.4.0" 100 | regenerator-runtime "^0.10.0" 101 | 102 | babel-traverse@^6.23.1: 103 | version "6.24.1" 104 | resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" 105 | dependencies: 106 | babel-code-frame "^6.22.0" 107 | babel-messages "^6.23.0" 108 | babel-runtime "^6.22.0" 109 | babel-types "^6.24.1" 110 | babylon "^6.15.0" 111 | debug "^2.2.0" 112 | globals "^9.0.0" 113 | invariant "^2.2.0" 114 | lodash "^4.2.0" 115 | 116 | babel-types@^6.23.0, babel-types@^6.24.1: 117 | version "6.24.1" 118 | resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" 119 | dependencies: 120 | babel-runtime "^6.22.0" 121 | esutils "^2.0.2" 122 | lodash "^4.2.0" 123 | to-fast-properties "^1.0.1" 124 | 125 | babylon@^6.15.0, babylon@^6.17.0: 126 | version "6.17.1" 127 | resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.1.tgz#17f14fddf361b695981fe679385e4f1c01ebd86f" 128 | 129 | backoff@~2.3.0: 130 | version "2.3.0" 131 | resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.3.0.tgz#ee7c7e38093f92e472859db635e7652454fc21ea" 132 | 133 | balanced-match@^0.4.1: 134 | version "0.4.2" 135 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 136 | 137 | bindings@1.2.x, bindings@~1.2.1: 138 | version "1.2.1" 139 | resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" 140 | 141 | brace-expansion@^1.1.7: 142 | version "1.1.7" 143 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" 144 | dependencies: 145 | balanced-match "^0.4.1" 146 | concat-map "0.0.1" 147 | 148 | buffer-shims@~1.0.0: 149 | version "1.0.0" 150 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 151 | 152 | bufferutil@1.1.x: 153 | version "1.1.0" 154 | resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-1.1.0.tgz#3f96be77a7f8652ff69ef29f1cabc8b89f7fd972" 155 | dependencies: 156 | bindings "1.2.x" 157 | nan "1.8.x" 158 | 159 | bufferutil@1.2.x: 160 | version "1.2.1" 161 | resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-1.2.1.tgz#37be5d36e1e06492221e68d474b1ac58e510cbd7" 162 | dependencies: 163 | bindings "1.2.x" 164 | nan "^2.0.5" 165 | 166 | builtin-modules@^1.1.1: 167 | version "1.1.1" 168 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 169 | 170 | caller-path@^0.1.0: 171 | version "0.1.0" 172 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 173 | dependencies: 174 | callsites "^0.2.0" 175 | 176 | callsites@^0.2.0: 177 | version "0.2.0" 178 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 179 | 180 | chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: 181 | version "1.1.3" 182 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 183 | dependencies: 184 | ansi-styles "^2.2.1" 185 | escape-string-regexp "^1.0.2" 186 | has-ansi "^2.0.0" 187 | strip-ansi "^3.0.0" 188 | supports-color "^2.0.0" 189 | 190 | circular-json@^0.3.1: 191 | version "0.3.1" 192 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" 193 | 194 | cli-cursor@^1.0.1: 195 | version "1.0.2" 196 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" 197 | dependencies: 198 | restore-cursor "^1.0.1" 199 | 200 | cli-width@^2.0.0: 201 | version "2.1.0" 202 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" 203 | 204 | co@^4.6.0: 205 | version "4.6.0" 206 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 207 | 208 | code-point-at@^1.0.0: 209 | version "1.1.0" 210 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 211 | 212 | commander@~2.1.0: 213 | version "2.1.0" 214 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" 215 | 216 | concat-map@0.0.1: 217 | version "0.0.1" 218 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 219 | 220 | concat-stream@^1.5.2: 221 | version "1.6.0" 222 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" 223 | dependencies: 224 | inherits "^2.0.3" 225 | readable-stream "^2.2.2" 226 | typedarray "^0.0.6" 227 | 228 | contains-path@^0.1.0: 229 | version "0.1.0" 230 | resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" 231 | 232 | core-js@^2.4.0: 233 | version "2.4.1" 234 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" 235 | 236 | core-util-is@~1.0.0: 237 | version "1.0.2" 238 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 239 | 240 | d@1: 241 | version "1.0.0" 242 | resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" 243 | dependencies: 244 | es5-ext "^0.10.9" 245 | 246 | debug@2.2.0: 247 | version "2.2.0" 248 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 249 | dependencies: 250 | ms "0.7.1" 251 | 252 | debug@^2.1.1: 253 | version "2.1.3" 254 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.1.3.tgz#ce8ab1b5ee8fbee2bfa3b633cab93d366b63418e" 255 | dependencies: 256 | ms "0.7.0" 257 | 258 | debug@^2.2.0: 259 | version "2.6.8" 260 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 261 | dependencies: 262 | ms "2.0.0" 263 | 264 | deep-is@~0.1.3: 265 | version "0.1.3" 266 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 267 | 268 | del@^2.0.2: 269 | version "2.2.2" 270 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 271 | dependencies: 272 | globby "^5.0.0" 273 | is-path-cwd "^1.0.0" 274 | is-path-in-cwd "^1.0.0" 275 | object-assign "^4.0.1" 276 | pify "^2.0.0" 277 | pinkie-promise "^2.0.0" 278 | rimraf "^2.2.8" 279 | 280 | doctrine@1.5.0: 281 | version "1.5.0" 282 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" 283 | dependencies: 284 | esutils "^2.0.2" 285 | isarray "^1.0.0" 286 | 287 | doctrine@^2.0.0: 288 | version "2.0.0" 289 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" 290 | dependencies: 291 | esutils "^2.0.2" 292 | isarray "^1.0.0" 293 | 294 | encoding@^0.1.11: 295 | version "0.1.12" 296 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" 297 | dependencies: 298 | iconv-lite "~0.4.13" 299 | 300 | error-tojson@0.0.1: 301 | version "0.0.1" 302 | resolved "https://registry.yarnpkg.com/error-tojson/-/error-tojson-0.0.1.tgz#a7b1aa94ffc00e9078c2eba26e204bd87cf2cbb9" 303 | 304 | es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: 305 | version "0.10.20" 306 | resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.20.tgz#72a9b4fd5832797ba1bb65dceb2e25c04241c492" 307 | dependencies: 308 | es6-iterator "2" 309 | es6-symbol "~3.1" 310 | 311 | es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: 312 | version "2.0.1" 313 | resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" 314 | dependencies: 315 | d "1" 316 | es5-ext "^0.10.14" 317 | es6-symbol "^3.1" 318 | 319 | es6-map@^0.1.3: 320 | version "0.1.5" 321 | resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" 322 | dependencies: 323 | d "1" 324 | es5-ext "~0.10.14" 325 | es6-iterator "~2.0.1" 326 | es6-set "~0.1.5" 327 | es6-symbol "~3.1.1" 328 | event-emitter "~0.3.5" 329 | 330 | es6-promise@^4.0.5: 331 | version "4.1.0" 332 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.0.tgz#dda03ca8f9f89bc597e689842929de7ba8cebdf0" 333 | 334 | es6-set@~0.1.5: 335 | version "0.1.5" 336 | resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" 337 | dependencies: 338 | d "1" 339 | es5-ext "~0.10.14" 340 | es6-iterator "~2.0.1" 341 | es6-symbol "3.1.1" 342 | event-emitter "~0.3.5" 343 | 344 | es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: 345 | version "3.1.1" 346 | resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" 347 | dependencies: 348 | d "1" 349 | es5-ext "~0.10.14" 350 | 351 | es6-weak-map@^2.0.1: 352 | version "2.0.2" 353 | resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" 354 | dependencies: 355 | d "1" 356 | es5-ext "^0.10.14" 357 | es6-iterator "^2.0.1" 358 | es6-symbol "^3.1.1" 359 | 360 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 361 | version "1.0.5" 362 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 363 | 364 | escope@^3.6.0: 365 | version "3.6.0" 366 | resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" 367 | dependencies: 368 | es6-map "^0.1.3" 369 | es6-weak-map "^2.0.1" 370 | esrecurse "^4.1.0" 371 | estraverse "^4.1.1" 372 | 373 | eslint-config-airbnb-base@^11.2.0: 374 | version "11.2.0" 375 | resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.2.0.tgz#19a9dc4481a26f70904545ec040116876018f853" 376 | 377 | eslint-import-resolver-node@^0.2.0: 378 | version "0.2.3" 379 | resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" 380 | dependencies: 381 | debug "^2.2.0" 382 | object-assign "^4.0.1" 383 | resolve "^1.1.6" 384 | 385 | eslint-module-utils@^2.0.0: 386 | version "2.0.0" 387 | resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz#a6f8c21d901358759cdc35dbac1982ae1ee58bce" 388 | dependencies: 389 | debug "2.2.0" 390 | pkg-dir "^1.0.0" 391 | 392 | eslint-plugin-import@^2.2.0: 393 | version "2.2.0" 394 | resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz#72ba306fad305d67c4816348a4699a4229ac8b4e" 395 | dependencies: 396 | builtin-modules "^1.1.1" 397 | contains-path "^0.1.0" 398 | debug "^2.2.0" 399 | doctrine "1.5.0" 400 | eslint-import-resolver-node "^0.2.0" 401 | eslint-module-utils "^2.0.0" 402 | has "^1.0.1" 403 | lodash.cond "^4.3.0" 404 | minimatch "^3.0.3" 405 | pkg-up "^1.0.0" 406 | 407 | eslint@^3.19.0: 408 | version "3.19.0" 409 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" 410 | dependencies: 411 | babel-code-frame "^6.16.0" 412 | chalk "^1.1.3" 413 | concat-stream "^1.5.2" 414 | debug "^2.1.1" 415 | doctrine "^2.0.0" 416 | escope "^3.6.0" 417 | espree "^3.4.0" 418 | esquery "^1.0.0" 419 | estraverse "^4.2.0" 420 | esutils "^2.0.2" 421 | file-entry-cache "^2.0.0" 422 | glob "^7.0.3" 423 | globals "^9.14.0" 424 | ignore "^3.2.0" 425 | imurmurhash "^0.1.4" 426 | inquirer "^0.12.0" 427 | is-my-json-valid "^2.10.0" 428 | is-resolvable "^1.0.0" 429 | js-yaml "^3.5.1" 430 | json-stable-stringify "^1.0.0" 431 | levn "^0.3.0" 432 | lodash "^4.0.0" 433 | mkdirp "^0.5.0" 434 | natural-compare "^1.4.0" 435 | optionator "^0.8.2" 436 | path-is-inside "^1.0.1" 437 | pluralize "^1.2.1" 438 | progress "^1.1.8" 439 | require-uncached "^1.0.2" 440 | shelljs "^0.7.5" 441 | strip-bom "^3.0.0" 442 | strip-json-comments "~2.0.1" 443 | table "^3.7.8" 444 | text-table "~0.2.0" 445 | user-home "^2.0.0" 446 | 447 | espree@^3.4.0: 448 | version "3.4.3" 449 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374" 450 | dependencies: 451 | acorn "^5.0.1" 452 | acorn-jsx "^3.0.0" 453 | 454 | esprima@^3.1.1: 455 | version "3.1.3" 456 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" 457 | 458 | esquery@^1.0.0: 459 | version "1.0.0" 460 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" 461 | dependencies: 462 | estraverse "^4.0.0" 463 | 464 | esrecurse@^4.1.0: 465 | version "4.1.0" 466 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" 467 | dependencies: 468 | estraverse "~4.1.0" 469 | object-assign "^4.0.1" 470 | 471 | estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0: 472 | version "4.2.0" 473 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 474 | 475 | estraverse@~4.1.0: 476 | version "4.1.1" 477 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" 478 | 479 | esutils@^2.0.2: 480 | version "2.0.2" 481 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 482 | 483 | event-emitter@~0.3.5: 484 | version "0.3.5" 485 | resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" 486 | dependencies: 487 | d "1" 488 | es5-ext "~0.10.14" 489 | 490 | eventsource@~0.1.6: 491 | version "0.1.6" 492 | resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" 493 | dependencies: 494 | original ">=0.0.5" 495 | 496 | exit-hook@^1.0.0: 497 | version "1.1.1" 498 | resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" 499 | 500 | extend@^3.0.0: 501 | version "3.0.1" 502 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" 503 | 504 | fast-levenshtein@~2.0.4: 505 | version "2.0.6" 506 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 507 | 508 | faye-websocket@~0.11.0: 509 | version "0.11.1" 510 | resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" 511 | dependencies: 512 | websocket-driver ">=0.5.1" 513 | 514 | figures@^1.3.5: 515 | version "1.7.0" 516 | resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" 517 | dependencies: 518 | escape-string-regexp "^1.0.5" 519 | object-assign "^4.1.0" 520 | 521 | file-entry-cache@^2.0.0: 522 | version "2.0.0" 523 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 524 | dependencies: 525 | flat-cache "^1.2.1" 526 | object-assign "^4.0.1" 527 | 528 | find-up@^1.0.0: 529 | version "1.1.2" 530 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 531 | dependencies: 532 | path-exists "^2.0.0" 533 | pinkie-promise "^2.0.0" 534 | 535 | flat-cache@^1.2.1: 536 | version "1.2.2" 537 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" 538 | dependencies: 539 | circular-json "^0.3.1" 540 | del "^2.0.2" 541 | graceful-fs "^4.1.2" 542 | write "^0.2.1" 543 | 544 | fs.realpath@^1.0.0: 545 | version "1.0.0" 546 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 547 | 548 | function-bind@^1.0.2: 549 | version "1.1.0" 550 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" 551 | 552 | generate-function@^2.0.0: 553 | version "2.0.0" 554 | resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" 555 | 556 | generate-object-property@^1.1.0: 557 | version "1.2.0" 558 | resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" 559 | dependencies: 560 | is-property "^1.0.0" 561 | 562 | glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: 563 | version "7.1.1" 564 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 565 | dependencies: 566 | fs.realpath "^1.0.0" 567 | inflight "^1.0.4" 568 | inherits "2" 569 | minimatch "^3.0.2" 570 | once "^1.3.0" 571 | path-is-absolute "^1.0.0" 572 | 573 | globals@^9.0.0, globals@^9.14.0: 574 | version "9.17.0" 575 | resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" 576 | 577 | globby@^5.0.0: 578 | version "5.0.0" 579 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 580 | dependencies: 581 | array-union "^1.0.1" 582 | arrify "^1.0.0" 583 | glob "^7.0.3" 584 | object-assign "^4.0.1" 585 | pify "^2.0.0" 586 | pinkie-promise "^2.0.0" 587 | 588 | graceful-fs@^4.1.2: 589 | version "4.1.11" 590 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 591 | 592 | has-ansi@^2.0.0: 593 | version "2.0.0" 594 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 595 | dependencies: 596 | ansi-regex "^2.0.0" 597 | 598 | has@^1.0.1: 599 | version "1.0.1" 600 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" 601 | dependencies: 602 | function-bind "^1.0.2" 603 | 604 | iconv-lite@~0.4.13: 605 | version "0.4.17" 606 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d" 607 | 608 | ignore@^3.2.0: 609 | version "3.3.0" 610 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.0.tgz#3812d22cbe9125f2c2b4915755a1b8abd745a001" 611 | 612 | imurmurhash@^0.1.4: 613 | version "0.1.4" 614 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 615 | 616 | inflight@^1.0.4: 617 | version "1.0.6" 618 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 619 | dependencies: 620 | once "^1.3.0" 621 | wrappy "1" 622 | 623 | inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: 624 | version "2.0.3" 625 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 626 | 627 | inquirer@^0.12.0: 628 | version "0.12.0" 629 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" 630 | dependencies: 631 | ansi-escapes "^1.1.0" 632 | ansi-regex "^2.0.0" 633 | chalk "^1.0.0" 634 | cli-cursor "^1.0.1" 635 | cli-width "^2.0.0" 636 | figures "^1.3.5" 637 | lodash "^4.3.0" 638 | readline2 "^1.0.1" 639 | run-async "^0.1.0" 640 | rx-lite "^3.1.2" 641 | string-width "^1.0.1" 642 | strip-ansi "^3.0.0" 643 | through "^2.3.6" 644 | 645 | interpret@^1.0.0: 646 | version "1.0.3" 647 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" 648 | 649 | invariant@^2.2.0: 650 | version "2.2.2" 651 | resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" 652 | dependencies: 653 | loose-envify "^1.0.0" 654 | 655 | is-fullwidth-code-point@^1.0.0: 656 | version "1.0.0" 657 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 658 | dependencies: 659 | number-is-nan "^1.0.0" 660 | 661 | is-fullwidth-code-point@^2.0.0: 662 | version "2.0.0" 663 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 664 | 665 | is-my-json-valid@^2.10.0: 666 | version "2.16.0" 667 | resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" 668 | dependencies: 669 | generate-function "^2.0.0" 670 | generate-object-property "^1.1.0" 671 | jsonpointer "^4.0.0" 672 | xtend "^4.0.0" 673 | 674 | is-path-cwd@^1.0.0: 675 | version "1.0.0" 676 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 677 | 678 | is-path-in-cwd@^1.0.0: 679 | version "1.0.0" 680 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" 681 | dependencies: 682 | is-path-inside "^1.0.0" 683 | 684 | is-path-inside@^1.0.0: 685 | version "1.0.0" 686 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" 687 | dependencies: 688 | path-is-inside "^1.0.1" 689 | 690 | is-property@^1.0.0: 691 | version "1.0.2" 692 | resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" 693 | 694 | is-resolvable@^1.0.0: 695 | version "1.0.0" 696 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" 697 | dependencies: 698 | tryit "^1.0.1" 699 | 700 | is-stream@^1.0.1: 701 | version "1.1.0" 702 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 703 | 704 | isarray@^1.0.0, isarray@~1.0.0: 705 | version "1.0.0" 706 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 707 | 708 | isbuffer@0.0.0: 709 | version "0.0.0" 710 | resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b" 711 | 712 | js-tokens@^3.0.0: 713 | version "3.0.1" 714 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" 715 | 716 | js-yaml@^3.5.1: 717 | version "3.8.4" 718 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" 719 | dependencies: 720 | argparse "^1.0.7" 721 | esprima "^3.1.1" 722 | 723 | json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: 724 | version "1.0.1" 725 | resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" 726 | dependencies: 727 | jsonify "~0.0.0" 728 | 729 | json3@^3.3.2: 730 | version "3.3.2" 731 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 732 | 733 | jsonify@~0.0.0: 734 | version "0.0.0" 735 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 736 | 737 | jsonpointer@^4.0.0: 738 | version "4.0.1" 739 | resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" 740 | 741 | kurento-client-core@Kurento/kurento-client-core-js#master: 742 | version "6.6.2-dev" 743 | resolved "https://codeload.github.com/Kurento/kurento-client-core-js/tar.gz/2160f8e6938f138b52b72a5c5c354d1e5fce1ca0" 744 | 745 | kurento-client-elements@Kurento/kurento-client-elements-js#master: 746 | version "6.6.2-dev" 747 | resolved "https://codeload.github.com/Kurento/kurento-client-elements-js/tar.gz/cbd1ff67fbf0faddc9f6f266bb33e449bc9e1f81" 748 | 749 | kurento-client-filters@Kurento/kurento-client-filters-js#master: 750 | version "6.6.2-dev" 751 | resolved "https://codeload.github.com/Kurento/kurento-client-filters-js/tar.gz/51308da53e432a2db9559dcdb308d87951417bf0" 752 | 753 | kurento-client@Kurento/kurento-client-js: 754 | version "6.6.1-dev" 755 | resolved "https://codeload.github.com/Kurento/kurento-client-js/tar.gz/efb160e85a4b1f376307fe1979c9fbcb5f978393" 756 | dependencies: 757 | async "~2.0.1" 758 | error-tojson "0.0.1" 759 | es6-promise "^4.0.5" 760 | extend "^3.0.0" 761 | inherits "~2.0.3" 762 | kurento-client-core Kurento/kurento-client-core-js#master 763 | kurento-client-elements Kurento/kurento-client-elements-js#master 764 | kurento-client-filters Kurento/kurento-client-filters-js#master 765 | kurento-jsonrpc Kurento/kurento-jsonrpc-js#master 766 | minimist "^1.2.0" 767 | promise "7.1.1" 768 | promisecallback "0.0.4" 769 | reconnect-ws KurentoForks/reconnect-ws#master 770 | 771 | kurento-jsonrpc@Kurento/kurento-jsonrpc-js#master: 772 | version "5.1.4-dev" 773 | resolved "https://codeload.github.com/Kurento/kurento-jsonrpc-js/tar.gz/b60e99188fe6ecb2c3dc2e40880599dd04b1056e" 774 | dependencies: 775 | bufferutil "1.2.x" 776 | inherits "^2.0.1" 777 | sockjs-client "1.1.1" 778 | utf-8-validate "1.2.x" 779 | ws "^1.1.1" 780 | 781 | levn@^0.3.0, levn@~0.3.0: 782 | version "0.3.0" 783 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 784 | dependencies: 785 | prelude-ls "~1.1.2" 786 | type-check "~0.3.2" 787 | 788 | lodash.cond@^4.3.0: 789 | version "4.5.2" 790 | resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" 791 | 792 | lodash@^4.0.0, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.8.0: 793 | version "4.17.4" 794 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 795 | 796 | loose-envify@^1.0.0: 797 | version "1.3.1" 798 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 799 | dependencies: 800 | js-tokens "^3.0.0" 801 | 802 | minimatch@^3.0.2, minimatch@^3.0.3: 803 | version "3.0.4" 804 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 805 | dependencies: 806 | brace-expansion "^1.1.7" 807 | 808 | minimist@0.0.8: 809 | version "0.0.8" 810 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 811 | 812 | minimist@^1.2.0: 813 | version "1.2.0" 814 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 815 | 816 | mkdirp@^0.5.0, mkdirp@^0.5.1: 817 | version "0.5.1" 818 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 819 | dependencies: 820 | minimist "0.0.8" 821 | 822 | ms@0.7.0: 823 | version "0.7.0" 824 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" 825 | 826 | ms@0.7.1: 827 | version "0.7.1" 828 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 829 | 830 | ms@2.0.0: 831 | version "2.0.0" 832 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 833 | 834 | mute-stream@0.0.5: 835 | version "0.0.5" 836 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" 837 | 838 | nan@1.8.x: 839 | version "1.8.4" 840 | resolved "https://registry.yarnpkg.com/nan/-/nan-1.8.4.tgz#3c76b5382eab33e44b758d2813ca9d92e9342f34" 841 | 842 | nan@^2.0.5: 843 | version "2.6.2" 844 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" 845 | 846 | nan@~1.0.0: 847 | version "1.0.0" 848 | resolved "https://registry.yarnpkg.com/nan/-/nan-1.0.0.tgz#ae24f8850818d662fcab5acf7f3b95bfaa2ccf38" 849 | 850 | nan@~2.4.0: 851 | version "2.4.0" 852 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" 853 | 854 | natural-compare@^1.4.0: 855 | version "1.4.0" 856 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 857 | 858 | node-fetch@^1.7.0: 859 | version "1.7.0" 860 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.0.tgz#3ff6c56544f9b7fb00682338bb55ee6f54a8a0ef" 861 | dependencies: 862 | encoding "^0.1.11" 863 | is-stream "^1.0.1" 864 | 865 | number-is-nan@^1.0.0: 866 | version "1.0.1" 867 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 868 | 869 | object-assign@^4.0.1, object-assign@^4.1.0: 870 | version "4.1.1" 871 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 872 | 873 | once@^1.3.0: 874 | version "1.4.0" 875 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 876 | dependencies: 877 | wrappy "1" 878 | 879 | onetime@^1.0.0: 880 | version "1.1.0" 881 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" 882 | 883 | optionator@^0.8.2: 884 | version "0.8.2" 885 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 886 | dependencies: 887 | deep-is "~0.1.3" 888 | fast-levenshtein "~2.0.4" 889 | levn "~0.3.0" 890 | prelude-ls "~1.1.2" 891 | type-check "~0.3.2" 892 | wordwrap "~1.0.0" 893 | 894 | options@>=0.0.5: 895 | version "0.0.6" 896 | resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" 897 | 898 | original@>=0.0.5: 899 | version "1.0.0" 900 | resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" 901 | dependencies: 902 | url-parse "1.0.x" 903 | 904 | os-homedir@^1.0.0: 905 | version "1.0.2" 906 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 907 | 908 | path-exists@^2.0.0: 909 | version "2.1.0" 910 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 911 | dependencies: 912 | pinkie-promise "^2.0.0" 913 | 914 | path-is-absolute@^1.0.0: 915 | version "1.0.1" 916 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 917 | 918 | path-is-inside@^1.0.1: 919 | version "1.0.2" 920 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 921 | 922 | path-parse@^1.0.5: 923 | version "1.0.5" 924 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" 925 | 926 | pify@^2.0.0: 927 | version "2.3.0" 928 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 929 | 930 | pinkie-promise@^2.0.0: 931 | version "2.0.1" 932 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 933 | dependencies: 934 | pinkie "^2.0.0" 935 | 936 | pinkie@^2.0.0: 937 | version "2.0.4" 938 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 939 | 940 | pkg-dir@^1.0.0: 941 | version "1.0.0" 942 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" 943 | dependencies: 944 | find-up "^1.0.0" 945 | 946 | pkg-up@^1.0.0: 947 | version "1.0.0" 948 | resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" 949 | dependencies: 950 | find-up "^1.0.0" 951 | 952 | pluralize@^1.2.1: 953 | version "1.2.1" 954 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" 955 | 956 | prelude-ls@~1.1.2: 957 | version "1.1.2" 958 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 959 | 960 | process-nextick-args@~1.0.6: 961 | version "1.0.7" 962 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 963 | 964 | progress@^1.1.8: 965 | version "1.1.8" 966 | resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" 967 | 968 | promise@7.1.1: 969 | version "7.1.1" 970 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" 971 | dependencies: 972 | asap "~2.0.3" 973 | 974 | promisecallback@0.0.4: 975 | version "0.0.4" 976 | resolved "https://registry.yarnpkg.com/promisecallback/-/promisecallback-0.0.4.tgz#b934f13c04e443622b4d66de4e42ea5f6ce66e74" 977 | 978 | querystringify@0.0.x: 979 | version "0.0.4" 980 | resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" 981 | 982 | querystringify@~1.0.0: 983 | version "1.0.0" 984 | resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" 985 | 986 | readable-stream@^2.2.2: 987 | version "2.2.9" 988 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" 989 | dependencies: 990 | buffer-shims "~1.0.0" 991 | core-util-is "~1.0.0" 992 | inherits "~2.0.1" 993 | isarray "~1.0.0" 994 | process-nextick-args "~1.0.6" 995 | string_decoder "~1.0.0" 996 | util-deprecate "~1.0.1" 997 | 998 | readline2@^1.0.1: 999 | version "1.0.1" 1000 | resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" 1001 | dependencies: 1002 | code-point-at "^1.0.0" 1003 | is-fullwidth-code-point "^1.0.0" 1004 | mute-stream "0.0.5" 1005 | 1006 | rechoir@^0.6.2: 1007 | version "0.6.2" 1008 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 1009 | dependencies: 1010 | resolve "^1.1.6" 1011 | 1012 | reconnect-core@KurentoForks/reconnect-core: 1013 | version "1.0.0" 1014 | resolved "https://codeload.github.com/KurentoForks/reconnect-core/tar.gz/921d43e91578abb2fb2613f585c010c1939cf734" 1015 | dependencies: 1016 | backoff "~2.3.0" 1017 | 1018 | reconnect-ws@KurentoForks/reconnect-ws#master: 1019 | version "0.0.0" 1020 | resolved "https://codeload.github.com/KurentoForks/reconnect-ws/tar.gz/f287385d75861654528c352e60221f95c9209f8a" 1021 | dependencies: 1022 | reconnect-core KurentoForks/reconnect-core 1023 | websocket-stream "~0.5.1" 1024 | 1025 | regenerator-runtime@^0.10.0: 1026 | version "0.10.5" 1027 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" 1028 | 1029 | require-uncached@^1.0.2: 1030 | version "1.0.3" 1031 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 1032 | dependencies: 1033 | caller-path "^0.1.0" 1034 | resolve-from "^1.0.0" 1035 | 1036 | requires-port@1.0.x: 1037 | version "1.0.0" 1038 | resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" 1039 | 1040 | resolve-from@^1.0.0: 1041 | version "1.0.1" 1042 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 1043 | 1044 | resolve@^1.1.6: 1045 | version "1.3.3" 1046 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" 1047 | dependencies: 1048 | path-parse "^1.0.5" 1049 | 1050 | restore-cursor@^1.0.1: 1051 | version "1.0.1" 1052 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" 1053 | dependencies: 1054 | exit-hook "^1.0.0" 1055 | onetime "^1.0.0" 1056 | 1057 | rimraf@^2.2.8: 1058 | version "2.6.1" 1059 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" 1060 | dependencies: 1061 | glob "^7.0.5" 1062 | 1063 | run-async@^0.1.0: 1064 | version "0.1.0" 1065 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" 1066 | dependencies: 1067 | once "^1.3.0" 1068 | 1069 | rx-lite@^3.1.2: 1070 | version "3.1.2" 1071 | resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" 1072 | 1073 | safe-buffer@^5.0.1: 1074 | version "5.0.1" 1075 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" 1076 | 1077 | shelljs@^0.7.5: 1078 | version "0.7.7" 1079 | resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.7.tgz#b2f5c77ef97148f4b4f6e22682e10bba8667cff1" 1080 | dependencies: 1081 | glob "^7.0.0" 1082 | interpret "^1.0.0" 1083 | rechoir "^0.6.2" 1084 | 1085 | slice-ansi@0.0.4: 1086 | version "0.0.4" 1087 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" 1088 | 1089 | sockjs-client@1.1.1: 1090 | version "1.1.1" 1091 | resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.1.tgz#284843e9a9784d7c474b1571b3240fca9dda4bb0" 1092 | dependencies: 1093 | debug "^2.2.0" 1094 | eventsource "~0.1.6" 1095 | faye-websocket "~0.11.0" 1096 | inherits "^2.0.1" 1097 | json3 "^3.3.2" 1098 | url-parse "^1.1.1" 1099 | 1100 | sprintf-js@~1.0.2: 1101 | version "1.0.3" 1102 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 1103 | 1104 | string-width@^1.0.1: 1105 | version "1.0.2" 1106 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 1107 | dependencies: 1108 | code-point-at "^1.0.0" 1109 | is-fullwidth-code-point "^1.0.0" 1110 | strip-ansi "^3.0.0" 1111 | 1112 | string-width@^2.0.0: 1113 | version "2.0.0" 1114 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" 1115 | dependencies: 1116 | is-fullwidth-code-point "^2.0.0" 1117 | strip-ansi "^3.0.0" 1118 | 1119 | string_decoder@~1.0.0: 1120 | version "1.0.1" 1121 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.1.tgz#62e200f039955a6810d8df0a33ffc0f013662d98" 1122 | dependencies: 1123 | safe-buffer "^5.0.1" 1124 | 1125 | strip-ansi@^3.0.0: 1126 | version "3.0.1" 1127 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1128 | dependencies: 1129 | ansi-regex "^2.0.0" 1130 | 1131 | strip-bom@^3.0.0: 1132 | version "3.0.0" 1133 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1134 | 1135 | strip-json-comments@~2.0.1: 1136 | version "2.0.1" 1137 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1138 | 1139 | supports-color@^2.0.0: 1140 | version "2.0.0" 1141 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1142 | 1143 | table@^3.7.8: 1144 | version "3.8.3" 1145 | resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" 1146 | dependencies: 1147 | ajv "^4.7.0" 1148 | ajv-keywords "^1.0.0" 1149 | chalk "^1.1.1" 1150 | lodash "^4.0.0" 1151 | slice-ansi "0.0.4" 1152 | string-width "^2.0.0" 1153 | 1154 | text-table@~0.2.0: 1155 | version "0.2.0" 1156 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1157 | 1158 | through@^2.3.6, through@~2.3.4: 1159 | version "2.3.8" 1160 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1161 | 1162 | tinycolor@0.x: 1163 | version "0.0.1" 1164 | resolved "https://registry.yarnpkg.com/tinycolor/-/tinycolor-0.0.1.tgz#320b5a52d83abb5978d81a3e887d4aefb15a6164" 1165 | 1166 | to-fast-properties@^1.0.1: 1167 | version "1.0.3" 1168 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" 1169 | 1170 | tryit@^1.0.1: 1171 | version "1.0.3" 1172 | resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" 1173 | 1174 | type-check@~0.3.2: 1175 | version "0.3.2" 1176 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1177 | dependencies: 1178 | prelude-ls "~1.1.2" 1179 | 1180 | typedarray@^0.0.6: 1181 | version "0.0.6" 1182 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 1183 | 1184 | ultron@1.0.x: 1185 | version "1.0.2" 1186 | resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" 1187 | 1188 | url-parse@1.0.x: 1189 | version "1.0.5" 1190 | resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" 1191 | dependencies: 1192 | querystringify "0.0.x" 1193 | requires-port "1.0.x" 1194 | 1195 | url-parse@^1.1.1: 1196 | version "1.1.9" 1197 | resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.9.tgz#c67f1d775d51f0a18911dd7b3ffad27bb9e5bd19" 1198 | dependencies: 1199 | querystringify "~1.0.0" 1200 | requires-port "1.0.x" 1201 | 1202 | user-home@^2.0.0: 1203 | version "2.0.0" 1204 | resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" 1205 | dependencies: 1206 | os-homedir "^1.0.0" 1207 | 1208 | utf-8-validate@1.1.x: 1209 | version "1.1.0" 1210 | resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-1.1.0.tgz#521a6bb2189d0b307ddc5b79c3c95a5fd8085db4" 1211 | dependencies: 1212 | bindings "1.2.x" 1213 | nan "1.8.x" 1214 | 1215 | utf-8-validate@1.2.x: 1216 | version "1.2.2" 1217 | resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-1.2.2.tgz#8bb871a4741e085c70487ca7acdbd7d6d36029eb" 1218 | dependencies: 1219 | bindings "~1.2.1" 1220 | nan "~2.4.0" 1221 | 1222 | util-deprecate@~1.0.1: 1223 | version "1.0.2" 1224 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1225 | 1226 | websocket-driver@>=0.5.1: 1227 | version "0.6.5" 1228 | resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" 1229 | dependencies: 1230 | websocket-extensions ">=0.1.1" 1231 | 1232 | websocket-extensions@>=0.1.1: 1233 | version "0.1.1" 1234 | resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" 1235 | 1236 | websocket-stream@~0.5.1: 1237 | version "0.5.1" 1238 | resolved "https://registry.yarnpkg.com/websocket-stream/-/websocket-stream-0.5.1.tgz#622cd1f0566fb84ce0a4d6f854526f3dc4d70e48" 1239 | dependencies: 1240 | isbuffer "0.0.0" 1241 | through "~2.3.4" 1242 | ws "~0.4.30" 1243 | 1244 | wordwrap@~1.0.0: 1245 | version "1.0.0" 1246 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 1247 | 1248 | wrappy@1: 1249 | version "1.0.2" 1250 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1251 | 1252 | write@^0.2.1: 1253 | version "0.2.1" 1254 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 1255 | dependencies: 1256 | mkdirp "^0.5.1" 1257 | 1258 | ws@^1.1.1: 1259 | version "1.1.4" 1260 | resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.4.tgz#57f40d036832e5f5055662a397c4de76ed66bf61" 1261 | dependencies: 1262 | options ">=0.0.5" 1263 | ultron "1.0.x" 1264 | 1265 | ws@~0.4.30: 1266 | version "0.4.32" 1267 | resolved "https://registry.yarnpkg.com/ws/-/ws-0.4.32.tgz#787a6154414f3c99ed83c5772153b20feb0cec32" 1268 | dependencies: 1269 | commander "~2.1.0" 1270 | nan "~1.0.0" 1271 | options ">=0.0.5" 1272 | tinycolor "0.x" 1273 | 1274 | ws@~0.7.2: 1275 | version "0.7.2" 1276 | resolved "https://registry.yarnpkg.com/ws/-/ws-0.7.2.tgz#438c560bdfa2b7da3dd5b6b46ed61325c24699d8" 1277 | dependencies: 1278 | options ">=0.0.5" 1279 | ultron "1.0.x" 1280 | optionalDependencies: 1281 | bufferutil "1.1.x" 1282 | utf-8-validate "1.1.x" 1283 | 1284 | xtend@^4.0.0: 1285 | version "4.0.1" 1286 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 1287 | --------------------------------------------------------------------------------