diff --git a/angular.json b/angular.json index dbe106cb45..b4f9cb83c6 100644 --- a/angular.json +++ b/angular.json @@ -28,6 +28,7 @@ ], "styles": [ "src/frontend/packages/core/src/styles.scss", + "src/frontend/packages/cf-autoscaler/src/styles.scss", "node_modules/xterm/dist/xterm.css" ], "scripts": [] @@ -114,7 +115,8 @@ } ], "styles": [ - "src/frontend/packages/core/src/styles.css" + "src/frontend/packages/core/src/styles.css", + "src/frontend/packages/cf-autoscaler/src/styles.css" ], "scripts": [] }, @@ -285,6 +287,37 @@ } } } + }, + "cf-autoscaler": { + "root": "src/frontend/packages/cf-autoscaler", + "sourceRoot": "src/frontend/packages/cf-autoscaler/src", + "projectType": "library", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "src/frontend/packages/cf-autoscaler/tsconfig.lib.json", + "project": "src/frontend/packages/cf-autoscaler/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/frontend/packages/cf-autoscaler/src/test.ts", + "tsConfig": "src/frontend/packages/cf-autoscaler/tsconfig.spec.json", + "karmaConfig": "src/frontend/packages/cf-autoscaler/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["src/tsconfig.json"], + "tslintConfig": "src/frontend/packages/cf-autoscaler/tslint.json", + "files": ["src/frontend/packages/cf-autoscaler/src/**/*.ts"] + } + } + } } }, "defaultProject": "stratos", diff --git a/deploy/Dockerfile.all-in-one b/deploy/Dockerfile.all-in-one index d76eee967a..cf48fcafcf 100644 --- a/deploy/Dockerfile.all-in-one +++ b/deploy/Dockerfile.all-in-one @@ -23,6 +23,8 @@ COPY --from=builder /home/stratos/dev-certs /srv/dev-certs COPY --from=builder /home/stratos/ui /srv/ui COPY --from=builder /home/stratos/jetstream /srv/jetstream COPY --from=builder /home/stratos/config.properties /srv/config.properties +# User Invite templates +COPY --from=builder /home/stratos/src/jetstream/templates /srv/templates EXPOSE 443 diff --git a/deploy/all-in-one/config.all-in-one.properties b/deploy/all-in-one/config.all-in-one.properties index 0720f40f1f..a7ded0fe8f 100644 --- a/deploy/all-in-one/config.all-in-one.properties +++ b/deploy/all-in-one/config.all-in-one.properties @@ -11,3 +11,5 @@ SESSION_STORE_SECRET=wheeee! ENCRYPTION_KEY=B374A26A71490437AA024E4FADD5B497FDFF1A8EA6FF12F6FB65AF2720B59CCF STRATOS_DEPLOYMENT_DOCKER_AIO=true SKIP_SSL_VALIDATION=true + +TEMPLATE_DIR=./templates \ No newline at end of file diff --git a/deploy/ci/automation/cfallinonetest.sh b/deploy/ci/automation/cfallinonetest.sh index b3e1454131..41dd853497 100755 --- a/deploy/ci/automation/cfallinonetest.sh +++ b/deploy/ci/automation/cfallinonetest.sh @@ -43,8 +43,18 @@ CONTAINER_ID=$(docker run \ -e UAA_ENDPOINT="${UAA}" \ -e SKIP_SSL_VALIDATION='true' \ -e CONSOLE_ADMIN_SCOPE='cloud_controller.admin' \ +-e SMTP_HOST="${SMTP_HOST}" \ +-e SMTP_FROM_ADDRESS="${SMTP_FROM_ADDRESS}" \ $IMAGE) +# Show backend log - wait a few seconds for it to start up + +echo "Backend startup log ..." +sleep 20 +docker logs ${CONTAINER_ID} | tail -n 100 + +echo "Preparing E2E Tests..." + # Need node modules to run the tests rm -rf node_modules npm install diff --git a/deploy/ci/automation/cfpushtest.sh b/deploy/ci/automation/cfpushtest.sh index b6410706ae..0727305ce0 100755 --- a/deploy/ci/automation/cfpushtest.sh +++ b/deploy/ci/automation/cfpushtest.sh @@ -1,5 +1,12 @@ #!/bin/bash +GREEN='\033[0;32m' +RED='\033[0;31m' +CYAN="\033[96m" +YELLOW="\033[93m" +BOLD="\033[1m" +RESET='\033[0m' + echo "====================" echo "Stratos CF Push Test" echo "====================" @@ -83,6 +90,15 @@ echo " env:" >> $MANIFEST echo " SKIP_AUTO_REGISTER: true" >> $MANIFEST echo " FORCE_ENABLE_PERSISTENCE_FEATURES: true" >> $MANIFEST +# Make sure we add invite users config if set +if [ -n "${SMTP_HOST}" ]; then + echo " SMTP_HOST: ${SMTP_HOST}" >> $MANIFEST +fi + +if [ -n "${SMTP_FROM_ADDRESS}" ]; then + echo " SMTP_FROM_ADDRESS: ${SMTP_FROM_ADDRESS}" >> $MANIFEST +fi + # SSO SUITE="" if [ "$2" == "sso" ] || [ "$3" == "sso" ] ; then @@ -101,7 +117,7 @@ cat $MANIFEST # Prebuild if [ "$2" == "prebuild" ]; then - echo "Pre-building UI ..." + echo -e "${CYAN}Pre-building UI ...${RESET}" npm install npm run prebuild-ui fi @@ -118,9 +134,20 @@ date if [ $RET -ne 0 ]; then set +e echo "Push failed... showing recent log of the Stratos app" - cf logs console --recent + cf logs --recent console set -e else + + # Show the recent logs just we can see startup settings + echo -e "${BOLD}${GREEN}Showing recent logs of the Stratos App${RESET}" + cf logs --recent console | tail -n 100 + + echo -e "${BOLD}${GREEN}" + echo "===============================================================================" + echo "" + echo "Running E2E Tests...." + echo -e "${RESET}" + # Push was okay, so we can prepare and run E2E tests rm -rf node_modules npm install diff --git a/deploy/ci/automation/e2e-clean-remnants.sh b/deploy/ci/automation/e2e-clean-remnants.sh index 3cda0f58de..4f87d94a0b 100755 --- a/deploy/ci/automation/e2e-clean-remnants.sh +++ b/deploy/ci/automation/e2e-clean-remnants.sh @@ -101,4 +101,8 @@ echo "Cleaning test Users" USERS=$(cf space-users e2e e2e | grep "accept" | sed -e 's/^[[:space:]]*//') clean "$USERS" "-" "delete-user" "^(acceptancee2etravis)(invite[0-9])(20[0-9]*)[Tt]([0-9]*)[zZ].*" +# user -a with org users so we get all users (including those without roles) +USERS=$(cf org-users -a e2e | grep "accept" | sed -e 's/^[[:space:]]*//') +clean "$USERS" "-" "delete-user" "^(acceptancee2etravis)(invite[0-9])(20[0-9]*)[Tt]([0-9]*)[zZ].*" + echo "Done" diff --git a/deploy/ci/automation/helmtest.sh b/deploy/ci/automation/helmtest.sh index c13d38c38e..1a4ca3887d 100755 --- a/deploy/ci/automation/helmtest.sh +++ b/deploy/ci/automation/helmtest.sh @@ -42,6 +42,7 @@ function deleteRelease { function waitForHelmRelease { echo "Waiting for Stratos Helm Release to be ready..." local DONE="false" + local TIMEOUT=0 while [ $DONE != "true" ]; do COUNT=$(kubectl get po --namespace=${NAMESPACE} | wc -l) kubectl get po --namespace=${NAMESPACE} @@ -63,6 +64,11 @@ function waitForHelmRelease { if [ "$DONE" != "true" ]; then echo "Waiting for Stratos Helm release to be ready..." sleep 5 + TIMEOUT=$((TIMEOUT+1)) + if [ ${TIMEOUT} -gt 60 ]; then + echo "Timed out waiting for Helm release to be ready" + exit 1 + fi fi done } diff --git a/deploy/ci/scripts/build-docker-image-resource.sh b/deploy/ci/scripts/build-docker-image-resource.sh index fb64d43305..00b101e6ea 100755 --- a/deploy/ci/scripts/build-docker-image-resource.sh +++ b/deploy/ci/scripts/build-docker-image-resource.sh @@ -9,9 +9,10 @@ mkdir -p ./tmp cd ./tmp git clone https://github.com/concourse/docker-image-resource.git cp ../docker-image-out-asset ./docker-image-resource/assets/out +cp ../docker-image-common.sh ./docker-image-resource/assets/common.sh chmod +x ./docker-image-resource/assets/out -docker build ./docker-image-resource -t splatform/stratos-concourse-docker-image-resource:latest +docker build ./docker-image-resource -f ./docker-image-resource/dockerfiles/alpine/Dockerfile -t splatform/stratos-concourse-docker-image-resource:latest docker push splatform/stratos-concourse-docker-image-resource:latest rm -rf ./tmp echo "All done" diff --git a/deploy/ci/scripts/docker-image-common.sh b/deploy/ci/scripts/docker-image-common.sh new file mode 100644 index 0000000000..eed6f24497 --- /dev/null +++ b/deploy/ci/scripts/docker-image-common.sh @@ -0,0 +1,210 @@ +LOG_FILE=${LOG_FILE:-/tmp/docker.log} +SKIP_PRIVILEGED=${SKIP_PRIVILEGED:-false} +STARTUP_TIMEOUT=${STARTUP_TIMEOUT:-120} + +sanitize_cgroups() { + mkdir -p /sys/fs/cgroup + mountpoint -q /sys/fs/cgroup || \ + mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup + + mount -o remount,rw /sys/fs/cgroup + + sed -e 1d /proc/cgroups | while read sys hierarchy num enabled; do + if [ "$enabled" != "1" ]; then + # subsystem disabled; skip + continue + fi + + grouping="$(cat /proc/self/cgroup | cut -d: -f2 | grep "\\<$sys\\>")" || true + if [ -z "$grouping" ]; then + # subsystem not mounted anywhere; mount it on its own + grouping="$sys" + fi + + mountpoint="/sys/fs/cgroup/$grouping" + + mkdir -p "$mountpoint" + + # clear out existing mount to make sure new one is read-write + if mountpoint -q "$mountpoint"; then + umount "$mountpoint" + fi + + mount -n -t cgroup -o "$grouping" cgroup "$mountpoint" + + if [ "$grouping" != "$sys" ]; then + if [ -L "/sys/fs/cgroup/$sys" ]; then + rm "/sys/fs/cgroup/$sys" + fi + + ln -s "$mountpoint" "/sys/fs/cgroup/$sys" + fi + done + + if ! test -e /sys/fs/cgroup/systemd ; then + mkdir /sys/fs/cgroup/systemd + mount -t cgroup -o none,name=systemd none /sys/fs/cgroup/systemd + fi +} + +start_docker() { + mkdir -p /var/log + mkdir -p /var/run + + if [ "$SKIP_PRIVILEGED" = "false" ]; then + sanitize_cgroups + + # check for /proc/sys being mounted readonly, as systemd does + if grep '/proc/sys\s\+\w\+\s\+ro,' /proc/mounts >/dev/null; then + mount -o remount,rw /proc/sys + fi + fi + + local mtu=$(cat /sys/class/net/$(ip route get 8.8.8.8|awk '{ print $5 }')/mtu) + local server_args="--experimental --mtu ${mtu}" + local registry="" + + server_args="${server_args} --max-concurrent-downloads=$1 --max-concurrent-uploads=$2" + + for registry in $3; do + server_args="${server_args} --insecure-registry ${registry}" + done + + if [ -n "$4" ]; then + server_args="${server_args} --registry-mirror $4" + fi + + try_start() { + dockerd --data-root /scratch/docker ${server_args} >$LOG_FILE 2>&1 & + echo $! > /tmp/docker.pid + + sleep 1 + + echo waiting for docker to come up... + until docker info >/dev/null 2>&1; do + sleep 1 + if ! kill -0 "$(cat /tmp/docker.pid)" 2>/dev/null; then + return 1 + fi + done + } + + export server_args LOG_FILE + declare -fx try_start + trap stop_docker EXIT + + if ! timeout ${STARTUP_TIMEOUT} bash -ce 'while true; do try_start && break; done'; then + echo Docker failed to start within ${STARTUP_TIMEOUT} seconds. + return 1 + fi +} + +stop_docker() { + local pid=$(cat /tmp/docker.pid) + if [ -z "$pid" ]; then + return 0 + fi + + kill -TERM $pid +} + +log_in() { + local username="$1" + local password="$2" + local registry="$3" + + if [ -n "${username}" ] && [ -n "${password}" ]; then + echo "${password}" | docker login -u "${username}" --password-stdin ${registry} + else + mkdir -p ~/.docker + echo '{"credsStore":"ecr-login"}' >> ~/.docker/config.json + fi +} + +private_registry() { + local repository="${1}" + + if echo "${repository}" | fgrep -q '/' ; then + local registry="$(extract_registry "${repository}")" + if echo "${registry}" | fgrep -q '.' ; then + return 0 + fi + fi + + return 1 +} + +extract_registry() { + local repository="${1}" + + echo "${repository}" | cut -d/ -f1 +} + +extract_repository() { + local long_repository="${1}" + + echo "${long_repository}" | cut -d/ -f2- +} + +image_from_tag() { + docker images --no-trunc "$1" | awk "{if (\$2 == \"$2\") print \$3}" +} + +image_from_digest() { + docker images --no-trunc --digests "$1" | awk "{if (\$3 == \"$2\") print \$4}" +} + +certs_to_file() { + local raw_ca_certs="${1}" + local cert_count="$(echo $raw_ca_certs | jq -r '. | length')" + + for i in $(seq 0 $(expr "$cert_count" - 1)); + do + local cert_dir="/etc/docker/certs.d/$(echo $raw_ca_certs | jq -r .[$i].domain)" + mkdir -p "$cert_dir" + echo $raw_ca_certs | jq -r .[$i].cert >> "${cert_dir}/ca.crt" + done +} + +set_client_certs() { + local raw_client_certs="${1}" + local cert_count="$(echo $raw_client_certs | jq -r '. | length')" + + for i in $(seq 0 $(expr "$cert_count" - 1)); + do + local cert_dir="/etc/docker/certs.d/$(echo $raw_client_certs | jq -r .[$i].domain)" + [ -d "$cert_dir" ] || mkdir -p "$cert_dir" + echo $raw_client_certs | jq -r .[$i].cert >> "${cert_dir}/client.cert" + echo $raw_client_certs | jq -r .[$i].key >> "${cert_dir}/client.key" + done +} + +docker_pull() { + GREEN='\033[0;32m' + RED='\033[0;31m' + NC='\033[0m' # No Color + + pull_attempt=1 + max_attempts=3 + while [ "$pull_attempt" -le "$max_attempts" ]; do + printf "Pulling ${GREEN}%s${NC}" "$1" + + if [ "$pull_attempt" != "1" ]; then + printf " (attempt %s of %s)" "$pull_attempt" "$max_attempts" + fi + + printf "...\n" + + if docker pull "$1"; then + printf "\nSuccessfully pulled ${GREEN}%s${NC}.\n\n" "$1" + return 0 + fi + + echo + + pull_attempt=$(expr "$pull_attempt" + 1) + done + + printf "\n${RED}Failed to pull image %s.${NC}" "$1" + return 1 +} \ No newline at end of file diff --git a/deploy/ci/scripts/docker-image-out-asset b/deploy/ci/scripts/docker-image-out-asset index c0c2ed9ec4..6ec1d07a52 100644 --- a/deploy/ci/scripts/docker-image-out-asset +++ b/deploy/ci/scripts/docker-image-out-asset @@ -70,6 +70,9 @@ labels_file=$(jq -r '.params.labels_file // ""' < $payload) patch_base_reg=$(jq -r '.params.patch_base_reg // ""' < $payload) patch_base_tag=$(jq -r '.params.patch_base_tag // ""' < $payload) +# Support squashing the layers +squash=$(jq -r '.params.squash // "false"' < $payload) + tag_name="" if [ -n "$tag_params" ]; then if [ ! -f "$tag_params" ]; then @@ -125,6 +128,14 @@ elif [ -n "$build" ]; then exit 1 fi + # Squash support + squashArg="" + if [ "${squash}" == "true" ]; then + squashArg=" --squash " + fi + + echo "Squash ${squash} => args: ${squashArg}" + # Patch Base registry in Dockerfile # Must be '@' delimited if [ -n "${patch_base_reg}" ]; then @@ -166,8 +177,9 @@ elif [ -n "$build" ]; then cache_from_args=() if [ "$cache" = "true" ]; then - docker_pull "${repository}:${cache_tag}" - cache_from_args+=("--cache-from ${repository}:${cache_tag}") + if docker_pull "${repository}:${cache_tag}"; then + cache_from_args+=("--cache-from ${repository}:${cache_tag}") + fi fi if [ -n "$cache_from" ]; then @@ -248,7 +260,7 @@ elif [ -n "$build" ]; then fi ECR_REGISTRY_PATTERN='/[a-zA-Z0-9][a-zA-Z0-9_-]*\.dkr\.ecr\.[a-zA-Z0-9][a-zA-Z0-9_-]*\.amazonaws\.com(\.cn)?[^ ]*/' - ecr_images=$(grep '^\s*FROM' ${dockerfile} | \ + ecr_images=$(egrep '^\s*FROM|^\s*ARG' ${dockerfile} | \ awk "match(\$0,${ECR_REGISTRY_PATTERN}){print substr(\$0, RSTART, RLENGTH)}" ) if [ -n "$ecr_images" ]; then for ecr_image in $ecr_images @@ -262,7 +274,7 @@ elif [ -n "$build" ]; then done fi - docker build -t "${repository}:${tag_name}" "${target[@]}" "${expanded_build_args[@]}" "${expanded_labels[@]}" -f "$dockerfile" $cache_from "$build" + docker build ${squashArg} -t "${repository}:${tag_name}" "${target[@]}" "${expanded_build_args[@]}" "${expanded_labels[@]}" -f "$dockerfile" $cache_from "$build" elif [ -n "$load_file" ]; then if [ -n "$load_repository" ]; then docker load -i "$load_file" @@ -302,7 +314,7 @@ if [ -e /tmp/push-failed ]; then exit 1 fi -digest="$(tail -1 push-output | awk '{print $3}')" +digest="$(grep 'digest' push-output | awk '{print $3}')" if [ "$need_tag_as_latest" = "true" ] && [ "${tag_name}" != "latest" ]; then docker tag "${repository}:${tag_name}" "${repository}:latest" diff --git a/deploy/ci/travis/upload-e2e-test-report.sh b/deploy/ci/travis/upload-e2e-test-report.sh index c36b6d56f1..8a54092604 100755 --- a/deploy/ci/travis/upload-e2e-test-report.sh +++ b/deploy/ci/travis/upload-e2e-test-report.sh @@ -13,6 +13,8 @@ chmod +x mc echo "Uploading test report...." +./mc -install -y + echo "Configuring upload client" ./mc config host add s3 ${AWS_ENDPOINT} ${AWS_ACCESS_KEY_ID} ${AWS_SECRET_ACCESS_KEY} --insecure @@ -25,4 +27,4 @@ if [[ $? != 0 ]]; then fi # Test report upload failure will not fail the Travis job -exit 0 \ No newline at end of file +exit 0 diff --git a/deploy/cloud-foundry/build.sh b/deploy/cloud-foundry/build.sh index c4add91fcc..0d3f2eb8d3 100755 --- a/deploy/cloud-foundry/build.sh +++ b/deploy/cloud-foundry/build.sh @@ -67,6 +67,9 @@ log "Building back-end" $CYAN # Copy backend executable here cp src/jetstream/jetstream . +# Copy the user invite templates +cp -R src/jetstream/templates ./templates + # Back-end serves static resources from ui folder not dist mv dist ui diff --git a/deploy/cloud-foundry/config.properties b/deploy/cloud-foundry/config.properties index 367dc85c47..c3c95ab593 100644 --- a/deploy/cloud-foundry/config.properties +++ b/deploy/cloud-foundry/config.properties @@ -16,3 +16,6 @@ SESSION_STORE_SECRET=wheeee! #CONSOLE_PROXY_CERT=use local dev-cert/pproxy.crt in portal-proxy repo ENCRYPTION_KEY=B374A26A71490437AA024E4FADD5B497FDFF1A8EA6FF12F6FB65AF2720B59CCF #VCAP_APPLICATION={"cf_api": "https://api.10.4.21.240.nip.io:8443"} + +# User invite templates +TEMPLATE_DIR=./templates \ No newline at end of file diff --git a/deploy/tools/init-cf-for-e2e.sh b/deploy/tools/init-cf-for-e2e.sh index ed7755ba03..6a8abc2066 100755 --- a/deploy/tools/init-cf-for-e2e.sh +++ b/deploy/tools/init-cf-for-e2e.sh @@ -13,11 +13,13 @@ USER_PASS="pass" REMOVE_USER="e2e-remove-user" SKIP_LOGIN="false" CF_API_ENDPOINT="https://api.local.pcfdev.io" +#(CFDEV)CF_API_ENDPOINT="https://api.dev.cfdev.sh" DEFAULT_ORG="e2e" DEFAULT_SPACE="e2e" SETUP_INVITE_USER="true" UAA_CLI_CMD="uaac" UAA_ENDPOINT="https://uaa.local.pcfdev.io" +#(CFDEV)UAA_ENDPOINT="https://uaa.dev.cfdev.sh" #(SCF)UAA_ENDPOINT="https://uaa.cf.capbristol.com" ADMIN_CLIENT_SECRET="admin-client-secret" #(SCF)ADMIN_CLIENT_SECRET="" @@ -108,8 +110,12 @@ function cloneRepo() { mkdir -p cfpushtemp pushd cfpushtemp git clone https://github.com/$PROJECT/$REPO - popd + else + echo "Rebasing: $PROJECT/$REPO" + pushd cfpushtemp/$REPO + git pull --rebase fi + popd } function addInviteUserUaaClient() { diff --git a/docs/cf-api-v3.md b/docs/cf-api-v3.md index d26cc0be98..5bef521e7c 100644 --- a/docs/cf-api-v3.md +++ b/docs/cf-api-v3.md @@ -338,7 +338,7 @@ links | `applications` | [HIGH] | Display a list of the apps that are bound to t Type | Name | Priority | UX Example | Notes --- | --- | --- | --- | --- -link | `service_instance.applications`| [HIGH] | Display bound applications in a list of service instances | Not sure if this will be implemented the same as routes and route mappings, but would need similar functionality to fetch list inline +link | `service_instance.applications`| [HIGH] | Display bound applications in a list of service instances | Not sure if this will be implemented the same as routes and route mappings, but would need similar functionality to fetch list inline. This could be replaced with a link to `service_bindings` and then the `app` for that binding `include` | `service_instance.applications`| [HIGH] | See above | See above link | `service_plan`| [HIGH] | Display service plan information per SI in a list of SI | See [2]. `/service_plan` has no v3 equivalent `include` | `service_plan` | [HIGH] | See above | See above @@ -360,6 +360,83 @@ filter | `name` | [MEDIUM] | | See [3] We've recently integrated user provided service instances into Stratos. There doesn't seem to be any current support for this in v3. We'd need similar functionality to `/service_instances` (where there's cross over). +### `/services` + +> Comparison of missing functionality as per proposed spec in https://docs.google.com/document/d/1bDsEiZRwQJNUI41cQlUaioaY7JA1fnv_AThOI2ekPXNM/edit# +> For simplicity have kept the `services` name instead of the proposed new name of `service_offerings` + +Type | Name | Priority | UX Example | Notes +--- | --- | --- | --- | --- +link | `service.service_plans`| [HIGH] | Show a count of service plans for a service when showing a list of services | Depends on implementation of 'included' pagination - see https://github.com/cloudfoundry/cc-api-v3-style-guide#proposal-pagination-of-related-resources +`include` | `service.service_plans` | [HIGH] | See above | See above +link | `service.service_broker` | [MEDIUM] | Not currently used, but would be very nice to display the broker where a service is coming from | Note - whole service broker entity (not just name), would be nice +`include` | `service.service_broker` | [MEDIUM] | See above | See above +filter | `name` | [MEDIUM] | | See [1] +`order_by` | `name` | [MEDIUM] | | see [1] +`order_by` | `active` | [MEDIUM] | | see [1] +`order_by` | `bindable` | [MEDIUM] | | see [1] + +[1] As other situations where we fetch lists this will help us from switching from local (fetch allll entities in a list and sort locally) to non-local (use CF api pagination including sorting). See ([non-local lists](cf-api-v2-usage.md#Lists) for more detail on local and non-local lists). + +### `/service/${guid}` + +See `/services` above + +### `spaces/${guid}/services` + +To be replaced with `/services` + +### `services/${guid}/service_plans` + +Currently missing in v3 docs. If implemented would need the same link/includes as `service_bindings` section below + +### `service_bindings` (POST) + +This looks good + +### `service_bindings/${guid}` (DELETE) + +This looks good + +### `service_bindings` + +We don't currently use this, but in order for us to we would need the following + +Type | Name | Priority | UX Example | Notes +--- | --- | --- | --- | --- +`include` | `service_binding.service_instance` | [HIGH] | | +`include` | `service_binding.app` | [HIGH] | | +filter | service instance name | [MEDIUM] | | See [1] +filter | application name | [MEDIUM] | | See [1] +`order_by` | service instance name | [MEDIUM] | | see [1] +`order_by` | application name | [MEDIUM] | | see [1] + +[1] As other situations where we fetch lists this will help us from switching from local (fetch allll entities in a list and sort locally) to non-local (use CF api pagination including sorting). See ([non-local lists](cf-api-v2-usage.md#Lists) for more detail on local and non-local lists). + +### `service_bindings/${guid}` + +We don't currently use this, but in order for us to we would need the same as above (list service_bindings) + +### `service_brokers` + +We don't currently use this but it would be very nice to. In order for us to though we would need the following + +Type | Name | Priority | UX Example | Notes +--- | --- | --- | --- | --- +link | `service_broker.space`| [HIGH] | | +`include` | `service_broker.space` | [HIGH] | | +link | `service_broker.service_offerings`| [HIGH] | For a given broker show a list of service offerings without making multiple requests | +`include` | `service_broker.service_offerings` | [HIGH] | See above | +filter | service broker name | [MEDIUM] | | See [1] +filter | space guid | [MEDIUM] | | See [1] +`order_by` | service broker name | [MEDIUM] | | see [1] + +[1] As other situations where we fetch lists this will help us from switching from local (fetch allll entities in a list and sort locally) to non-local (use CF api pagination including sorting). See ([non-local lists](cf-api-v2-usage.md#Lists) for more detail on local and non-local lists). + +### `service_brokers/{guid}` + +We don't currently use this, but in order for us to we would need the same as above (list service_bindings) + ## v3 Required Features ### Single 'included` section per request diff --git a/package-lock.json b/package-lock.json index 6ddbab29c8..511619aff6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } } } @@ -84,10 +84,10 @@ "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "ansi-styles": { @@ -104,11 +104,11 @@ "dev": true, "optional": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "clone": { @@ -136,12 +136,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "json-schema-traverse": { @@ -156,15 +156,15 @@ "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", "dev": true, "requires": { - "clone": "^2.1.2", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" + "clone": "2.1.2", + "errno": "0.1.7", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.88.0", + "source-map": "0.6.1" } }, "mime-db": { @@ -181,7 +181,7 @@ "dev": true, "optional": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.38.0" } }, "node-sass": { @@ -191,25 +191,25 @@ "dev": true, "optional": true, "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.10.0", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "async-foreach": "0.1.3", + "chalk": "1.1.3", + "cross-spawn": "3.0.1", + "gaze": "1.1.3", + "get-stdin": "4.0.1", + "glob": "7.1.3", + "in-publish": "2.0.0", + "lodash.assign": "4.2.0", + "lodash.clonedeep": "4.5.0", + "lodash.mergewith": "4.6.1", + "meow": "3.7.0", + "mkdirp": "0.5.1", + "nan": "2.10.0", + "node-gyp": "3.8.0", + "npmlog": "4.1.2", + "request": "2.88.0", + "sass-graph": "2.2.4", + "stdout-stream": "1.4.0", + "true-case-path": "1.0.2" }, "dependencies": { "request": { @@ -219,26 +219,26 @@ "dev": true, "optional": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.1.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.22", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" } } } @@ -249,7 +249,7 @@ "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" } }, "rxjs": { @@ -258,7 +258,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "semver": { @@ -287,7 +287,7 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } }, "uuid": { @@ -317,7 +317,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "semver": { @@ -365,7 +365,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } } } @@ -389,10 +389,10 @@ "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "chokidar": { @@ -401,19 +401,19 @@ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.7", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "lodash.debounce": "4.0.8", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" } }, "fast-deep-equal": { @@ -434,7 +434,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "source-map": { @@ -449,7 +449,7 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } } } @@ -470,7 +470,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } } } @@ -480,7 +480,7 @@ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.6.tgz", "integrity": "sha512-ICKPS+bKabhQNqnPoVZegUAhgNPbVFlrxHoJ+ZZeVGxw5iBE8TnP3a2sRvakdMTKhykDlwVVGMKLxu2Y34uhmg==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/cdk": { @@ -488,8 +488,8 @@ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.3.3.tgz", "integrity": "sha512-0M3nwq+c9+d+p0XeU12I9djutlsajRrxcu7AvHQXUs/5grYFsXsX0f468qXDiKmcgqGUBNtN2fBOUhGNlispuQ==", "requires": { - "parse5": "^5.0.0", - "tslib": "^1.7.1" + "parse5": "5.1.0", + "tslib": "1.9.3" }, "dependencies": { "parse5": { @@ -526,10 +526,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "ansi-escapes": { @@ -549,19 +549,19 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.7", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "lodash.debounce": "4.0.8", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" } }, "cli-cursor": { @@ -570,7 +570,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "2.0.0" } }, "fast-deep-equal": { @@ -584,7 +584,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "1.0.5" } }, "inquirer": { @@ -593,19 +593,19 @@ "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.17.10", + "ansi-escapes": "3.2.0", + "chalk": "2.2.2", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "3.0.3", + "figures": "2.0.0", + "lodash": "4.17.11", "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" + "run-async": "2.3.0", + "rxjs": "6.3.3", + "string-width": "2.1.1", + "strip-ansi": "5.0.0", + "through": "2.3.8" } }, "is-fullwidth-code-point": { @@ -619,6 +619,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -631,7 +637,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "opn": { @@ -640,7 +646,7 @@ "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" } }, "restore-cursor": { @@ -649,8 +655,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "onetime": "2.0.1", + "signal-exit": "3.0.2" } }, "run-async": { @@ -659,7 +665,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "^2.1.0" + "is-promise": "2.1.0" } }, "rxjs": { @@ -668,7 +674,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "semver": { @@ -688,8 +694,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "strip-ansi": { @@ -698,7 +704,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -709,7 +715,7 @@ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", "dev": true, "requires": { - "ansi-regex": "^4.0.0" + "ansi-regex": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -725,7 +731,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } } } @@ -735,7 +741,7 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.6.tgz", "integrity": "sha512-jzWUgsgS0dmPy7yDHX4qCqVpt7ZZmHhApgkg5RkzTAlp+0cvZ/KsDpBgHXnZUIfmk/5g1/EtTbkbClgp1kCkIg==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/compiler": { @@ -743,7 +749,7 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.6.tgz", "integrity": "sha512-GXdvgH8oxK8HRh/FelN3U5p0tsTUwGh8b/iuuJKaunBSSDDjIy7pPnn3zT+lN4YeEi6qN1XWudt+HpWHYHyymg==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/compiler-cli": { @@ -753,15 +759,15 @@ "dev": true, "requires": { "canonical-path": "1.0.0", - "chokidar": "^2.1.1", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.7.2", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "shelljs": "^0.8.1", - "source-map": "^0.6.1", - "tslib": "^1.9.0", + "chokidar": "2.1.2", + "convert-source-map": "1.5.1", + "dependency-graph": "0.7.2", + "magic-string": "0.25.2", + "minimist": "1.2.0", + "reflect-metadata": "0.1.13", + "shelljs": "0.8.3", + "source-map": "0.6.1", + "tslib": "1.9.3", "yargs": "9.0.1" }, "dependencies": { @@ -783,18 +789,18 @@ "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.0" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.7", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "3.0.0", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.1.0" } }, "cliui": { @@ -803,9 +809,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" }, "dependencies": { "string-width": { @@ -814,9 +820,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -827,10 +833,10 @@ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" } }, "mem": { @@ -839,7 +845,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "minimist": { @@ -860,9 +866,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "path-type": { @@ -871,7 +877,7 @@ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "2.3.0" } }, "pify": { @@ -886,9 +892,9 @@ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" } }, "read-pkg-up": { @@ -897,8 +903,8 @@ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "find-up": "2.1.0", + "read-pkg": "2.0.0" } }, "readdirp": { @@ -907,9 +913,9 @@ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "graceful-fs": "4.1.11", + "micromatch": "3.1.10", + "readable-stream": "2.3.6" } }, "shelljs": { @@ -918,9 +924,9 @@ "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", "dev": true, "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "glob": "7.1.2", + "interpret": "1.1.0", + "rechoir": "0.6.2" } }, "source-map": { @@ -935,8 +941,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "is-fullwidth-code-point": { @@ -951,7 +957,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -980,19 +986,19 @@ "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", "dev": true, "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" } }, "yargs-parser": { @@ -1001,7 +1007,7 @@ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -1011,7 +1017,7 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.6.tgz", "integrity": "sha512-MZg17DWH1KUoDa9wFYK9Z+3F7DnUW2DjSwGyIi9U4cB54IWFhgt1JsA0mcuSYuRSRpvwaArCDC2AN90f+0/EFA==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/flex-layout": { @@ -1019,7 +1025,7 @@ "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.23.tgz", "integrity": "sha512-jH2i3i/M7SbK6scVlj2urVL5OhzwavbQ7KwvUjyc/UwccKnnzuOuWEGCINLja/aoaUO3I35LluCLv6a6VN0olA==", "requires": { - "tslib": "^1.7.1" + "tslib": "1.9.3" } }, "@angular/forms": { @@ -1027,7 +1033,7 @@ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.6.tgz", "integrity": "sha512-At72AJNGe+Zzmryb7DDIG+iws7zKdgZx/eEVjZ/Obu/yREefbZY4R7q83U90Vljtn97BwJPx9ur9ttxE9WpnlA==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/http": { @@ -1035,7 +1041,7 @@ "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.6.tgz", "integrity": "sha512-B5TC8JFW/I+ms5iJthGjf/eg1tKKqpO6yrOpDSQofHG7Dd4dozyGXGznFJTRFfNda5rku61/qnQaa8SyQyuplQ==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/language-service": { @@ -1049,7 +1055,7 @@ "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.3.3.tgz", "integrity": "sha512-DZdJaVpXsd5QlfpN5P871llw8AKh5QvRiyro3QRmEajYN85Xiawqpbt7O60TrwcFM6DzYLI3UeyWq8LFdmi/+Q==", "requires": { - "tslib": "^1.7.1" + "tslib": "1.9.3" } }, "@angular/material-moment-adapter": { @@ -1057,7 +1063,7 @@ "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-7.3.3.tgz", "integrity": "sha512-Fp2EcFpwdwa5qWWBa7G1mJZuaQ3oD3pohyo04HD2ud8YFP5IPbeoHVc65JC7TIwQFbVKk0lojXK3b2vQXZOapw==", "requires": { - "tslib": "^1.7.1" + "tslib": "1.9.3" } }, "@angular/platform-browser": { @@ -1065,7 +1071,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.6.tgz", "integrity": "sha512-VE4yS4l8Cdx6DlvrbOFOZDKmQuyz1RhVcshgSt9hKlkehvAXMtX8Sqnp6po7z0aPykTh0TZZtMtLEerkFEe+DA==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/platform-browser-dynamic": { @@ -1073,7 +1079,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.6.tgz", "integrity": "sha512-/co/q4v11nKin2MFscCMZyixbW103I2FxbPgCAYBN5NSvfIwTrt5J6xWmDoKJ8HkZBqL3R9B+uhYdzsRN/pQxg==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@angular/platform-server": { @@ -1081,9 +1087,9 @@ "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-7.2.6.tgz", "integrity": "sha512-rtLT0BxgEGJDYGMeFNYfna3H91JITFfm4Qd86nH7EmR1mVWa563lSQle1VWwbQhRbj5fDhwt+anbVwz7SEn7ew==", "requires": { - "domino": "^2.1.0", - "tslib": "^1.9.0", - "xhr2": "^0.1.4" + "domino": "2.1.2", + "tslib": "1.9.3", + "xhr2": "0.1.4" } }, "@angular/router": { @@ -1091,7 +1097,7 @@ "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.6.tgz", "integrity": "sha512-ayMVor4Mu4wk7JKpt51UxHovnLB4munZ8ELR1CA4w+s0rJsSSwyB4WXElC+DbgCyl7BYLAaGui2c5DbTAJ9jlw==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "@mrmlnc/readdir-enhanced": { @@ -1100,8 +1106,8 @@ "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", "dev": true, "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" + "call-me-maybe": "1.0.1", + "glob-to-regexp": "0.3.0" } }, "@ngrx/effects": { @@ -1149,7 +1155,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } } } @@ -1166,20 +1172,20 @@ "integrity": "sha512-rt+YzuNpmVGIt5agPM5Z61cMaSLRmEcpiI4HdVL8eOBw7HicVk2N894rHbQdU3/LHZUt592zq8Z7jOFUtbV3JQ==", "dev": true, "requires": { - "@types/yargs": "^11.0.0", - "app-root-path": "^2.0.1", + "@types/yargs": "11.1.2", + "app-root-path": "2.1.0", "cosmiconfig": "4.0.0", "fs-extra": "6.0.0", "graphviz": "0.0.8", "ignore": "5.0.4", "npm-run-all": "4.1.5", - "opn": "^5.3.0", + "opn": "5.3.0", "rxjs": "6.3.3", "semver": "5.4.1", "strip-json-comments": "2.0.1", "tmp": "0.0.33", - "viz.js": "^1.8.1", - "yargs": "^11.0.0", + "viz.js": "1.8.2", + "yargs": "11.1.0", "yargs-parser": "10.0.0" }, "dependencies": { @@ -1201,9 +1207,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" } }, "fs-extra": { @@ -1212,9 +1218,9 @@ "integrity": "sha512-lk2cUCo8QzbiEWEbt7Cw3m27WMiRG321xsssbcIpfMhpRjrlC08WBOVQqj1/nQYYNnPtyIhP1oqLO3QwT2tPCw==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" } }, "ignore": { @@ -1235,7 +1241,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.11" } }, "mem": { @@ -1244,7 +1250,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "os-locale": { @@ -1253,9 +1259,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "rxjs": { @@ -1264,7 +1270,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "semver": { @@ -1279,8 +1285,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -1289,7 +1295,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "strip-json-comments": { @@ -1316,18 +1322,18 @@ "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" }, "dependencies": { "yargs-parser": { @@ -1336,7 +1342,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -1347,7 +1353,7 @@ "integrity": "sha512-+DHejWujTVYeMHLff8U96rLc4uE4Emncoftvn5AjhB1Jw1pWxLzgBUT/WYbPrHmy6YPEBTZQx5myHhVcuuu64g==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -1368,10 +1374,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "chokidar": { @@ -1379,19 +1385,19 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.7", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "lodash.debounce": "4.0.8", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" } }, "fast-deep-equal": { @@ -1409,7 +1415,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "source-map": { @@ -1428,7 +1434,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } } } @@ -1454,10 +1460,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "chokidar": { @@ -1465,19 +1471,19 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.7", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "lodash.debounce": "4.0.8", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" } }, "fast-deep-equal": { @@ -1496,7 +1502,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "semver": { @@ -1515,7 +1521,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } } } @@ -1525,18 +1531,18 @@ "resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-10.1.0.tgz", "integrity": "sha512-0nZ3ub5o/qQfFMz+av89u/VKqsqiVhPFBDDOOPq2qimP83i+pcb0i3MEao7PSxIbKAEkL/pwRcEJw4KDZ8B67w==", "requires": { - "d3": "^4.10.2", - "d3-array": "^1.2.1", - "d3-brush": "^1.0.4", - "d3-color": "^1.0.3", - "d3-force": "^1.1.0", - "d3-format": "^1.2.0", - "d3-hierarchy": "^1.1.5", - "d3-interpolate": "^1.1.5", - "d3-scale": "^1.0.6", - "d3-selection": "^1.1.0", - "d3-shape": "^1.2.0", - "d3-time-format": "^2.1.0" + "d3": "4.13.0", + "d3-array": "1.2.4", + "d3-brush": "1.0.6", + "d3-color": "1.2.3", + "d3-force": "1.2.0", + "d3-format": "1.3.2", + "d3-hierarchy": "1.1.8", + "d3-interpolate": "1.3.2", + "d3-scale": "1.0.7", + "d3-selection": "1.4.0", + "d3-shape": "1.3.4", + "d3-time-format": "2.1.3" } }, "@tweenjs/tween.js": { @@ -1573,7 +1579,7 @@ "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "dev": true, "requires": { - "@types/node": "*" + "@types/node": "11.9.4" } }, "@types/jasmine": { @@ -1588,7 +1594,7 @@ "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", "dev": true, "requires": { - "@types/jasmine": "*" + "@types/jasmine": "2.8.7" } }, "@types/karma": { @@ -1597,9 +1603,9 @@ "integrity": "sha512-xm+iMKgLSpTPo1Z7wLKbrZt8aLzA1udb9UOb9+OSDMFIufqlbp0DXQpY75EQ3ETi6EJiaohIodBlMs+UD4NSMQ==", "dev": true, "requires": { - "@types/bluebird": "*", - "@types/node": "*", - "log4js": "^3.0.0" + "@types/bluebird": "3.5.25", + "@types/node": "11.9.4", + "log4js": "3.0.6" } }, "@types/marked": { @@ -1607,6 +1613,14 @@ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.6.5.tgz", "integrity": "sha512-6kBKf64aVfx93UJrcyEZ+OBM5nGv4RLsI6sR1Ar34bpgvGVRoyTgpxn4ZmtxOM5aDTAaaznYuYUH8bUX3Nk3YA==" }, + "@types/moment-timezone": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/@types/moment-timezone/-/moment-timezone-0.5.12.tgz", + "integrity": "sha512-hnHH2+Efg2vExr/dSz+IX860nSiyk9Sk4pJF2EmS11lRpMcNXeB4KBW5xcgw2QPsb9amTXdsVNEe5IoJXiT0uw==", + "requires": { + "moment": "2.24.0" + } + }, "@types/node": { "version": "11.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz", @@ -1625,10 +1639,10 @@ "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", "dev": true, "requires": { - "@types/caseless": "*", - "@types/form-data": "*", - "@types/node": "*", - "@types/tough-cookie": "*" + "@types/caseless": "0.12.1", + "@types/form-data": "2.2.1", + "@types/node": "11.9.4", + "@types/tough-cookie": "2.3.5" } }, "@types/selenium-webdriver": { @@ -1660,9 +1674,9 @@ "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==", "dev": true, "requires": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" + "@types/node": "11.9.4", + "@types/source-list-map": "0.1.2", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -1753,7 +1767,7 @@ "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", "dev": true, "requires": { - "@xtuc/ieee754": "^1.2.0" + "@xtuc/ieee754": "1.2.0" } }, "@webassemblyjs/leb128": { @@ -1875,8 +1889,8 @@ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "jsonparse": "1.3.1", + "through": "2.3.8" } }, "abbrev": { @@ -1891,7 +1905,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.18", "negotiator": "0.6.1" } }, @@ -1913,7 +1927,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "^3.0.4" + "acorn": "3.3.0" }, "dependencies": { "acorn": { @@ -1942,7 +1956,7 @@ "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", "dev": true, "requires": { - "es6-promisify": "^5.0.0" + "es6-promisify": "5.0.0" } }, "agentkeepalive": { @@ -1951,7 +1965,7 @@ "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", "dev": true, "requires": { - "humanize-ms": "^1.2.1" + "humanize-ms": "1.2.1" } }, "ajv": { @@ -1960,10 +1974,10 @@ "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" }, "dependencies": { "fast-deep-equal": { @@ -1997,11 +2011,10 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" }, "dependencies": { "kind-of": { @@ -2009,9 +2022,8 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -2027,7 +2039,7 @@ "resolved": "https://registry.npmjs.org/angular2-virtual-scroll/-/angular2-virtual-scroll-0.3.2.tgz", "integrity": "sha512-eyPb6FnaA8d4lBh3v31F3YLLblr9Nn8zv25uKVmxPkA2/te6QDg0sPMqy6o8GouwW+U9LKzcUJZZBHa0ICY/rg==", "requires": { - "@tweenjs/tween.js": "^17.1.0" + "@tweenjs/tween.js": "17.2.0" } }, "ansi-align": { @@ -2036,7 +2048,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -2057,8 +2069,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -2067,7 +2079,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -2078,7 +2090,7 @@ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { - "ansi-wrap": "^0.1.0" + "ansi-wrap": "0.1.0" } }, "ansi-cyan": { @@ -2132,7 +2144,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "ansi-wrap": { @@ -2146,8 +2158,8 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" } }, "app-root-path": { @@ -2162,7 +2174,7 @@ "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", "dev": true, "requires": { - "buffer-equal": "^1.0.0" + "buffer-equal": "1.0.0" } }, "append-transform": { @@ -2171,7 +2183,7 @@ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", "dev": true, "requires": { - "default-require-extensions": "^2.0.0" + "default-require-extensions": "2.0.0" } }, "aproba": { @@ -2192,8 +2204,8 @@ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "argparse": { @@ -2202,7 +2214,7 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "argv": { @@ -2222,7 +2234,7 @@ "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", "dev": true, "requires": { - "make-iterator": "^1.0.0" + "make-iterator": "1.0.1" } }, "arr-flatten": { @@ -2236,7 +2248,7 @@ "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", "dev": true, "requires": { - "make-iterator": "^1.0.0" + "make-iterator": "1.0.1" } }, "arr-union": { @@ -2274,8 +2286,8 @@ "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", "dev": true, "requires": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" + "array-slice": "1.1.0", + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -2292,7 +2304,7 @@ "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", "dev": true, "requires": { - "is-number": "^4.0.0" + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -2327,9 +2339,9 @@ "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, "requires": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" + "default-compare": "1.0.0", + "get-value": "2.0.6", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -2346,7 +2358,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -2390,9 +2402,9 @@ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "assert": { @@ -2433,10 +2445,10 @@ "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^1.0.7", - "stream-exhaust": "^1.0.1" + "end-of-stream": "1.4.1", + "once": "1.4.0", + "process-nextick-args": "1.0.7", + "stream-exhaust": "1.0.2" }, "dependencies": { "process-nextick-args": { @@ -2470,7 +2482,7 @@ "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", "dev": true, "requires": { - "async-done": "^1.2.2" + "async-done": "1.3.1" } }, "asynckit": { @@ -2490,12 +2502,12 @@ "integrity": "sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==", "dev": true, "requires": { - "browserslist": "^4.4.1", - "caniuse-lite": "^1.0.30000929", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.13", - "postcss-value-parser": "^3.3.1" + "browserslist": "4.4.1", + "caniuse-lite": "1.0.30000938", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "7.0.14", + "postcss-value-parser": "3.3.1" } }, "aws-sign2": { @@ -2516,9 +2528,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" }, "dependencies": { "ansi-styles": { @@ -2533,11 +2545,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -2554,16 +2566,22 @@ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" }, "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -2578,7 +2596,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-runtime": { @@ -2587,8 +2605,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.5", + "regenerator-runtime": "0.11.1" } }, "babel-template": { @@ -2597,11 +2615,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.13" } }, "babel-traverse": { @@ -2610,15 +2628,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.4", + "lodash": "4.17.13" } }, "babel-types": { @@ -2627,10 +2645,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.13", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -2645,15 +2663,15 @@ "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", "dev": true, "requires": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" + "arr-filter": "1.1.2", + "arr-flatten": "1.1.0", + "arr-map": "2.0.2", + "array-each": "1.0.1", + "array-initial": "1.1.0", + "array-last": "1.3.0", + "async-done": "1.3.1", + "async-settle": "1.0.0", + "now-and-later": "2.0.1" } }, "backo2": { @@ -2672,13 +2690,13 @@ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -2686,7 +2704,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -2694,7 +2712,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -2702,7 +2720,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -2710,9 +2728,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -2748,7 +2766,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "better-assert": { @@ -2783,7 +2801,7 @@ "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, "requires": { - "inherits": "~2.0.0" + "inherits": "2.0.3" } }, "blocking-proxy": { @@ -2792,7 +2810,7 @@ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.0" }, "dependencies": { "minimist": { @@ -2822,15 +2840,15 @@ "dev": true, "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "~1.6.16" + "type-is": "1.6.16" }, "dependencies": { "iconv-lite": { @@ -2839,7 +2857,7 @@ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } @@ -2850,12 +2868,12 @@ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true, "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "array-flatten": "2.1.2", + "deep-equal": "1.0.1", + "dns-equal": "1.0.0", + "dns-txt": "2.0.2", + "multicast-dns": "6.2.3", + "multicast-dns-service-types": "1.1.0" } }, "boxen": { @@ -2864,13 +2882,13 @@ "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.2.2", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "2.0.1" }, "dependencies": { "ansi-regex": { @@ -2897,8 +2915,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -2907,7 +2925,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -2917,7 +2935,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -2926,16 +2944,16 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -2943,7 +2961,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2960,12 +2978,12 @@ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "browserify-cipher": { @@ -2974,9 +2992,9 @@ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "browserify-aes": "1.2.0", + "browserify-des": "1.0.2", + "evp_bytestokey": "1.0.3" } }, "browserify-des": { @@ -2985,10 +3003,10 @@ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "browserify-rsa": { @@ -2997,8 +3015,8 @@ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "bn.js": "4.11.8", + "randombytes": "2.1.0" } }, "browserify-sign": { @@ -3007,13 +3025,13 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "elliptic": "6.4.1", + "inherits": "2.0.3", + "parse-asn1": "5.1.4" } }, "browserify-zlib": { @@ -3022,7 +3040,7 @@ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "requires": { - "pako": "~1.0.5" + "pako": "1.0.6" } }, "browserslist": { @@ -3031,9 +3049,9 @@ "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000929", - "electron-to-chromium": "^1.3.103", - "node-releases": "^1.1.3" + "caniuse-lite": "1.0.30000938", + "electron-to-chromium": "1.3.113", + "node-releases": "1.1.8" } }, "browserstack": { @@ -3042,7 +3060,7 @@ "integrity": "sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg==", "dev": true, "requires": { - "https-proxy-agent": "^2.2.1" + "https-proxy-agent": "2.2.1" } }, "browserstack-local": { @@ -3051,11 +3069,11 @@ "integrity": "sha512-BUJWxIsJkJxqfTPJIvGWTsf+IYSqSFUeFNW9tnuyTG7va/0LkXLhIi/ErFGDle1urQkol48HlQUXj4QrliXFpg==", "dev": true, "requires": { - "https-proxy-agent": "^2.2.1", - "is-running": "^2.0.0", - "ps-tree": "=1.1.1", - "sinon": "^1.17.6", - "temp-fs": "^0.9.9" + "https-proxy-agent": "2.2.1", + "is-running": "2.1.0", + "ps-tree": "1.1.1", + "sinon": "1.17.7", + "temp-fs": "0.9.9" } }, "buffer": { @@ -3064,9 +3082,9 @@ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "1.3.0", + "ieee754": "1.1.12", + "isarray": "1.0.0" } }, "buffer-alloc": { @@ -3075,8 +3093,8 @@ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "buffer-alloc-unsafe": "1.1.0", + "buffer-fill": "1.0.0" } }, "buffer-alloc-unsafe": { @@ -3151,19 +3169,19 @@ "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" + "bluebird": "3.5.1", + "chownr": "1.1.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.3", + "mississippi": "2.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.3.0", + "unique-filename": "1.1.1", + "y18n": "4.0.0" } }, "cache-base": { @@ -3171,15 +3189,15 @@ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" } }, "call-me-maybe": { @@ -3194,7 +3212,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsite": { @@ -3222,8 +3240,8 @@ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "camelcase": "2.1.1", + "map-obj": "1.0.1" }, "dependencies": { "camelcase": { @@ -3265,8 +3283,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chalk": { @@ -3275,9 +3293,9 @@ "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" }, "dependencies": { "has-flag": { @@ -3292,7 +3310,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } } } @@ -3309,18 +3327,18 @@ "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.1.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.0" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.7", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.1.0" } }, "chownr": { @@ -3335,7 +3353,7 @@ "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "ci-info": { @@ -3350,8 +3368,8 @@ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "circular-dependency-plugin": { @@ -3370,10 +3388,10 @@ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -3381,7 +3399,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -3392,7 +3410,7 @@ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", "dev": true, "requires": { - "source-map": "~0.6.0" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -3415,7 +3433,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "^1.0.1" + "restore-cursor": "1.0.1" } }, "cli-width": { @@ -3431,8 +3449,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -3463,10 +3481,10 @@ "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", "dev": true, "requires": { - "for-own": "^1.0.0", - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.0", - "shallow-clone": "^1.0.0" + "for-own": "1.0.0", + "is-plain-object": "2.0.4", + "kind-of": "6.0.2", + "shallow-clone": "1.0.0" } }, "clone-stats": { @@ -3481,9 +3499,9 @@ "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", "dev": true, "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "inherits": "2.0.3", + "process-nextick-args": "2.0.0", + "readable-stream": "2.3.6" } }, "co": { @@ -3505,7 +3523,7 @@ "dev": true, "requires": { "argv": "0.0.2", - "request": "^2.81.0", + "request": "2.88.0", "urlgrey": "0.4.4" } }, @@ -3515,12 +3533,12 @@ "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", "dev": true, "requires": { - "app-root-path": "^2.1.0", - "css-selector-tokenizer": "^0.7.0", - "cssauron": "^1.4.0", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.1" + "app-root-path": "2.1.0", + "css-selector-tokenizer": "0.7.1", + "cssauron": "1.4.0", + "semver-dsl": "1.0.1", + "source-map": "0.5.7", + "sprintf-js": "1.1.2" }, "dependencies": { "source-map": { @@ -3543,9 +3561,9 @@ "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", "dev": true, "requires": { - "arr-map": "^2.0.2", - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "arr-map": "2.0.2", + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "collection-visit": { @@ -3553,8 +3571,8 @@ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color-convert": { @@ -3563,7 +3581,7 @@ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -3590,7 +3608,7 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "^4.5.0" + "lodash": "4.17.13" } }, "combined-stream": { @@ -3599,7 +3617,7 @@ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -3613,12 +3631,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "compare-versions": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.3.0.tgz", - "integrity": "sha512-MAAAIOdi2s4Gl6rZ76PNcUa9IOYB+5ICdT41o5uMRf09aEu/F9RK+qhe8RjXNPwcTjGV7KU7h2P/fljThFVqyQ==", - "dev": true - }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -3642,7 +3654,7 @@ "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", "dev": true, "requires": { - "mime-db": ">= 1.38.0 < 2" + "mime-db": "1.38.0" }, "dependencies": { "mime-db": { @@ -3659,13 +3671,13 @@ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", "dev": true, "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.5", "bytes": "3.0.0", - "compressible": "~2.0.14", + "compressible": "2.0.16", "debug": "2.6.9", - "on-headers": "~1.0.1", + "on-headers": "1.0.1", "safe-buffer": "5.1.2", - "vary": "~1.1.2" + "vary": "1.1.2" } }, "concat-map": { @@ -3679,10 +3691,10 @@ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "buffer-from": "1.1.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" } }, "configstore": { @@ -3691,12 +3703,12 @@ "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "dev": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.3.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.4.2", + "xdg-basedir": "3.0.0" } }, "connect": { @@ -3707,7 +3719,7 @@ "requires": { "debug": "2.6.9", "finalhandler": "1.1.0", - "parseurl": "~1.3.2", + "parseurl": "1.3.2", "utils-merge": "1.0.1" }, "dependencies": { @@ -3718,12 +3730,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" } }, "statuses": { @@ -3752,7 +3764,7 @@ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "dev": true, "requires": { - "date-now": "^0.1.4" + "date-now": "0.1.4" } }, "console-control-strings": { @@ -3803,12 +3815,12 @@ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" } }, "copy-descriptor": { @@ -3822,8 +3834,8 @@ "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", "dev": true, "requires": { - "each-props": "^1.3.0", - "is-plain-object": "^2.0.1" + "each-props": "1.3.2", + "is-plain-object": "2.0.4" } }, "copy-webpack-plugin": { @@ -3832,14 +3844,14 @@ "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", "dev": true, "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" + "cacache": "10.0.4", + "find-cache-dir": "1.0.0", + "globby": "7.1.1", + "is-glob": "4.0.0", + "loader-utils": "1.2.3", + "minimatch": "3.0.4", + "p-limit": "1.2.0", + "serialize-javascript": "1.6.1" }, "dependencies": { "globby": { @@ -3848,12 +3860,12 @@ "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "glob": "7.1.2", + "ignore": "3.3.8", + "pify": "3.0.0", + "slash": "1.0.0" } } } @@ -3874,10 +3886,10 @@ "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", "dev": true, "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" + "is-directory": "0.3.1", + "js-yaml": "3.13.1", + "parse-json": "4.0.0", + "require-from-string": "2.0.2" }, "dependencies": { "parse-json": { @@ -3886,8 +3898,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "error-ex": "1.3.1", + "json-parse-better-errors": "1.0.2" } } } @@ -3898,8 +3910,8 @@ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "bn.js": "4.11.8", + "elliptic": "6.4.1" } }, "create-error-class": { @@ -3908,7 +3920,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.1" } }, "create-hash": { @@ -3917,11 +3929,11 @@ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "md5.js": "1.3.5", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" } }, "create-hmac": { @@ -3930,12 +3942,12 @@ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" } }, "cross-spawn": { @@ -3944,8 +3956,8 @@ "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "which": "1.3.1" } }, "crypto-browserify": { @@ -3954,17 +3966,17 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "browserify-cipher": "1.0.1", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.3", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", + "inherits": "2.0.3", + "pbkdf2": "3.0.17", + "public-encrypt": "4.0.3", + "randombytes": "2.1.0", + "randomfill": "1.0.4" } }, "crypto-random-string": { @@ -3985,9 +3997,9 @@ "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", "dev": true, "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" + "cssesc": "0.1.0", + "fastparse": "1.1.2", + "regexpu-core": "1.0.0" } }, "cssauron": { @@ -3996,7 +4008,7 @@ "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", "dev": true, "requires": { - "through": "X.X.X" + "through": "2.3.8" } }, "cssesc": { @@ -4017,7 +4029,7 @@ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "array-find-index": "^1.0.1" + "array-find-index": "1.0.2" } }, "custom-event": { @@ -4038,7 +4050,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "0.10.44" } }, "d3": { @@ -4088,11 +4100,11 @@ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-interpolate": "1.1.6", + "d3-selection": "1.3.0", + "d3-transition": "1.1.1" } }, "d3-color": { @@ -4105,10 +4117,10 @@ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==", "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-collection": "1.0.4", + "d3-dispatch": "1.0.3", + "d3-quadtree": "1.0.3", + "d3-timer": "1.0.7" } }, "d3-format": { @@ -4126,7 +4138,7 @@ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz", "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==", "requires": { - "d3-color": "1" + "d3-color": "1.0.3" } }, "d3-selection": { @@ -4139,7 +4151,7 @@ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", "requires": { - "d3-path": "1" + "d3-path": "1.0.5" } }, "d3-time-format": { @@ -4147,7 +4159,7 @@ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", "requires": { - "d3-time": "1" + "d3-time": "1.0.8" } } } @@ -4167,11 +4179,11 @@ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz", "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-interpolate": "1.3.2", + "d3-selection": "1.4.0", + "d3-transition": "1.1.1" } }, "d3-chord": { @@ -4179,8 +4191,8 @@ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", "requires": { - "d3-array": "1", - "d3-path": "1" + "d3-array": "1.2.4", + "d3-path": "1.0.5" } }, "d3-collection": { @@ -4203,8 +4215,8 @@ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", "requires": { - "d3-dispatch": "1", - "d3-selection": "1" + "d3-dispatch": "1.0.3", + "d3-selection": "1.4.0" } }, "d3-dsv": { @@ -4212,9 +4224,9 @@ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", "requires": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" + "commander": "2.15.1", + "iconv-lite": "0.4.19", + "rw": "1.3.3" } }, "d3-ease": { @@ -4227,10 +4239,10 @@ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.0.tgz", "integrity": "sha512-PFLcDnRVANHMudbQlIB87gcfQorEsDIAvRpZ2bNddfM/WxdsEkyrEaOIPoydhH1I1V4HPjNLGOMLXCA0AuGQ9w==", "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-collection": "1.0.4", + "d3-dispatch": "1.0.3", + "d3-quadtree": "1.0.3", + "d3-timer": "1.0.7" } }, "d3-format": { @@ -4243,7 +4255,7 @@ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz", "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==", "requires": { - "d3-array": "1" + "d3-array": "1.2.4" } }, "d3-hierarchy": { @@ -4256,7 +4268,7 @@ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", "requires": { - "d3-color": "1" + "d3-color": "1.2.3" } }, "d3-path": { @@ -4289,10 +4301,10 @@ "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz", "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==", "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-dsv": "1", - "xmlhttprequest": "1" + "d3-collection": "1.0.4", + "d3-dispatch": "1.0.3", + "d3-dsv": "1.0.8", + "xmlhttprequest": "1.8.0" } }, "d3-scale": { @@ -4300,13 +4312,13 @@ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-color": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "1.2.4", + "d3-collection": "1.0.4", + "d3-color": "1.2.3", + "d3-format": "1.3.2", + "d3-interpolate": "1.3.2", + "d3-time": "1.0.8", + "d3-time-format": "2.1.3" } }, "d3-selection": { @@ -4319,7 +4331,7 @@ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.4.tgz", "integrity": "sha512-izaz4fOpOnY3CD17hkZWNxbaN70sIGagLR/5jb6RS96Y+6VqX+q1BQf1av6QSBRdfULi3Gb8Js4CzG4+KAPjMg==", "requires": { - "d3-path": "1" + "d3-path": "1.0.5" } }, "d3-time": { @@ -4332,7 +4344,7 @@ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", "requires": { - "d3-time": "1" + "d3-time": "1.0.8" } }, "d3-timer": { @@ -4345,12 +4357,12 @@ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" + "d3-color": "1.2.3", + "d3-dispatch": "1.0.3", + "d3-ease": "1.0.3", + "d3-interpolate": "1.3.2", + "d3-selection": "1.4.0", + "d3-timer": "1.0.7" } }, "d3-voronoi": { @@ -4363,11 +4375,11 @@ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz", "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-interpolate": "1.3.2", + "d3-selection": "1.4.0", + "d3-transition": "1.1.1" } }, "dashdash": { @@ -4376,7 +4388,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "date-format": { @@ -4440,7 +4452,7 @@ "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, "requires": { - "kind-of": "^5.0.2" + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -4457,8 +4469,8 @@ "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", "dev": true, "requires": { - "execa": "^0.10.0", - "ip-regex": "^2.1.0" + "execa": "0.10.0", + "ip-regex": "2.1.0" }, "dependencies": { "cross-spawn": { @@ -4467,11 +4479,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.5.0", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "execa": { @@ -4480,13 +4492,13 @@ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "6.0.5", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } } } @@ -4497,7 +4509,7 @@ "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", "dev": true, "requires": { - "strip-bom": "^3.0.0" + "strip-bom": "3.0.0" }, "dependencies": { "strip-bom": { @@ -4520,8 +4532,8 @@ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "dev": true, "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "foreach": "2.0.5", + "object-keys": "1.0.11" } }, "define-property": { @@ -4529,8 +4541,8 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -4538,7 +4550,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -4546,7 +4558,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -4554,9 +4566,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -4567,12 +4579,12 @@ "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", "dev": true, "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" + "globby": "6.1.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "p-map": "1.2.0", + "pify": "3.0.0", + "rimraf": "2.6.2" }, "dependencies": { "globby": { @@ -4581,11 +4593,11 @@ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" }, "dependencies": { "pify": { @@ -4616,12 +4628,12 @@ "integrity": "sha1-fP7PXvffuAL60nq49b5a5UbFt54=", "dev": true, "requires": { - "async": "^1.5.2", - "bluebird": "^3.3.5", - "extend-shallow": "^2.0.1", - "lazy-cache": "^1.0.4", - "matched": "^0.4.1", - "rimraf": "^2.5.2" + "async": "1.5.2", + "bluebird": "3.5.1", + "extend-shallow": "2.0.1", + "lazy-cache": "1.0.4", + "matched": "0.4.4", + "rimraf": "2.6.2" }, "dependencies": { "extend-shallow": { @@ -4630,7 +4642,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -4653,8 +4665,8 @@ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "destroy": { @@ -4675,7 +4687,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "detect-node": { @@ -4702,9 +4714,9 @@ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.1.0" } }, "dir-glob": { @@ -4713,8 +4725,8 @@ "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", "dev": true, "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" + "arrify": "1.0.1", + "path-type": "3.0.0" } }, "dns-equal": { @@ -4729,8 +4741,8 @@ "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", "dev": true, "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" + "ip": "1.1.5", + "safe-buffer": "5.1.2" } }, "dns-txt": { @@ -4739,7 +4751,7 @@ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "dev": true, "requires": { - "buffer-indexof": "^1.0.0" + "buffer-indexof": "1.1.1" } }, "doctrine": { @@ -4748,8 +4760,8 @@ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } }, "dom-serialize": { @@ -4758,10 +4770,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "custom-event": "1.0.1", + "ent": "2.2.0", + "extend": "3.0.1", + "void-elements": "2.0.1" } }, "domain-browser": { @@ -4781,7 +4793,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "duplexer": { @@ -4802,10 +4814,10 @@ "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "dev": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" } }, "each-props": { @@ -4814,8 +4826,8 @@ "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, "requires": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0" } }, "ecc-jsbn": { @@ -4825,7 +4837,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "ee-first": { @@ -4846,13 +4858,13 @@ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.7", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, "emojis-list": { @@ -4873,7 +4885,7 @@ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "dev": true, "requires": { - "iconv-lite": "~0.4.13" + "iconv-lite": "0.4.19" } }, "end-of-stream": { @@ -4882,7 +4894,7 @@ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "engine.io": { @@ -4891,12 +4903,12 @@ "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" + "debug": "3.1.0", + "engine.io-parser": "2.1.3", + "ws": "3.3.3" }, "dependencies": { "debug": { @@ -4918,14 +4930,14 @@ "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", + "debug": "3.1.0", + "engine.io-parser": "2.1.3", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", "yeast": "0.1.2" }, "dependencies": { @@ -4947,10 +4959,10 @@ "dev": true, "requires": { "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", + "arraybuffer.slice": "0.0.7", "base64-arraybuffer": "0.1.5", "blob": "0.0.5", - "has-binary2": "~1.0.2" + "has-binary2": "1.0.3" } }, "enhanced-resolve": { @@ -4959,9 +4971,9 @@ "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" + "graceful-fs": "4.1.11", + "memory-fs": "0.4.1", + "tapable": "1.1.1" } }, "ent": { @@ -4982,7 +4994,7 @@ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "error-ex": { @@ -4991,7 +5003,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "error-stack-parser": { @@ -4999,7 +5011,7 @@ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.1.tgz", "integrity": "sha1-oyArj7AxFKqbQKDjZp5IsrZaAQo=", "requires": { - "stackframe": "^1.0.3" + "stackframe": "1.0.4" } }, "es-abstract": { @@ -5008,11 +5020,11 @@ "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -5021,9 +5033,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" } }, "es5-ext": { @@ -5032,9 +5044,9 @@ "integrity": "sha512-TO4Vt9IhW3FzDKLDOpoA8VS9BCV4b9WTf6BqvMOgfoa8wX73F3Kh3y2J7yTstTaXlQ0k1vq4DH2vw6RSs42z+g==", "dev": true, "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "next-tick": "1.0.0" } }, "es6-iterator": { @@ -5043,9 +5055,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.44", + "es6-symbol": "3.1.1" } }, "es6-map": { @@ -5054,12 +5066,12 @@ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "d": "1.0.0", + "es5-ext": "0.10.44", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" } }, "es6-promise": { @@ -5074,7 +5086,7 @@ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "es6-promise": "^4.0.3" + "es6-promise": "4.2.4" } }, "es6-set": { @@ -5083,11 +5095,11 @@ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", + "d": "1.0.0", + "es5-ext": "0.10.44", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" + "event-emitter": "0.3.5" } }, "es6-symbol": { @@ -5096,8 +5108,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.44" } }, "es6-weak-map": { @@ -5106,10 +5118,10 @@ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.44", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "escape-html": { @@ -5130,11 +5142,11 @@ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", "dev": true, "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" + "esprima": "2.7.3", + "estraverse": "1.9.3", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.2.0" }, "dependencies": { "esprima": { @@ -5156,7 +5168,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -5167,10 +5179,10 @@ "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "dev": true, "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "eslint": { @@ -5179,39 +5191,39 @@ "integrity": "sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE=", "dev": true, "requires": { - "chalk": "^1.1.3", - "concat-stream": "^1.4.6", - "debug": "^2.1.1", - "doctrine": "^1.2.2", - "es6-map": "^0.1.3", - "escope": "^3.6.0", - "espree": "^3.1.6", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^1.1.1", - "glob": "^7.0.3", - "globals": "^9.2.0", - "ignore": "^3.1.2", - "imurmurhash": "^0.1.4", - "inquirer": "^0.12.0", - "is-my-json-valid": "^2.10.0", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.5.1", - "json-stable-stringify": "^1.0.0", - "levn": "^0.3.0", - "lodash": "^4.0.0", - "mkdirp": "^0.5.0", - "optionator": "^0.8.1", - "path-is-absolute": "^1.0.0", - "path-is-inside": "^1.0.1", - "pluralize": "^1.2.1", - "progress": "^1.1.8", - "require-uncached": "^1.0.2", - "shelljs": "^0.6.0", - "strip-json-comments": "~1.0.1", - "table": "^3.7.8", - "text-table": "~0.2.0", - "user-home": "^2.0.0" + "chalk": "1.1.3", + "concat-stream": "1.6.2", + "debug": "2.6.9", + "doctrine": "1.5.0", + "es6-map": "0.1.5", + "escope": "3.6.0", + "espree": "3.5.4", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "1.3.1", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.8", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.17.2", + "is-resolvable": "1.1.0", + "js-yaml": "3.13.1", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "optionator": "0.8.2", + "path-is-absolute": "1.0.1", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.6.1", + "strip-json-comments": "1.0.4", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -5226,13 +5238,19 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -5247,8 +5265,8 @@ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "espree": { @@ -5257,8 +5275,8 @@ "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "acorn": "5.7.3", + "acorn-jsx": "3.0.1" }, "dependencies": { "acorn": { @@ -5281,7 +5299,7 @@ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "4.2.0" } }, "estraverse": { @@ -5314,8 +5332,8 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.44" } }, "event-stream": { @@ -5324,13 +5342,13 @@ "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" } }, "eventemitter3": { @@ -5351,7 +5369,7 @@ "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", "dev": true, "requires": { - "original": "^1.0.0" + "original": "1.0.2" } }, "evp_bytestokey": { @@ -5360,8 +5378,8 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "md5.js": "1.3.5", + "safe-buffer": "5.1.2" } }, "execa": { @@ -5370,13 +5388,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" }, "dependencies": { "cross-spawn": { @@ -5385,9 +5403,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" } } } @@ -5410,9 +5428,9 @@ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", "dev": true, "requires": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" + "array-slice": "0.2.3", + "array-unique": "0.2.1", + "braces": "0.1.5" }, "dependencies": { "array-slice": { @@ -5433,7 +5451,7 @@ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", "dev": true, "requires": { - "expand-range": "^0.1.0" + "expand-range": "0.1.1" } } } @@ -5443,13 +5461,13 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -5457,7 +5475,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -5465,7 +5483,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5476,8 +5494,8 @@ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", "dev": true, "requires": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" + "is-number": "0.1.1", + "repeat-string": "0.2.2" }, "dependencies": { "is-number": { @@ -5500,7 +5518,7 @@ "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", "dev": true, "requires": { - "os-homedir": "^1.0.1" + "os-homedir": "1.0.2" } }, "express": { @@ -5509,36 +5527,36 @@ "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", "dev": true, "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.3", "content-disposition": "0.5.2", - "content-type": "~1.0.4", + "content-type": "1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", + "proxy-addr": "2.0.4", "qs": "6.5.2", - "range-parser": "~1.2.0", + "range-parser": "1.2.0", "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "statuses": "1.4.0", + "type-is": "1.6.16", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { "array-flatten": { @@ -5552,7 +5570,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } @@ -5568,8 +5586,8 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -5577,7 +5595,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -5588,9 +5606,9 @@ "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "chardet": "0.7.0", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" }, "dependencies": { "iconv-lite": { @@ -5599,7 +5617,7 @@ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } @@ -5609,14 +5627,14 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -5624,7 +5642,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -5632,7 +5650,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -5640,7 +5658,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -5648,7 +5666,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -5656,9 +5674,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -5675,10 +5693,10 @@ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "parse-node-version": "1.0.1", + "time-stamp": "1.1.0" } }, "fast-deep-equal": { @@ -5693,12 +5711,12 @@ "integrity": "sha512-TR6zxCKftDQnUAPvkrCWdBgDq/gbqx8A3ApnBrR5rMvpp6+KMJI0Igw7fkWPgeVK0uhRXTXdvO3O+YP0CaUX2g==", "dev": true, "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.0.1", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.1", - "micromatch": "^3.1.10" + "@mrmlnc/readdir-enhanced": "2.2.1", + "@nodelib/fs.stat": "1.1.2", + "glob-parent": "3.1.0", + "is-glob": "4.0.0", + "merge2": "1.2.2", + "micromatch": "3.1.10" } }, "fast-json-stable-stringify": { @@ -5724,7 +5742,7 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.0" } }, "figgy-pudding": { @@ -5739,8 +5757,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" } }, "file-entry-cache": { @@ -5749,8 +5767,8 @@ "integrity": "sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.0", + "object-assign": "4.1.1" } }, "file-loader": { @@ -5759,8 +5777,8 @@ "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", "dev": true, "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" + "loader-utils": "1.2.3", + "schema-utils": "1.0.0" } }, "fileset": { @@ -5769,8 +5787,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "glob": "7.1.2", + "minimatch": "3.0.4" } }, "fill-range": { @@ -5778,10 +5796,10 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -5789,7 +5807,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5801,12 +5819,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.4.0", + "unpipe": "1.0.0" } }, "find-cache-dir": { @@ -5815,9 +5833,9 @@ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "dev": true, "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" + "commondir": "1.0.1", + "make-dir": "1.3.0", + "pkg-dir": "2.0.0" } }, "find-parent-dir": { @@ -5832,7 +5850,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "findup-sync": { @@ -5841,10 +5859,10 @@ "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "dev": true, "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "detect-file": "1.0.0", + "is-glob": "4.0.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" }, "dependencies": { "expand-tilde": { @@ -5898,11 +5916,11 @@ "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" }, "dependencies": { "expand-tilde": { @@ -5928,10 +5946,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" }, "dependencies": { "circular-json": { @@ -5946,13 +5964,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" } }, "globby": { @@ -5961,12 +5979,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -5989,8 +6007,8 @@ "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "follow-redirects": { @@ -5999,7 +6017,7 @@ "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==", "dev": true, "requires": { - "debug": "^3.1.0" + "debug": "3.1.0" }, "dependencies": { "debug": { @@ -6024,7 +6042,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "foreach": { @@ -6045,9 +6063,9 @@ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.18" } }, "formatio": { @@ -6056,7 +6074,7 @@ "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", "dev": true, "requires": { - "samsam": "~1.1" + "samsam": "1.1.2" } }, "forwarded": { @@ -6070,7 +6088,7 @@ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fresh": { @@ -6091,8 +6109,8 @@ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "front-matter": { @@ -6101,7 +6119,7 @@ "integrity": "sha1-91mDufL0E75ljJPf172M5AePXNs=", "dev": true, "requires": { - "js-yaml": "^3.4.6" + "js-yaml": "3.13.1" } }, "fs-access": { @@ -6110,7 +6128,7 @@ "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, "requires": { - "null-check": "^1.0.0" + "null-check": "1.0.0" } }, "fs-exists-sync": { @@ -6125,9 +6143,9 @@ "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^3.0.0", - "universalify": "^0.1.0" + "graceful-fs": "4.1.11", + "jsonfile": "3.0.1", + "universalify": "0.1.1" } }, "fs-minipass": { @@ -6136,7 +6154,7 @@ "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "fs-mkdirp-stream": { @@ -6145,8 +6163,8 @@ "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" + "graceful-fs": "4.1.11", + "through2": "2.0.3" } }, "fs-write-stream-atomic": { @@ -6155,10 +6173,10 @@ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.6" } }, "fs.realpath": { @@ -6173,8 +6191,8 @@ "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.10.0", + "node-pre-gyp": "0.10.3" }, "dependencies": { "abbrev": { @@ -6184,8 +6202,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -6197,21 +6214,19 @@ "bundled": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -6222,18 +6237,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -6268,7 +6280,7 @@ "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "fs.realpath": { @@ -6281,14 +6293,14 @@ "bundled": true, "optional": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" } }, "glob": { @@ -6296,12 +6308,12 @@ "bundled": true, "optional": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -6314,7 +6326,7 @@ "bundled": true, "optional": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -6322,7 +6334,7 @@ "bundled": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -6330,14 +6342,13 @@ "bundled": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -6347,9 +6358,8 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -6360,23 +6370,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.3.5", "bundled": true, - "optional": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "minizlib": { @@ -6384,13 +6391,12 @@ "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6405,9 +6411,9 @@ "bundled": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "2.6.9", + "iconv-lite": "0.4.24", + "sax": "1.2.4" } }, "node-pre-gyp": { @@ -6415,16 +6421,16 @@ "bundled": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.4", + "nopt": "4.0.1", + "npm-packlist": "1.2.0", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.6.3", + "semver": "5.6.0", + "tar": "4.4.8" } }, "nopt": { @@ -6432,8 +6438,8 @@ "bundled": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { @@ -6446,8 +6452,8 @@ "bundled": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.5" } }, "npmlog": { @@ -6455,16 +6461,15 @@ "bundled": true, "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -6474,9 +6479,8 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -6494,8 +6498,8 @@ "bundled": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -6513,10 +6517,10 @@ "bundled": true, "optional": true, "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -6531,13 +6535,13 @@ "bundled": true, "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "rimraf": { @@ -6545,13 +6549,12 @@ "bundled": true, "optional": true, "requires": { - "glob": "^7.1.3" + "glob": "7.1.3" } }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -6581,11 +6584,10 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -6593,15 +6595,14 @@ "bundled": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -6614,13 +6615,13 @@ "bundled": true, "optional": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "chownr": "1.1.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.5", + "minizlib": "1.2.1", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "util-deprecate": { @@ -6633,18 +6634,16 @@ "bundled": true, "optional": true, "requires": { - "string-width": "^1.0.2 || 2" + "string-width": "1.0.2" } }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -6654,10 +6653,10 @@ "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" } }, "function-bind": { @@ -6672,14 +6671,14 @@ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" } }, "gaze": { @@ -6688,7 +6687,7 @@ "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, "requires": { - "globule": "^1.0.0" + "globule": "1.2.1" } }, "generate-function": { @@ -6703,7 +6702,7 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true, "requires": { - "is-property": "^1.0.0" + "is-property": "1.0.2" } }, "genfun": { @@ -6741,7 +6740,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { @@ -6750,12 +6749,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-parent": { @@ -6763,8 +6762,8 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -6772,7 +6771,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -6783,16 +6782,16 @@ "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", "dev": true, "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" + "extend": "3.0.1", + "glob": "7.1.2", + "glob-parent": "3.1.0", + "is-negated-glob": "1.0.0", + "ordered-read-streams": "1.0.1", + "pumpify": "1.5.1", + "readable-stream": "2.3.6", + "remove-trailing-separator": "1.1.0", + "to-absolute-glob": "2.0.2", + "unique-stream": "2.3.1" } }, "glob-to-regexp": { @@ -6807,12 +6806,12 @@ "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "object.defaults": "^1.1.0" + "anymatch": "2.0.0", + "async-done": "1.3.1", + "chokidar": "2.0.3", + "is-negated-glob": "1.0.0", + "just-debounce": "1.0.0", + "object.defaults": "1.1.0" } }, "global-dirs": { @@ -6821,7 +6820,7 @@ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "1.3.5" } }, "global-modules": { @@ -6830,8 +6829,8 @@ "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", "dev": true, "requires": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" + "global-prefix": "0.1.5", + "is-windows": "0.2.0" }, "dependencies": { "is-windows": { @@ -6848,10 +6847,10 @@ "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "0.2.0", + "which": "1.3.1" }, "dependencies": { "is-windows": { @@ -6874,13 +6873,13 @@ "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "fast-glob": "2.2.2", + "glob": "7.1.2", + "ignore": "3.3.8", + "pify": "3.0.0", + "slash": "1.0.0" } }, "globule": { @@ -6889,9 +6888,9 @@ "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", "dev": true, "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" + "glob": "7.1.2", + "lodash": "4.17.13", + "minimatch": "3.0.4" } }, "glogg": { @@ -6900,7 +6899,7 @@ "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "gonzales-pe-sl": { @@ -6909,7 +6908,7 @@ "integrity": "sha1-aoaLw4BkXxQf7rBCxvl/zHG1n+Y=", "dev": true, "requires": { - "minimist": "1.1.x" + "minimist": "1.1.3" }, "dependencies": { "minimist": { @@ -6926,17 +6925,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" } }, "graceful-fs": { @@ -6950,7 +6949,7 @@ "integrity": "sha1-5ZnkBzPvgOFlO/6JpfAx7PKqSqo=", "dev": true, "requires": { - "temp": "~0.4.0" + "temp": "0.4.0" } }, "gulp": { @@ -6959,10 +6958,10 @@ "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, "requires": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" + "glob-watcher": "5.0.3", + "gulp-cli": "2.2.0", + "undertaker": "1.2.1", + "vinyl-fs": "3.0.3" }, "dependencies": { "camelcase": { @@ -6988,24 +6987,24 @@ "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", "dev": true, "requires": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.1.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.0.1", - "yargs": "^7.1.0" + "ansi-colors": "1.1.0", + "archy": "1.0.0", + "array-sort": "1.0.0", + "color-support": "1.1.3", + "concat-stream": "1.6.2", + "copy-props": "2.0.4", + "fancy-log": "1.3.3", + "gulplog": "1.0.0", + "interpret": "1.1.0", + "isobject": "3.0.1", + "liftoff": "3.1.0", + "matchdep": "2.0.0", + "mute-stdout": "1.0.1", + "pretty-hrtime": "1.0.3", + "replace-homedir": "1.0.0", + "semver-greatest-satisfied-range": "1.1.0", + "v8flags": "3.1.0", + "yargs": "7.1.0" } }, "y18n": { @@ -7043,11 +7042,11 @@ "integrity": "sha512-I+697f6jf+PncdTrqfuwoauxgnLG1yHRg3vlmvDgmJuEnlEHy4meBktJ/oHgfyg4tp6X25wuZqUOraVeVg97wQ==", "dev": true, "requires": { - "get-stream": "^3.0.0", - "plugin-error": "^0.1.2", - "through2": "^2.0.1", - "vinyl": "^2.1.0", - "yazl": "^2.1.0" + "get-stream": "3.0.0", + "plugin-error": "0.1.2", + "through2": "2.0.3", + "vinyl": "2.1.0", + "yazl": "2.4.3" } }, "gulplog": { @@ -7056,7 +7055,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "^1.0.0" + "glogg": "1.0.2" } }, "hammerjs": { @@ -7071,15 +7070,15 @@ "dev": true }, "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", + "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "neo-async": "2.6.0", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "source-map": { @@ -7126,8 +7125,8 @@ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "ajv": "6.9.1", + "har-schema": "2.0.0" }, "dependencies": { "ajv": { @@ -7136,10 +7135,10 @@ "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "fast-deep-equal": { @@ -7160,7 +7159,7 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } } } @@ -7171,7 +7170,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -7180,7 +7179,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-binary2": { @@ -7218,7 +7217,7 @@ "integrity": "sha1-omHEwqbGZ+DHe3AKfyl8Oe86pYk=", "dev": true, "requires": { - "is-glob": "^2.0.1" + "is-glob": "2.0.1" }, "dependencies": { "is-extglob": { @@ -7233,7 +7232,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -7255,9 +7254,9 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -7265,8 +7264,8 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -7274,7 +7273,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7285,8 +7284,8 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "hash.js": { @@ -7295,8 +7294,8 @@ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "hmac-drbg": { @@ -7305,9 +7304,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "hash.js": "1.1.7", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, "homedir-polyfill": { @@ -7316,7 +7315,7 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "parse-passwd": "1.0.0" } }, "hosted-git-info": { @@ -7331,10 +7330,10 @@ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "inherits": "2.0.3", + "obuf": "1.1.2", + "readable-stream": "2.3.6", + "wbuf": "1.7.3" } }, "html-entities": { @@ -7361,10 +7360,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.4.0" } }, "http-parser-js": { @@ -7379,9 +7378,9 @@ "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "dev": true, "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "eventemitter3": "3.1.0", + "follow-redirects": "1.5.0", + "requires-port": "1.0.0" } }, "http-proxy-agent": { @@ -7390,7 +7389,7 @@ "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "dev": true, "requires": { - "agent-base": "4", + "agent-base": "4.2.0", "debug": "3.1.0" }, "dependencies": { @@ -7411,10 +7410,10 @@ "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { - "http-proxy": "^1.16.2", - "is-glob": "^4.0.0", - "lodash": "^4.17.5", - "micromatch": "^3.1.9" + "http-proxy": "1.17.0", + "is-glob": "4.0.0", + "lodash": "4.17.13", + "micromatch": "3.1.10" } }, "http-signature": { @@ -7423,9 +7422,9 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.1" } }, "https-browserify": { @@ -7440,8 +7439,8 @@ "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", "dev": true, "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" + "agent-base": "4.2.0", + "debug": "3.1.0" }, "dependencies": { "debug": { @@ -7461,7 +7460,7 @@ "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "dev": true, "requires": { - "ms": "^2.0.0" + "ms": "2.0.0" } }, "iconv-lite": { @@ -7493,7 +7492,7 @@ "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "image-size": { @@ -7520,7 +7519,7 @@ "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", "dev": true, "requires": { - "import-from": "^2.1.0" + "import-from": "2.1.0" } }, "import-from": { @@ -7529,7 +7528,7 @@ "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "3.0.0" } }, "import-lazy": { @@ -7544,8 +7543,8 @@ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "dev": true, "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "3.0.0", + "resolve-cwd": "2.0.0" }, "dependencies": { "find-up": { @@ -7554,7 +7553,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "locate-path": { @@ -7563,8 +7562,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "p-limit": { @@ -7573,7 +7572,7 @@ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.0.0" } }, "p-locate": { @@ -7582,7 +7581,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.1.0" } }, "p-try": { @@ -7597,7 +7596,7 @@ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "3.0.0" } } } @@ -7620,7 +7619,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "indexof": { @@ -7635,8 +7634,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -7662,19 +7661,19 @@ "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "dev": true, "requires": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.2.0", + "figures": "1.7.0", + "lodash": "4.17.10", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" }, "dependencies": { "ansi-styles": { @@ -7689,13 +7688,19 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -7710,8 +7715,8 @@ "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", "dev": true, "requires": { - "default-gateway": "^2.6.0", - "ipaddr.js": "^1.5.2" + "default-gateway": "2.7.2", + "ipaddr.js": "1.8.0" } }, "interpret": { @@ -7720,13 +7725,18 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, + "intersect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/intersect/-/intersect-1.0.1.tgz", + "integrity": "sha1-MyZQ4QhU2MCsWMGSvcJ6i/fnoww=" + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -7759,8 +7769,8 @@ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "is-relative": "1.0.0", + "is-windows": "1.0.2" } }, "is-accessor-descriptor": { @@ -7768,7 +7778,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7776,7 +7786,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7792,7 +7802,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.11.0" } }, "is-buffer": { @@ -7806,7 +7816,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-callable": { @@ -7821,7 +7831,7 @@ "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "requires": { - "ci-info": "^1.5.0" + "ci-info": "1.6.0" } }, "is-data-descriptor": { @@ -7829,7 +7839,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7837,7 +7847,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7853,9 +7863,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -7887,7 +7897,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -7896,7 +7906,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-glob": { @@ -7904,7 +7914,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-installed-globally": { @@ -7913,8 +7923,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" } }, "is-module": { @@ -7935,11 +7945,11 @@ "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", "dev": true, "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "is-my-ip-valid": "^1.0.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "is-my-ip-valid": "1.0.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" } }, "is-negated-glob": { @@ -7959,7 +7969,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7967,7 +7977,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7983,7 +7993,7 @@ "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "requires": { - "is-number": "^4.0.0" + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -8005,7 +8015,7 @@ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "1.0.1" } }, "is-path-inside": { @@ -8014,7 +8024,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-plain-object": { @@ -8022,7 +8032,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-promise": { @@ -8049,7 +8059,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "^1.0.1" + "has": "1.0.3" } }, "is-relative": { @@ -8058,7 +8068,7 @@ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "^1.0.0" + "is-unc-path": "1.0.0" } }, "is-resolvable": { @@ -8103,7 +8113,7 @@ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "unc-path-regex": "0.1.2" } }, "is-utf8": { @@ -8140,7 +8150,7 @@ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "dev": true, "requires": { - "buffer-alloc": "^1.2.0" + "buffer-alloc": "1.2.0" } }, "isexe": { @@ -8166,20 +8176,20 @@ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "abbrev": "1.0.9", + "async": "1.5.2", + "escodegen": "1.8.1", + "esprima": "2.7.3", + "glob": "5.0.15", + "handlebars": "4.1.1", + "js-yaml": "3.13.1", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "once": "1.4.0", + "resolve": "1.1.7", + "supports-color": "3.2.3", + "which": "1.3.1", + "wordwrap": "1.0.0" }, "dependencies": { "esprima": { @@ -8194,11 +8204,11 @@ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-flag": { @@ -8219,38 +8229,42 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } }, "istanbul-api": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.1.tgz", - "integrity": "sha512-duj6AlLcsWNwUpfyfHt0nWIeRiZpuShnP40YTxOGQgtaN8fd6JYSxsvxUphTDy8V5MfDXo4s/xVCIIvVCO808g==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", + "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", "dev": true, "requires": { - "async": "^2.1.4", - "compare-versions": "^3.1.0", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.0", - "istanbul-lib-hook": "^1.2.0", - "istanbul-lib-instrument": "^1.10.1", - "istanbul-lib-report": "^1.1.4", - "istanbul-lib-source-maps": "^1.2.4", - "istanbul-reports": "^1.3.0", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" + "async": "2.6.2", + "fileset": "2.0.3", + "istanbul-lib-coverage": "1.2.0", + "istanbul-lib-hook": "1.2.1", + "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-report": "1.1.4", + "istanbul-lib-source-maps": "1.2.5", + "istanbul-reports": "1.3.0", + "js-yaml": "3.13.1", + "mkdirp": "0.5.1", + "once": "1.4.0" }, "dependencies": { + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=" + }, "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", "dev": true, "requires": { - "lodash": "^4.17.10" + "lodash": "4.17.13" } } } @@ -8261,10 +8275,10 @@ "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", "dev": true, "requires": { - "convert-source-map": "^1.5.0", - "istanbul-lib-instrument": "^1.7.3", - "loader-utils": "^1.1.0", - "schema-utils": "^0.3.0" + "convert-source-map": "1.5.1", + "istanbul-lib-instrument": "1.10.1", + "loader-utils": "1.2.3", + "schema-utils": "0.3.0" }, "dependencies": { "ajv": { @@ -8273,10 +8287,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "schema-utils": { @@ -8285,7 +8299,7 @@ "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "dev": true, "requires": { - "ajv": "^5.0.0" + "ajv": "5.5.2" } } } @@ -8302,7 +8316,7 @@ "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==", "dev": true, "requires": { - "append-transform": "^1.0.0" + "append-transform": "1.0.0" } }, "istanbul-lib-instrument": { @@ -8311,13 +8325,13 @@ "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "istanbul-lib-report": { @@ -8326,10 +8340,10 @@ "integrity": "sha512-Azqvq5tT0U09nrncK3q82e/Zjkxa4tkFZv7E6VcqP0QCPn6oNljDPfrZEC/umNXds2t7b8sRJfs6Kmpzt8m2kA==", "dev": true, "requires": { - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" }, "dependencies": { "has-flag": { @@ -8344,7 +8358,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -8355,11 +8369,11 @@ "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.6" }, "dependencies": { "debug": { @@ -8379,7 +8393,7 @@ "integrity": "sha512-y2Z2IMqE1gefWUaVjrBm0mSKvUkaBy9Vqz8iwr/r40Y9hBbIteH5wqHG/9DLTfJ9xUnUT2j7A3+VVJ6EaYBllA==", "dev": true, "requires": { - "handlebars": "^4.0.3" + "handlebars": "4.1.1" } }, "jasmine": { @@ -8388,9 +8402,9 @@ "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", "dev": true, "requires": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" + "exit": "0.1.2", + "glob": "7.1.2", + "jasmine-core": "2.8.0" }, "dependencies": { "jasmine-core": { @@ -8440,8 +8454,8 @@ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "jsbn": { @@ -8481,7 +8495,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stable-stringify-without-jsonify": { @@ -8508,7 +8522,7 @@ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.0" }, "dependencies": { "minimist": { @@ -8525,7 +8539,7 @@ "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.11" } }, "jsonify": { @@ -8564,11 +8578,11 @@ "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", "dev": true, "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", - "pako": "~1.0.2", - "readable-stream": "~2.0.6" + "core-js": "2.3.0", + "es6-promise": "3.0.2", + "lie": "3.1.1", + "pako": "1.0.6", + "readable-stream": "2.0.6" }, "dependencies": { "core-js": { @@ -8595,12 +8609,12 @@ "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -8623,58 +8637,153 @@ "integrity": "sha512-EFoFs3F6G0BcUGPNOn/YloGOb3h09hzTguyXlg6loHlKY76qbJikkcyPk43m2kfRF65TUGda/mig29QQtyhm1g==", "dev": true, "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "chokidar": "^2.0.3", - "colors": "^1.1.0", - "combine-lists": "^1.0.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", - "flatted": "^2.0.0", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.5", - "log4js": "^3.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", + "bluebird": "3.5.1", + "body-parser": "1.18.3", + "chokidar": "2.0.3", + "colors": "1.1.2", + "combine-lists": "1.0.1", + "connect": "3.6.6", + "core-js": "2.6.5", + "di": "0.0.1", + "dom-serialize": "2.2.1", + "expand-braces": "0.1.2", + "flatted": "2.0.0", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "http-proxy": "1.17.0", + "isbinaryfile": "3.0.3", + "lodash": "4.17.13", + "log4js": "3.0.6", + "mime": "2.4.0", + "minimatch": "3.0.4", + "optimist": "0.6.1", + "qjobs": "1.2.0", + "range-parser": "1.2.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", "socket.io": "2.1.1", - "source-map": "^0.6.1", + "source-map": "0.6.1", "tmp": "0.0.33", "useragent": "2.3.0" }, "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "requires": { + "lodash": "4.17.13" + } + }, + "circular-json": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "dev": true + }, + "date-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz", + "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==" + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "2.1.1" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "4.1.11" + } + }, + "log4js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "dev": true, + "requires": { + "circular-json": "0.5.9", + "date-format": "1.2.0", + "debug": "3.2.6", + "rfdc": "1.1.2", + "streamroller": "0.7.0" + }, + "dependencies": { + "date-format": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", + "dev": true + }, + "streamroller": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "dev": true, + "requires": { + "date-format": "1.2.0", + "debug": "3.2.6", + "mkdirp": "0.5.1", + "readable-stream": "2.3.6" + } + } + } + }, "mime": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "streamroller": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.3.tgz", + "integrity": "sha512-P7z9NwP51EltdZ81otaGAN3ob+/F88USJE546joNq7bqRNTe6jc74fTBDyynxP4qpIfKlt/CesEYicuMzI0yJg==", + "requires": { + "async": "2.6.2", + "date-format": "2.0.0", + "debug": "3.2.6", + "fs-extra": "7.0.1", + "lodash": "4.17.13" + } } } }, "karma-chrome-launcher": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz", - "integrity": "sha1-IWh5xorATY1RQOmWGboEtZr9Rs8=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", "dev": true, "requires": { - "fs-access": "^1.0.0", - "which": "^1.2.1" + "fs-access": "1.0.1", + "which": "1.3.1" } }, "karma-cli": { @@ -8683,7 +8792,7 @@ "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.7.1" } }, "karma-coverage-istanbul-reporter": { @@ -8692,8 +8801,8 @@ "integrity": "sha512-UcgrHkFehI5+ivMouD8NH/UOHiX4oCAtwaANylzPFdcAuD52fnCUuelacq2gh8tZ4ydhU3+xiXofSq7j5Ehygw==", "dev": true, "requires": { - "istanbul-api": "^1.3.1", - "minimatch": "^3.0.4" + "istanbul-api": "1.3.7", + "minimatch": "3.0.4" } }, "karma-jasmine": { @@ -8702,7 +8811,7 @@ "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", "dev": true, "requires": { - "jasmine-core": "^3.3" + "jasmine-core": "3.3.0" } }, "karma-jasmine-html-reporter": { @@ -8717,7 +8826,7 @@ "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", "dev": true, "requires": { - "source-map-support": "^0.5.5" + "source-map-support": "0.5.10" } }, "karma-spec-reporter": { @@ -8726,7 +8835,7 @@ "integrity": "sha1-SDDccUihVcfXoYbmMjOaDYD63sM=", "dev": true, "requires": { - "colors": "^1.1.2" + "colors": "1.1.2" } }, "killable": { @@ -8746,7 +8855,7 @@ "integrity": "sha1-PTvNhgDnv971MjHHOf8FOu1WDkQ=", "dev": true, "requires": { - "graceful-fs": "^4.1.11" + "graceful-fs": "4.1.11" } }, "known-css-properties": { @@ -8761,8 +8870,8 @@ "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "dev": true, "requires": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" + "default-resolution": "2.0.0", + "es6-weak-map": "2.0.2" } }, "latest-version": { @@ -8771,7 +8880,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lazy-cache": { @@ -8786,7 +8895,7 @@ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, "requires": { - "readable-stream": "^2.0.5" + "readable-stream": "2.3.6" } }, "lcid": { @@ -8795,7 +8904,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "lead": { @@ -8804,7 +8913,7 @@ "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", "dev": true, "requires": { - "flush-write-stream": "^1.0.2" + "flush-write-stream": "1.0.3" } }, "less": { @@ -8813,15 +8922,15 @@ "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", "dev": true, "requires": { - "clone": "^2.1.2", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" + "clone": "2.1.2", + "errno": "0.1.7", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.88.0", + "source-map": "0.6.1" }, "dependencies": { "clone": { @@ -8845,9 +8954,9 @@ "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", "dev": true, "requires": { - "clone": "^2.1.1", - "loader-utils": "^1.1.0", - "pify": "^3.0.0" + "clone": "2.1.1", + "loader-utils": "1.2.3", + "pify": "3.0.0" } }, "less-plugin-npm-import": { @@ -8856,8 +8965,8 @@ "integrity": "sha1-gj5phskzGKmBccqFiEi2vq1Vvz4=", "dev": true, "requires": { - "promise": "~7.0.1", - "resolve": "~1.1.6" + "promise": "7.0.4", + "resolve": "1.1.7" }, "dependencies": { "promise": { @@ -8866,7 +8975,7 @@ "integrity": "sha1-Nj6EpMNsg1a4kP7WLJHOhdAu1Tk=", "dev": true, "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "resolve": { @@ -8883,8 +8992,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "license-webpack-plugin": { @@ -8893,8 +9002,8 @@ "integrity": "sha512-vDiBeMWxjE9n6TabQ9J4FH8urFdsRK0Nvxn1cit9biCiR9aq1zBR0X2BlAkEiIG6qPamLeU0GzvIgLkrFc398A==", "dev": true, "requires": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" + "@types/webpack-sources": "0.1.5", + "webpack-sources": "1.3.0" } }, "lie": { @@ -8903,7 +9012,7 @@ "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", "dev": true, "requires": { - "immediate": "~3.0.5" + "immediate": "3.0.6" } }, "liftoff": { @@ -8912,14 +9021,14 @@ "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", "dev": true, "requires": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "extend": "3.0.1", + "findup-sync": "3.0.0", + "fined": "1.2.0", + "flagged-respawn": "1.0.1", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.7.1" } }, "load-json-file": { @@ -8928,11 +9037,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" }, "dependencies": { "pify": { @@ -8955,9 +9064,9 @@ "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" } }, "locate-path": { @@ -8966,20 +9075,19 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==" }, "lodash-es": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", - "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.14.tgz", + "integrity": "sha512-7zchRrGa8UZXjD/4ivUWP1867jDkhzTG2c/uj739utSd7O/pFFdxspCemIFKEEjErbcqRzn8nKnGsi7mvTgRPA==" }, "lodash.assign": { "version": "4.2.0", @@ -9028,10 +9136,10 @@ "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", "dev": true, "requires": { - "circular-json": "^0.5.5", - "date-format": "^1.2.0", - "debug": "^3.1.0", - "rfdc": "^1.1.2", + "circular-json": "0.5.9", + "date-format": "1.2.0", + "debug": "3.2.6", + "rfdc": "1.1.2", "streamroller": "0.7.0" }, "dependencies": { @@ -9047,7 +9155,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "ms": { @@ -9074,8 +9182,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", @@ -9083,7 +9190,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.2" } }, "loud-rejection": { @@ -9092,8 +9199,8 @@ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" } }, "lowercase-keys": { @@ -9108,8 +9215,8 @@ "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "magic-string": { @@ -9118,7 +9225,7 @@ "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==", "dev": true, "requires": { - "sourcemap-codec": "^1.4.4" + "sourcemap-codec": "1.4.4" } }, "make-dir": { @@ -9127,7 +9234,7 @@ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "make-error": { @@ -9142,17 +9249,17 @@ "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", "dev": true, "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^11.0.1", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" + "agentkeepalive": "3.5.2", + "cacache": "11.3.2", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "4.0.1", + "ssri": "6.0.1" }, "dependencies": { "bluebird": { @@ -9167,20 +9274,20 @@ "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", "dev": true, "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "bluebird": "3.5.3", + "chownr": "1.1.1", + "figgy-pudding": "3.5.1", + "glob": "7.1.3", + "graceful-fs": "4.1.15", + "lru-cache": "5.1.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "6.0.1", + "unique-filename": "1.1.1", + "y18n": "4.0.0" }, "dependencies": { "lru-cache": { @@ -9189,7 +9296,7 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "^3.0.2" + "yallist": "3.0.3" } } } @@ -9200,12 +9307,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { @@ -9220,16 +9327,16 @@ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.3", + "through2": "2.0.3" } }, "pump": { @@ -9238,8 +9345,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "ssri": { @@ -9248,7 +9355,7 @@ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1" + "figgy-pudding": "3.5.1" } }, "yallist": { @@ -9265,7 +9372,7 @@ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "map-age-cleaner": { @@ -9274,7 +9381,7 @@ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { - "p-defer": "^1.0.0" + "p-defer": "1.0.0" } }, "map-cache": { @@ -9299,7 +9406,7 @@ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "mappy-breakpoints": { @@ -9308,9 +9415,9 @@ "integrity": "sha1-55ZqFe6louprSJXSW364It199Fs=" }, "marked": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz", - "integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" }, "matchdep": { "version": "2.0.0", @@ -9318,9 +9425,9 @@ "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", "dev": true, "requires": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", + "findup-sync": "2.0.0", + "micromatch": "3.1.10", + "resolve": "1.7.1", "stack-trace": "0.0.10" }, "dependencies": { @@ -9330,7 +9437,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.1" } }, "findup-sync": { @@ -9339,10 +9446,10 @@ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" } }, "global-modules": { @@ -9351,9 +9458,9 @@ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" } }, "global-prefix": { @@ -9362,11 +9469,11 @@ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.1" } }, "is-glob": { @@ -9375,7 +9482,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } }, "resolve-dir": { @@ -9384,8 +9491,8 @@ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" } } } @@ -9396,15 +9503,15 @@ "integrity": "sha1-Vte36xgDPwz5vFLrIJD6x9weifo=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "async-array-reduce": "^0.2.0", - "extend-shallow": "^2.0.1", - "fs-exists-sync": "^0.1.0", - "glob": "^7.0.5", - "has-glob": "^0.1.1", - "is-valid-glob": "^0.3.0", - "lazy-cache": "^2.0.1", - "resolve-dir": "^0.1.0" + "arr-union": "3.1.0", + "async-array-reduce": "0.2.1", + "extend-shallow": "2.0.1", + "fs-exists-sync": "0.1.0", + "glob": "7.1.2", + "has-glob": "0.1.1", + "is-valid-glob": "0.3.0", + "lazy-cache": "2.0.2", + "resolve-dir": "0.1.1" }, "dependencies": { "extend-shallow": { @@ -9413,7 +9520,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "lazy-cache": { @@ -9422,7 +9529,7 @@ "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", "dev": true, "requires": { - "set-getter": "^0.1.0" + "set-getter": "0.1.0" } } } @@ -9433,9 +9540,9 @@ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "hash-base": "3.0.4", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "media-typer": { @@ -9450,9 +9557,9 @@ "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", "dev": true, "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^1.1.0" + "map-age-cleaner": "0.1.3", + "mimic-fn": "1.2.0", + "p-is-promise": "1.1.0" } }, "memory-fs": { @@ -9461,8 +9568,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "errno": "0.1.7", + "readable-stream": "2.3.6" } }, "memorystream": { @@ -9477,16 +9584,16 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" }, "dependencies": { "minimist": { @@ -9526,19 +9633,19 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "miller-rabin": { @@ -9547,8 +9654,8 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "bn.js": "4.11.8", + "brorand": "1.1.0" } }, "mime": { @@ -9570,7 +9677,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.33.0" } }, "mimic-fn": { @@ -9585,9 +9692,9 @@ "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" + "loader-utils": "1.2.3", + "schema-utils": "1.0.0", + "webpack-sources": "1.3.0" } }, "minimalistic-assert": { @@ -9607,7 +9714,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -9622,8 +9729,8 @@ "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "safe-buffer": "5.1.2", + "yallist": "3.0.3" }, "dependencies": { "yallist": { @@ -9640,7 +9747,7 @@ "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "mississippi": { @@ -9649,16 +9756,16 @@ "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "2.0.1", + "pumpify": "1.5.1", + "stream-each": "1.2.3", + "through2": "2.0.3" } }, "mixin-deep": { @@ -9666,8 +9773,8 @@ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -9675,7 +9782,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -9686,8 +9793,8 @@ "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", "dev": true, "requires": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" + "for-in": "0.1.8", + "is-extendable": "0.1.1" }, "dependencies": { "for-in": { @@ -9718,18 +9825,26 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, + "moment-timezone": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.23.tgz", + "integrity": "sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==", + "requires": { + "moment": "2.24.0" + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" } }, "ms": { @@ -9743,8 +9858,8 @@ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" + "dns-packet": "1.3.1", + "thunky": "1.0.3" } }, "multicast-dns-service-types": { @@ -9775,18 +9890,18 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "negotiator": { @@ -9813,34 +9928,34 @@ "integrity": "sha512-MIPKxyrnV22fS3wSfst2XjwWOonFKujVVEnIehYJhiu8GOg37bCdbbr9plsE1jRDmDAUz6M1MvdKibUrJyRp6Q==", "dev": true, "requires": { - "@ngtools/json-schema": "^1.1.0", - "autoprefixer": "^9.0.0", - "browserslist": "^4.0.0", - "chalk": "^2.3.1", - "chokidar": "^2.0.3", - "clean-css": "^4.1.11", - "commander": "^2.12.0", - "fs-extra": "^7.0.0", - "glob": "^7.1.2", - "injection-js": "^2.2.1", - "less": "^3.8.0", - "less-plugin-npm-import": "^2.1.0", - "node-sass": "^4.9.3", - "node-sass-tilde-importer": "^1.0.0", - "opencollective-postinstall": "^2.0.1", - "postcss": "^7.0.0", - "postcss-url": "^8.0.0", - "read-pkg-up": "^4.0.0", - "rimraf": "^2.6.1", - "rollup": "^0.67.0", - "rollup-plugin-commonjs": "^9.1.3", - "rollup-plugin-json": "^3.1.0", - "rollup-plugin-node-resolve": "^4.0.0", - "rollup-plugin-sourcemaps": "^0.4.2", - "rxjs": "^6.0.0", - "stylus": "^0.54.5", - "uglify-js": "^3.0.7", - "update-notifier": "^2.3.0" + "@ngtools/json-schema": "1.1.0", + "autoprefixer": "9.4.6", + "browserslist": "4.4.1", + "chalk": "2.4.2", + "chokidar": "2.0.3", + "clean-css": "4.2.1", + "commander": "2.15.1", + "fs-extra": "7.0.1", + "glob": "7.1.2", + "injection-js": "2.2.1", + "less": "3.9.0", + "less-plugin-npm-import": "2.1.0", + "node-sass": "4.11.0", + "node-sass-tilde-importer": "1.0.2", + "opencollective-postinstall": "2.0.2", + "postcss": "7.0.14", + "postcss-url": "8.0.0", + "read-pkg-up": "4.0.0", + "rimraf": "2.6.2", + "rollup": "0.67.4", + "rollup-plugin-commonjs": "9.2.0", + "rollup-plugin-json": "3.1.0", + "rollup-plugin-node-resolve": "4.0.0", + "rollup-plugin-sourcemaps": "0.4.2", + "rxjs": "6.4.0", + "stylus": "0.54.5", + "uglify-js": "3.4.9", + "update-notifier": "2.5.0" }, "dependencies": { "chalk": { @@ -9849,9 +9964,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "find-up": { @@ -9860,7 +9975,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "fs-extra": { @@ -9869,9 +9984,9 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" } }, "jsonfile": { @@ -9880,7 +9995,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.11" } }, "load-json-file": { @@ -9889,10 +10004,10 @@ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.1.11", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.0.0" } }, "locate-path": { @@ -9901,8 +10016,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "p-limit": { @@ -9911,7 +10026,7 @@ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.0.0" } }, "p-locate": { @@ -9920,7 +10035,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.1.0" } }, "p-try": { @@ -9935,8 +10050,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "error-ex": "1.3.1", + "json-parse-better-errors": "1.0.2" } }, "read-pkg": { @@ -9945,9 +10060,9 @@ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "load-json-file": "4.0.0", + "normalize-package-data": "2.4.0", + "path-type": "3.0.0" } }, "read-pkg-up": { @@ -9956,8 +10071,8 @@ "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", "dev": true, "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" + "find-up": "3.0.0", + "read-pkg": "3.0.0" } }, "strip-bom": { @@ -9974,14 +10089,13 @@ "integrity": "sha512-IO2zBWMAyAWZgK6zbPhmR3tNRgW+jfi/Z+Xkvaa42w6eYNQ8bEwYv7uxZo/3MQJ5RglxZ+6KsDLXPzjN+ZUEZw==", "dev": true, "requires": { - "deep-freeze-strict": "^1.1.1" + "deep-freeze-strict": "1.1.1" } }, "ngrx-store-localstorage": { "version": "github:cf-stratos/ngrx-store-localstorage#e722ed60861d49bec99482ac54f13e7485bb0ac9", - "from": "github:cf-stratos/ngrx-store-localstorage#lodash-dep-update", "requires": { - "lodash": "^4.17.11" + "lodash": "4.17.11" }, "dependencies": { "lodash": { @@ -9996,7 +10110,7 @@ "resolved": "https://registry.npmjs.org/ngx-moment/-/ngx-moment-3.3.0.tgz", "integrity": "sha512-6fpllpJqLfjRWboOhphgeEYt+rzIA9O29rG5QWCebRt2X0uNk4P93sLEb0S8lbDF0dEp2NOC3UOD+xoCVlJQhA==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "nice-try": { @@ -10011,9 +10125,9 @@ "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", "dev": true, "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" + "encoding": "0.1.12", + "json-parse-better-errors": "1.0.2", + "safe-buffer": "5.1.2" } }, "node-forge": { @@ -10034,18 +10148,18 @@ "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "dev": true, "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" + "fstream": "1.0.11", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "osenv": "0.1.5", + "request": "2.88.0", + "rimraf": "2.6.2", + "semver": "5.3.0", + "tar": "2.2.1", + "which": "1.3.1" }, "dependencies": { "semver": { @@ -10062,28 +10176,28 @@ "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", "dev": true, "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", + "assert": "1.4.1", + "browserify-zlib": "0.2.0", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "3.0.0", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.6", + "stream-browserify": "2.0.2", + "stream-http": "2.8.3", + "string_decoder": "1.1.1", + "timers-browserify": "2.0.10", "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", + "url": "0.11.0", + "util": "0.11.1", "vm-browserify": "0.0.4" }, "dependencies": { @@ -10110,7 +10224,7 @@ "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==", "dev": true, "requires": { - "semver": "^5.3.0" + "semver": "5.5.0" } }, "node-sass": { @@ -10119,25 +10233,25 @@ "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", "dev": true, "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.10.0", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "async-foreach": "0.1.3", + "chalk": "1.1.3", + "cross-spawn": "3.0.1", + "gaze": "1.1.3", + "get-stdin": "4.0.1", + "glob": "7.1.2", + "in-publish": "2.0.0", + "lodash.assign": "4.2.0", + "lodash.clonedeep": "4.5.0", + "lodash.mergewith": "4.6.1", + "meow": "3.7.0", + "mkdirp": "0.5.1", + "nan": "2.10.0", + "node-gyp": "3.8.0", + "npmlog": "4.1.2", + "request": "2.88.0", + "sass-graph": "2.2.4", + "stdout-stream": "1.4.0", + "true-case-path": "1.0.2" }, "dependencies": { "ansi-styles": { @@ -10152,11 +10266,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -10173,7 +10287,7 @@ "integrity": "sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg==", "dev": true, "requires": { - "find-parent-dir": "^0.3.0" + "find-parent-dir": "0.3.0" } }, "nopt": { @@ -10182,7 +10296,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } }, "normalize-package-data": { @@ -10191,10 +10305,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" } }, "normalize-path": { @@ -10202,7 +10316,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "normalize-range": { @@ -10222,7 +10336,7 @@ "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", "dev": true, "requires": { - "once": "^1.3.2" + "once": "1.4.0" } }, "npm-bundled": { @@ -10237,10 +10351,10 @@ "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", "dev": true, "requires": { - "hosted-git-info": "^2.6.0", - "osenv": "^0.1.5", - "semver": "^5.5.0", - "validate-npm-package-name": "^3.0.0" + "hosted-git-info": "2.6.0", + "osenv": "0.1.5", + "semver": "5.5.0", + "validate-npm-package-name": "3.0.0" } }, "npm-packlist": { @@ -10249,8 +10363,8 @@ "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.6" } }, "npm-pick-manifest": { @@ -10259,9 +10373,9 @@ "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" + "figgy-pudding": "3.5.1", + "npm-package-arg": "6.1.0", + "semver": "5.5.0" } }, "npm-registry-fetch": { @@ -10270,12 +10384,12 @@ "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", "dev": true, "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^4.1.3", - "make-fetch-happen": "^4.0.1", - "npm-package-arg": "^6.1.0" + "JSONStream": "1.3.5", + "bluebird": "3.5.1", + "figgy-pudding": "3.5.1", + "lru-cache": "4.1.3", + "make-fetch-happen": "4.0.1", + "npm-package-arg": "6.1.0" } }, "npm-run-all": { @@ -10284,15 +10398,15 @@ "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" + "ansi-styles": "3.2.1", + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "memorystream": "0.3.1", + "minimatch": "3.0.4", + "pidtree": "0.3.0", + "read-pkg": "3.0.0", + "shell-quote": "1.6.1", + "string.prototype.padend": "3.0.0" }, "dependencies": { "chalk": { @@ -10301,9 +10415,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "cross-spawn": { @@ -10312,11 +10426,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.5.0", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "load-json-file": { @@ -10325,10 +10439,10 @@ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.1.11", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.0.0" } }, "parse-json": { @@ -10337,8 +10451,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "error-ex": "1.3.1", + "json-parse-better-errors": "1.0.2" } }, "read-pkg": { @@ -10347,9 +10461,9 @@ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "load-json-file": "4.0.0", + "normalize-package-data": "2.4.0", + "path-type": "3.0.0" } }, "strip-bom": { @@ -10366,7 +10480,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "npmlog": { @@ -10375,10 +10489,10 @@ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "null-check": { @@ -10422,9 +10536,9 @@ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -10432,7 +10546,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "kind-of": { @@ -10440,7 +10554,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -10456,7 +10570,7 @@ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.assign": { @@ -10465,10 +10579,10 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "define-properties": "1.1.2", + "function-bind": "1.1.1", + "has-symbols": "1.0.0", + "object-keys": "1.0.11" } }, "object.defaults": { @@ -10477,10 +10591,10 @@ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" } }, "object.map": { @@ -10489,8 +10603,8 @@ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "object.pick": { @@ -10498,7 +10612,7 @@ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "object.reduce": { @@ -10507,8 +10621,8 @@ "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "obuf": { @@ -10538,7 +10652,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -10559,7 +10673,7 @@ "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" } }, "optimist": { @@ -10568,8 +10682,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" }, "dependencies": { "wordwrap": { @@ -10586,12 +10700,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "ordered-read-streams": { @@ -10600,7 +10714,7 @@ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.6" } }, "original": { @@ -10609,7 +10723,7 @@ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", "dev": true, "requires": { - "url-parse": "^1.4.3" + "url-parse": "1.4.4" } }, "os-browserify": { @@ -10630,7 +10744,7 @@ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { - "lcid": "^1.0.0" + "lcid": "1.0.0" } }, "os-tmpdir": { @@ -10645,8 +10759,8 @@ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "p-defer": { @@ -10673,7 +10787,7 @@ "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -10682,7 +10796,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-map": { @@ -10703,10 +10817,10 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.5.0" } }, "pacote": { @@ -10715,33 +10829,33 @@ "integrity": "sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==", "dev": true, "requires": { - "bluebird": "^3.5.3", - "cacache": "^11.3.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^4.0.1", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^2.2.3", - "npm-registry-fetch": "^3.8.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.8", - "unique-filename": "^1.1.1", - "which": "^1.3.1" + "bluebird": "3.5.3", + "cacache": "11.3.2", + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "glob": "7.1.3", + "lru-cache": "5.1.1", + "make-fetch-happen": "4.0.1", + "minimatch": "3.0.4", + "minipass": "2.3.5", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.1.0", + "npm-packlist": "1.4.1", + "npm-pick-manifest": "2.2.3", + "npm-registry-fetch": "3.9.0", + "osenv": "0.1.5", + "promise-inflight": "1.0.1", + "promise-retry": "1.1.1", + "protoduck": "5.0.1", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "semver": "5.6.0", + "ssri": "6.0.1", + "tar": "4.4.8", + "unique-filename": "1.1.1", + "which": "1.3.1" }, "dependencies": { "bluebird": { @@ -10756,20 +10870,20 @@ "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", "dev": true, "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "bluebird": "3.5.3", + "chownr": "1.1.1", + "figgy-pudding": "3.5.1", + "glob": "7.1.3", + "graceful-fs": "4.1.15", + "lru-cache": "5.1.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "6.0.1", + "unique-filename": "1.1.1", + "y18n": "4.0.0" } }, "get-stream": { @@ -10778,7 +10892,7 @@ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "pump": "^3.0.0" + "pump": "3.0.0" } }, "glob": { @@ -10787,12 +10901,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { @@ -10807,7 +10921,7 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "^3.0.2" + "yallist": "3.0.3" } }, "mississippi": { @@ -10816,16 +10930,16 @@ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.3", + "through2": "2.0.3" } }, "pump": { @@ -10834,8 +10948,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "semver": { @@ -10850,7 +10964,7 @@ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1" + "figgy-pudding": "3.5.1" } }, "tar": { @@ -10859,13 +10973,13 @@ "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "chownr": "1.1.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.5", + "minizlib": "1.2.1", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "yallist": { @@ -10888,9 +11002,9 @@ "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "dev": true, "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "parse-asn1": { @@ -10899,12 +11013,12 @@ "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", "dev": true, "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "asn1.js": "4.10.1", + "browserify-aes": "1.2.0", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.17", + "safe-buffer": "5.1.2" } }, "parse-filepath": { @@ -10913,9 +11027,9 @@ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" } }, "parse-json": { @@ -10924,7 +11038,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "parse-node-version": { @@ -10951,7 +11065,7 @@ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseuri": { @@ -10960,7 +11074,7 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseurl": { @@ -10980,8 +11094,8 @@ "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", "dev": true, "requires": { - "process": "^0.11.1", - "util": "^0.10.3" + "process": "0.11.10", + "util": "0.10.3" } }, "path-browserify": { @@ -11030,7 +11144,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "^0.1.0" + "path-root-regex": "0.1.2" } }, "path-root-regex": { @@ -11051,7 +11165,7 @@ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "pause-stream": { @@ -11060,7 +11174,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "pbkdf2": { @@ -11069,11 +11183,11 @@ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" } }, "performance-now": { @@ -11106,7 +11220,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -11115,7 +11229,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "^2.1.0" + "find-up": "2.1.0" } }, "plugin-error": { @@ -11124,11 +11238,11 @@ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", "dev": true, "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" }, "dependencies": { "arr-diff": { @@ -11137,8 +11251,8 @@ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" } }, "arr-union": { @@ -11159,7 +11273,7 @@ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", "dev": true, "requires": { - "kind-of": "^1.1.0" + "kind-of": "1.1.0" } }, "kind-of": { @@ -11182,9 +11296,9 @@ "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", "dev": true, "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" + "async": "1.5.2", + "debug": "2.6.9", + "mkdirp": "0.5.1" } }, "posix-character-classes": { @@ -11198,9 +11312,9 @@ "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", "dev": true, "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "chalk": "2.4.2", + "source-map": "0.6.1", + "supports-color": "6.1.0" }, "dependencies": { "chalk": { @@ -11209,9 +11323,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" }, "dependencies": { "supports-color": { @@ -11220,7 +11334,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -11237,7 +11351,7 @@ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -11248,10 +11362,10 @@ "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", "dev": true, "requires": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "postcss": "7.0.14", + "postcss-value-parser": "3.3.1", + "read-cache": "1.0.0", + "resolve": "1.7.1" } }, "postcss-load-config": { @@ -11260,8 +11374,8 @@ "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", "dev": true, "requires": { - "cosmiconfig": "^4.0.0", - "import-cwd": "^2.0.0" + "cosmiconfig": "4.0.0", + "import-cwd": "2.1.0" } }, "postcss-loader": { @@ -11270,10 +11384,10 @@ "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" + "loader-utils": "1.2.3", + "postcss": "7.0.14", + "postcss-load-config": "2.0.0", + "schema-utils": "1.0.0" } }, "postcss-url": { @@ -11282,11 +11396,11 @@ "integrity": "sha512-E2cbOQ5aii2zNHh8F6fk1cxls7QVFZjLPSrqvmiza8OuXLzIpErij8BDS5Y3STPfJgpIMNCPEr8JlKQWEoozUw==", "dev": true, "requires": { - "mime": "^2.3.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.0", - "postcss": "^7.0.2", - "xxhashjs": "^0.2.1" + "mime": "2.4.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "postcss": "7.0.14", + "xxhashjs": "0.2.2" }, "dependencies": { "mime": { @@ -11345,7 +11459,7 @@ "dev": true, "optional": true, "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "promise-inflight": { @@ -11360,8 +11474,8 @@ "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", "dev": true, "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" + "err-code": "1.1.2", + "retry": "0.10.1" } }, "protoduck": { @@ -11370,7 +11484,7 @@ "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", "dev": true, "requires": { - "genfun": "^5.0.0" + "genfun": "5.0.0" } }, "protractor": { @@ -11379,22 +11493,22 @@ "integrity": "sha512-6TSYqMhUUzxr4/wN0ttSISqPMKvcVRXF4k8jOEpGWD8OioLak4KLgfzHK9FJ49IrjzRrZ+Mx1q2Op8Rk0zEcnQ==", "dev": true, "requires": { - "@types/node": "^6.0.46", - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", + "@types/node": "6.14.3", + "@types/q": "0.0.32", + "@types/selenium-webdriver": "3.0.10", + "blocking-proxy": "1.0.1", + "browserstack": "1.5.1", + "chalk": "1.1.3", + "glob": "7.1.2", "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "optimist": "~0.6.0", + "jasminewd2": "2.2.0", + "optimist": "0.6.1", "q": "1.4.1", - "saucelabs": "^1.5.0", + "saucelabs": "1.5.0", "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", + "source-map-support": "0.4.18", "webdriver-js-extender": "2.0.0", - "webdriver-manager": "^12.0.6" + "webdriver-manager": "12.1.0" }, "dependencies": { "@types/node": { @@ -11415,11 +11529,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "del": { @@ -11428,13 +11542,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" } }, "globby": { @@ -11443,12 +11557,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "minimist": { @@ -11475,7 +11589,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.6" } }, "supports-color": { @@ -11490,17 +11604,17 @@ "integrity": "sha512-oEc5fmkpz6Yh6udhwir5m0eN5mgRPq9P/NU5YWuT3Up5slt6Zz+znhLU7q4+8rwCZz/Qq3Fgpr/4oao7NPCm2A==", "dev": true, "requires": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" + "adm-zip": "0.4.11", + "chalk": "1.1.3", + "del": "2.2.2", + "glob": "7.1.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "q": "1.4.1", + "request": "2.88.0", + "rimraf": "2.6.2", + "semver": "5.5.0", + "xml2js": "0.4.19" } } } @@ -11511,7 +11625,7 @@ "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", "dev": true, "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.1.2", "ipaddr.js": "1.8.0" } }, @@ -11527,7 +11641,7 @@ "integrity": "sha1-mvZ6mdex0BMuUaUDCZ04qNKs4sM=", "dev": true, "requires": { - "table-parser": "^0.1.3" + "table-parser": "0.1.3" } }, "ps-tree": { @@ -11536,7 +11650,7 @@ "integrity": "sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A==", "dev": true, "requires": { - "event-stream": "=3.3.4" + "event-stream": "3.3.4" } }, "pseudomap": { @@ -11557,12 +11671,12 @@ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "parse-asn1": "5.1.4", + "randombytes": "2.1.0", + "safe-buffer": "5.1.2" } }, "pump": { @@ -11571,8 +11685,8 @@ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "pumpify": { @@ -11581,9 +11695,9 @@ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "duplexify": "3.6.0", + "inherits": "2.0.3", + "pump": "2.0.1" } }, "punycode": { @@ -11633,7 +11747,7 @@ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { - "safe-buffer": "^5.1.0" + "safe-buffer": "5.1.2" } }, "randomfill": { @@ -11642,8 +11756,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "randombytes": "2.1.0", + "safe-buffer": "5.1.2" } }, "range-parser": { @@ -11670,7 +11784,7 @@ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } @@ -11681,8 +11795,8 @@ "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" + "loader-utils": "1.2.3", + "schema-utils": "1.0.0" } }, "rc": { @@ -11691,10 +11805,10 @@ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -11717,7 +11831,7 @@ "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", "dev": true, "requires": { - "pify": "^2.3.0" + "pify": "2.3.0" }, "dependencies": { "pify": { @@ -11734,9 +11848,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" }, "dependencies": { "path-type": { @@ -11745,9 +11859,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -11764,8 +11878,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" }, "dependencies": { "find-up": { @@ -11774,8 +11888,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "path-exists": { @@ -11784,7 +11898,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } } } @@ -11794,13 +11908,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "readdirp": { @@ -11808,10 +11922,10 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.6", + "set-immediate-shim": "1.0.1" } }, "readline2": { @@ -11820,8 +11934,8 @@ "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", "mute-stream": "0.0.5" } }, @@ -11831,7 +11945,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.7.1" } }, "redent": { @@ -11840,8 +11954,8 @@ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "reflect-metadata": { @@ -11867,8 +11981,8 @@ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "regexpu-core": { @@ -11877,9 +11991,9 @@ "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.4.0", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "registry-auth-token": { @@ -11888,8 +12002,8 @@ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.8", + "safe-buffer": "5.1.2" } }, "registry-url": { @@ -11898,7 +12012,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "1.2.8" } }, "regjsgen": { @@ -11913,7 +12027,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -11930,8 +12044,8 @@ "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, "requires": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" + "is-buffer": "1.1.6", + "is-utf8": "0.2.1" } }, "remove-bom-stream": { @@ -11940,9 +12054,9 @@ "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", "dev": true, "requires": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" + "remove-bom-buffer": "3.0.0", + "safe-buffer": "5.1.2", + "through2": "2.0.3" } }, "remove-trailing-separator": { @@ -11966,7 +12080,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "replace-ext": { @@ -11981,9 +12095,9 @@ "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" + "homedir-polyfill": "1.0.1", + "is-absolute": "1.0.0", + "remove-trailing-separator": "1.1.0" } }, "replace-in-file": { @@ -11992,9 +12106,9 @@ "integrity": "sha512-fto9Ooab00CniGkSjRCZCamER7P5S4mZHQ4w4dLd09nwP3FtFfjUJh8/OVC/In4ki5MEy+dYO5v9r7rtq2DrYQ==", "dev": true, "requires": { - "chalk": "^2.3.2", - "glob": "^7.1.2", - "yargs": "^11.0.0" + "chalk": "2.4.1", + "glob": "7.1.2", + "yargs": "11.1.0" }, "dependencies": { "ansi-regex": { @@ -12015,9 +12129,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "cliui": { @@ -12026,9 +12140,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" } }, "is-fullwidth-code-point": { @@ -12043,9 +12157,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" }, "dependencies": { "mem": { @@ -12054,7 +12168,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } } } @@ -12065,8 +12179,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -12075,7 +12189,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "which-module": { @@ -12096,18 +12210,18 @@ "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" } }, "yargs-parser": { @@ -12116,7 +12230,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -12127,26 +12241,26 @@ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.1.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.22", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" }, "dependencies": { "extend": { @@ -12167,7 +12281,7 @@ "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", "dev": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.38.0" } }, "uuid": { @@ -12184,7 +12298,7 @@ "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", "dev": true, "requires": { - "lodash": "^4.17.11" + "lodash": "4.17.11" }, "dependencies": { "lodash": { @@ -12202,8 +12316,8 @@ "dev": true, "requires": { "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" + "stealthy-require": "1.1.1", + "tough-cookie": "2.4.3" } }, "require-directory": { @@ -12230,8 +12344,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" }, "dependencies": { "resolve-from": { @@ -12259,7 +12373,7 @@ "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.5" } }, "resolve-cwd": { @@ -12268,7 +12382,7 @@ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "3.0.0" } }, "resolve-dir": { @@ -12277,8 +12391,8 @@ "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", "dev": true, "requires": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" + "expand-tilde": "1.2.2", + "global-modules": "0.2.3" } }, "resolve-from": { @@ -12293,7 +12407,7 @@ "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", "dev": true, "requires": { - "value-or-function": "^3.0.0" + "value-or-function": "3.0.0" } }, "resolve-url": { @@ -12307,8 +12421,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" + "exit-hook": "1.1.1", + "onetime": "1.1.0" } }, "ret": { @@ -12335,7 +12449,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -12344,7 +12458,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "ripemd160": { @@ -12353,8 +12467,8 @@ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "3.0.4", + "inherits": "2.0.3" } }, "rollup": { @@ -12364,7 +12478,7 @@ "dev": true, "requires": { "@types/estree": "0.0.39", - "@types/node": "*" + "@types/node": "11.9.4" } }, "rollup-plugin-commonjs": { @@ -12373,10 +12487,10 @@ "integrity": "sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA==", "dev": true, "requires": { - "estree-walker": "^0.5.2", - "magic-string": "^0.25.1", - "resolve": "^1.8.1", - "rollup-pluginutils": "^2.3.3" + "estree-walker": "0.5.2", + "magic-string": "0.25.2", + "resolve": "1.10.0", + "rollup-pluginutils": "2.4.1" }, "dependencies": { "path-parse": { @@ -12391,7 +12505,7 @@ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "path-parse": "1.0.6" } } } @@ -12402,7 +12516,7 @@ "integrity": "sha512-BlYk5VspvGpjz7lAwArVzBXR60JK+4EKtPkCHouAWg39obk9S61hZYJDBfMK+oitPdoe11i69TlxKlMQNFC/Uw==", "dev": true, "requires": { - "rollup-pluginutils": "^2.3.1" + "rollup-pluginutils": "2.4.1" } }, "rollup-plugin-node-resolve": { @@ -12411,9 +12525,9 @@ "integrity": "sha512-7Ni+/M5RPSUBfUaP9alwYQiIKnKeXCOHiqBpKUl9kwp3jX5ZJtgXAait1cne6pGEVUUztPD6skIKH9Kq9sNtfw==", "dev": true, "requires": { - "builtin-modules": "^3.0.0", - "is-module": "^1.0.0", - "resolve": "^1.8.1" + "builtin-modules": "3.0.0", + "is-module": "1.0.0", + "resolve": "1.10.0" }, "dependencies": { "builtin-modules": { @@ -12434,7 +12548,7 @@ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "path-parse": "1.0.6" } } } @@ -12445,8 +12559,8 @@ "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=", "dev": true, "requires": { - "rollup-pluginutils": "^2.0.1", - "source-map-resolve": "^0.5.0" + "rollup-pluginutils": "2.4.1", + "source-map-resolve": "0.5.2" } }, "rollup-pluginutils": { @@ -12455,8 +12569,8 @@ "integrity": "sha512-wesMQ9/172IJDIW/lYWm0vW0LiKe5Ekjws481R7z9WTRtmO59cqyM/2uUlxvf6yzm/fElFmHUobeQOYz46dZJw==", "dev": true, "requires": { - "estree-walker": "^0.6.0", - "micromatch": "^3.1.10" + "estree-walker": "0.6.0", + "micromatch": "3.1.10" }, "dependencies": { "estree-walker": { @@ -12473,7 +12587,7 @@ "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "dev": true, "requires": { - "once": "^1.3.0" + "once": "1.4.0" } }, "run-queue": { @@ -12482,7 +12596,7 @@ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { - "aproba": "^1.1.1" + "aproba": "1.2.0" } }, "rw": { @@ -12501,7 +12615,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "rxjs-spy": { @@ -12509,11 +12623,11 @@ "resolved": "https://registry.npmjs.org/rxjs-spy/-/rxjs-spy-7.0.2.tgz", "integrity": "sha512-rSzEEQLH07FPBqmxIC8oeI2Z7tWibvxKxybcKtcc6Cj8Tz178ySDgzgOkoct8dAz5IE/H2jrR2/l+ZYYfpuEXw==", "requires": { - "@types/circular-json": "^0.4.0", - "@types/stacktrace-js": "^0.0.32", - "circular-json": "^0.5.0", - "error-stack-parser": "^2.0.1", - "stacktrace-gps": "^3.0.2" + "@types/circular-json": "0.4.0", + "@types/stacktrace-js": "0.0.32", + "circular-json": "0.5.4", + "error-stack-parser": "2.0.1", + "stacktrace-gps": "3.0.2" } }, "rxjs-tslint": { @@ -12522,10 +12636,10 @@ "integrity": "sha512-odvEAx6VoZSJs5o9gWZ7SxgTuS8ldq0mr4qnprbr6flNtIt4DtNJSYJpBK0WEy0o+f1oe92XVHrmgco1e7Cpdw==", "dev": true, "requires": { - "chalk": "^2.4.0", - "optimist": "^0.6.1", - "tslint": "^5.9.1", - "tsutils": "^2.25.0" + "chalk": "2.4.1", + "optimist": "0.6.1", + "tslint": "5.13.0", + "tsutils": "2.27.1" }, "dependencies": { "chalk": { @@ -12534,9 +12648,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } } } @@ -12556,7 +12670,7 @@ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -12576,10 +12690,10 @@ "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", "dev": true, "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^7.0.0" + "glob": "7.1.2", + "lodash": "4.17.10", + "scss-tokenizer": "0.2.3", + "yargs": "7.1.0" }, "dependencies": { "camelcase": { @@ -12594,11 +12708,17 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" } }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", @@ -12611,19 +12731,19 @@ "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", "dev": true, "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" } } } @@ -12634,20 +12754,20 @@ "integrity": "sha1-Yw9pwhaqIGuCMvsqqQe98zNrbYM=", "dev": true, "requires": { - "commander": "^2.8.1", - "eslint": "^2.7.0", + "commander": "2.15.1", + "eslint": "2.13.1", "front-matter": "2.1.2", - "fs-extra": "^3.0.1", - "glob": "^7.0.0", - "globule": "^1.0.0", - "gonzales-pe-sl": "^4.2.3", - "js-yaml": "^3.5.4", - "known-css-properties": "^0.3.0", - "lodash.capitalize": "^4.1.0", - "lodash.kebabcase": "^4.0.0", - "merge": "^1.2.0", - "path-is-absolute": "^1.0.0", - "util": "^0.10.3" + "fs-extra": "3.0.1", + "glob": "7.1.2", + "globule": "1.2.1", + "gonzales-pe-sl": "4.2.3", + "js-yaml": "3.13.1", + "known-css-properties": "0.3.0", + "lodash.capitalize": "4.2.1", + "lodash.kebabcase": "4.1.1", + "merge": "1.2.1", + "path-is-absolute": "1.0.1", + "util": "0.10.3" } }, "sass-loader": { @@ -12656,12 +12776,12 @@ "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", "dev": true, "requires": { - "clone-deep": "^2.0.1", - "loader-utils": "^1.0.1", - "lodash.tail": "^4.1.1", - "neo-async": "^2.5.0", - "pify": "^3.0.0", - "semver": "^5.5.0" + "clone-deep": "2.0.2", + "loader-utils": "1.2.3", + "lodash.tail": "4.1.1", + "neo-async": "2.6.0", + "pify": "3.0.0", + "semver": "5.5.0" } }, "saucelabs": { @@ -12670,7 +12790,7 @@ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", "dev": true, "requires": { - "https-proxy-agent": "^2.2.1" + "https-proxy-agent": "2.2.1" } }, "sax": { @@ -12685,9 +12805,9 @@ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "ajv": "6.9.1", + "ajv-errors": "1.0.1", + "ajv-keywords": "3.4.0" } }, "scss-tokenizer": { @@ -12696,8 +12816,8 @@ "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" + "js-base64": "2.4.8", + "source-map": "0.4.4" }, "dependencies": { "source-map": { @@ -12706,7 +12826,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -12723,10 +12843,10 @@ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", "dev": true, "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", + "jszip": "3.1.5", + "rimraf": "2.6.2", "tmp": "0.0.30", - "xml2js": "^0.4.17" + "xml2js": "0.4.19" }, "dependencies": { "tmp": { @@ -12735,7 +12855,7 @@ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", "dev": true, "requires": { - "os-tmpdir": "~1.0.1" + "os-tmpdir": "1.0.2" } } } @@ -12761,7 +12881,7 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "5.5.0" } }, "semver-dsl": { @@ -12770,7 +12890,7 @@ "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", "dev": true, "requires": { - "semver": "^5.3.0" + "semver": "5.5.0" } }, "semver-greatest-satisfied-range": { @@ -12779,7 +12899,7 @@ "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", "dev": true, "requires": { - "sver-compat": "^1.5.0" + "sver-compat": "1.5.0" } }, "semver-intersect": { @@ -12788,7 +12908,7 @@ "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", "dev": true, "requires": { - "semver": "^5.0.0" + "semver": "5.5.0" } }, "send": { @@ -12798,18 +12918,18 @@ "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" }, "dependencies": { "mime": { @@ -12832,13 +12952,13 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "escape-html": "1.0.3", + "http-errors": "1.6.3", + "mime-types": "2.1.18", + "parseurl": "1.3.2" } }, "serve-static": { @@ -12847,9 +12967,9 @@ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.2" } }, @@ -12865,7 +12985,7 @@ "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", "dev": true, "requires": { - "to-object-path": "^0.3.0" + "to-object-path": "0.3.0" } }, "set-immediate-shim": { @@ -12878,10 +12998,10 @@ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -12889,7 +13009,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -12912,8 +13032,8 @@ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "shallow-clone": { @@ -12922,9 +13042,9 @@ "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", "dev": true, "requires": { - "is-extendable": "^0.1.1", - "kind-of": "^5.0.0", - "mixin-object": "^2.0.1" + "is-extendable": "0.1.1", + "kind-of": "5.1.0", + "mixin-object": "2.0.1" }, "dependencies": { "kind-of": { @@ -12941,7 +13061,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -12956,10 +13076,10 @@ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", "dev": true, "requires": { - "array-filter": "~0.0.0", - "array-map": "~0.0.0", - "array-reduce": "~0.0.0", - "jsonify": "~0.0.0" + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" } }, "shelljs": { @@ -12983,7 +13103,7 @@ "formatio": "1.1.1", "lolex": "1.3.2", "samsam": "1.1.2", - "util": ">=0.10.3 <1" + "util": "0.10.3" } }, "slash": { @@ -13009,14 +13129,14 @@ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.6", + "source-map-resolve": "0.5.2", + "use": "3.1.0" }, "dependencies": { "define-property": { @@ -13024,7 +13144,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -13032,7 +13152,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -13042,9 +13162,9 @@ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -13052,7 +13172,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -13060,7 +13180,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -13068,7 +13188,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -13076,9 +13196,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -13088,7 +13208,7 @@ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -13096,7 +13216,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -13107,12 +13227,12 @@ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", + "debug": "3.1.0", + "engine.io": "3.2.1", + "has-binary2": "1.0.3", + "socket.io-adapter": "1.1.1", "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" + "socket.io-parser": "3.2.0" }, "dependencies": { "debug": { @@ -13142,15 +13262,15 @@ "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", + "debug": "3.1.0", + "engine.io-client": "3.2.1", + "has-binary2": "1.0.3", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", + "socket.io-parser": "3.2.0", "to-array": "0.1.4" }, "dependencies": { @@ -13172,7 +13292,7 @@ "dev": true, "requires": { "component-emitter": "1.2.1", - "debug": "~3.1.0", + "debug": "3.1.0", "isarray": "2.0.1" }, "dependencies": { @@ -13199,8 +13319,8 @@ "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", "dev": true, "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" + "faye-websocket": "0.10.0", + "uuid": "3.2.1" } }, "sockjs-client": { @@ -13209,12 +13329,12 @@ "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", "dev": true, "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" + "debug": "3.2.6", + "eventsource": "1.0.7", + "faye-websocket": "0.11.1", + "inherits": "2.0.3", + "json3": "3.3.2", + "url-parse": "1.4.4" }, "dependencies": { "debug": { @@ -13223,7 +13343,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "faye-websocket": { @@ -13232,7 +13352,7 @@ "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.0" } }, "ms": { @@ -13249,7 +13369,7 @@ "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", "dev": true, "requires": { - "ip": "^1.1.5", + "ip": "1.1.5", "smart-buffer": "4.0.2" } }, @@ -13259,8 +13379,8 @@ "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", "dev": true, "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" + "agent-base": "4.2.0", + "socks": "2.2.3" } }, "source-list-map": { @@ -13280,8 +13400,8 @@ "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", "dev": true, "requires": { - "async": "^2.5.0", - "loader-utils": "^1.1.0" + "async": "2.6.2", + "loader-utils": "1.2.3" }, "dependencies": { "async": { @@ -13290,7 +13410,7 @@ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", "dev": true, "requires": { - "lodash": "^4.17.11" + "lodash": "4.17.11" } }, "lodash": { @@ -13306,11 +13426,11 @@ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.1", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-support": { @@ -13319,8 +13439,8 @@ "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "buffer-from": "1.1.0", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -13354,8 +13474,8 @@ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -13370,8 +13490,8 @@ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -13386,11 +13506,11 @@ "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", "dev": true, "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "debug": "4.1.1", + "handle-thing": "2.0.0", + "http-deceiver": "1.2.7", + "select-hose": "2.0.0", + "spdy-transport": "3.0.0" }, "dependencies": { "debug": { @@ -13399,7 +13519,7 @@ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "ms": { @@ -13416,12 +13536,12 @@ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "debug": "4.1.1", + "detect-node": "2.0.4", + "hpack.js": "2.1.6", + "obuf": "1.1.2", + "readable-stream": "3.1.1", + "wbuf": "1.7.3" }, "dependencies": { "debug": { @@ -13430,7 +13550,7 @@ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "ms": { @@ -13445,9 +13565,9 @@ "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "2.0.3", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } } } @@ -13458,7 +13578,7 @@ "integrity": "sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "2.2.2" } }, "split": { @@ -13467,7 +13587,7 @@ "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { - "through": "2" + "through": "2.3.8" } }, "split-string": { @@ -13475,7 +13595,7 @@ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "sprintf-js": { @@ -13490,14 +13610,14 @@ "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "dev": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" } }, "ssri": { @@ -13506,7 +13626,7 @@ "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", "dev": true, "requires": { - "safe-buffer": "^5.1.1" + "safe-buffer": "5.1.2" } }, "stack-trace": { @@ -13526,7 +13646,7 @@ "integrity": "sha512-9o+nWhiz5wFnrB3hBHs2PTyYrS60M1vvpSzHxwxnIbtY2q9Nt51hZvhrG1+2AxD374ecwyS+IUwfkHRE/2zuGg==", "requires": { "source-map": "0.5.6", - "stackframe": "^1.0.4" + "stackframe": "1.0.4" } }, "static-extend": { @@ -13534,8 +13654,8 @@ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -13543,7 +13663,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -13554,7 +13674,7 @@ "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==", "dev": true, "requires": { - "lodash": "^4.17.4" + "lodash": "4.17.13" } }, "statuses": { @@ -13569,7 +13689,7 @@ "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.6" } }, "stealthy-require": { @@ -13583,7 +13703,7 @@ "resolved": "https://registry.npmjs.org/stratos-angular6-json-schema-form/-/stratos-angular6-json-schema-form-7.0.6.tgz", "integrity": "sha512-S/yasSZ+1YNPsiTARCqaPOW8+5ob+9jfOO0XxJ/myDlSTtgoNb4nGnl3LWlEFwMUWiOjkOUVyyVlBsiK5vIBTg==", "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "stratos-merge-dirs": { @@ -13592,10 +13712,10 @@ "integrity": "sha512-4StuTlfzL6LXdx+/r0zIm+ayxyPKP0+8338OBn448WursU5njivXu29Av2WXcqmjzcHH48hmmmfHFeBTPsuJCQ==", "dev": true, "requires": { - "inquirer": "^0.11.0", - "minimist": "^1.2.0", - "node-fs": "~0.1.7", - "path": "^0.12.7" + "inquirer": "0.11.4", + "minimist": "1.2.0", + "node-fs": "0.1.7", + "path": "0.12.7" }, "dependencies": { "ansi-styles": { @@ -13610,11 +13730,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "cli-width": { @@ -13629,19 +13749,19 @@ "integrity": "sha1-geM3ToNhvq/y2XAWIG01nQsy+k0=", "dev": true, "requires": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^1.0.1", - "figures": "^1.3.5", - "lodash": "^3.3.1", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "1.1.1", + "figures": "1.7.0", + "lodash": "3.10.1", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" } }, "lodash": { @@ -13670,11 +13790,11 @@ "integrity": "sha512-e87eX4LB+KLVOfEF/TeWpNYM9aQFJTO+jsslym7zCZLYd8xlNWZUjmUf00J0DWn7YpoNGdjv0wzkKPcXJ2c5uw==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "fs-extra": "^3.0.1", - "klaw-sync": "^2.1.0", - "mkdirp": "~0.3.5", - "underscore": "~1.6.0" + "circular-json": "0.3.3", + "fs-extra": "3.0.1", + "klaw-sync": "2.1.0", + "mkdirp": "0.3.5", + "underscore": "1.6.0" }, "dependencies": { "circular-json": { @@ -13697,8 +13817,8 @@ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "stream-combiner": { @@ -13707,7 +13827,7 @@ "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.1" } }, "stream-each": { @@ -13716,8 +13836,8 @@ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" } }, "stream-exhaust": { @@ -13732,11 +13852,11 @@ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" } }, "stream-shift": { @@ -13751,10 +13871,10 @@ "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", "dev": true, "requires": { - "date-format": "^1.2.0", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "readable-stream": "^2.3.0" + "date-format": "1.2.0", + "debug": "3.2.6", + "mkdirp": "0.5.1", + "readable-stream": "2.3.6" }, "dependencies": { "debug": { @@ -13763,7 +13883,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "ms": { @@ -13780,9 +13900,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string.prototype.padend": { @@ -13791,9 +13911,9 @@ "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.4.3", - "function-bind": "^1.0.2" + "define-properties": "1.1.2", + "es-abstract": "1.12.0", + "function-bind": "1.1.1" } }, "string_decoder": { @@ -13801,7 +13921,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "strip-ansi": { @@ -13810,7 +13930,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -13819,7 +13939,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "strip-eof": { @@ -13834,7 +13954,7 @@ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "get-stdin": "^4.0.1" + "get-stdin": "4.0.1" } }, "strip-json-comments": { @@ -13849,8 +13969,8 @@ "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" + "loader-utils": "1.2.3", + "schema-utils": "1.0.0" } }, "stylus": { @@ -13859,12 +13979,12 @@ "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", "dev": true, "requires": { - "css-parse": "1.7.x", - "debug": "*", - "glob": "7.0.x", - "mkdirp": "0.5.x", - "sax": "0.5.x", - "source-map": "0.1.x" + "css-parse": "1.7.0", + "debug": "2.6.9", + "glob": "7.0.6", + "mkdirp": "0.5.1", + "sax": "0.5.8", + "source-map": "0.1.43" }, "dependencies": { "glob": { @@ -13873,12 +13993,12 @@ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "source-map": { @@ -13887,7 +14007,7 @@ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -13898,9 +14018,9 @@ "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", "dev": true, "requires": { - "loader-utils": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "when": "~3.6.x" + "loader-utils": "1.2.3", + "lodash.clonedeep": "4.5.0", + "when": "3.6.4" } }, "supports-color": { @@ -13909,7 +14029,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "sver-compat": { @@ -13918,8 +14038,8 @@ "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", "dev": true, "requires": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "symbol-observable": { @@ -13934,12 +14054,12 @@ "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.10", "slice-ansi": "0.0.4", - "string-width": "^2.0.0" + "string-width": "2.1.1" }, "dependencies": { "ajv": { @@ -13948,8 +14068,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } }, "ajv-keywords": { @@ -13976,11 +14096,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "is-fullwidth-code-point": { @@ -13989,14 +14109,20 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "strip-ansi": { @@ -14005,7 +14131,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -14024,7 +14150,7 @@ "integrity": "sha1-BEHPzhallIFoTCfRtaZ/8VpDx7A=", "dev": true, "requires": { - "connected-domain": "^1.0.0" + "connected-domain": "1.0.0" } }, "tapable": { @@ -14039,9 +14165,9 @@ "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" } }, "temp": { @@ -14056,7 +14182,7 @@ "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", "dev": true, "requires": { - "rimraf": "~2.5.2" + "rimraf": "2.5.4" }, "dependencies": { "rimraf": { @@ -14065,7 +14191,7 @@ "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } } } @@ -14076,7 +14202,7 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "terser": { @@ -14085,9 +14211,9 @@ "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", "dev": true, "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1", - "source-map-support": "~0.5.9" + "commander": "2.17.1", + "source-map": "0.6.1", + "source-map-support": "0.5.10" }, "dependencies": { "commander": { @@ -14110,14 +14236,14 @@ "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==", "dev": true, "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" + "cacache": "11.3.2", + "find-cache-dir": "2.0.0", + "schema-utils": "1.0.0", + "serialize-javascript": "1.6.1", + "source-map": "0.6.1", + "terser": "3.16.1", + "webpack-sources": "1.3.0", + "worker-farm": "1.6.0" }, "dependencies": { "bluebird": { @@ -14132,20 +14258,20 @@ "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", "dev": true, "requires": { - "bluebird": "^3.5.3", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "bluebird": "3.5.3", + "chownr": "1.1.1", + "figgy-pudding": "3.5.1", + "glob": "7.1.3", + "graceful-fs": "4.1.15", + "lru-cache": "5.1.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "6.0.1", + "unique-filename": "1.1.1", + "y18n": "4.0.0" } }, "find-cache-dir": { @@ -14154,9 +14280,9 @@ "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", "dev": true, "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^3.0.0" + "commondir": "1.0.1", + "make-dir": "1.3.0", + "pkg-dir": "3.0.0" } }, "find-up": { @@ -14165,7 +14291,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "glob": { @@ -14174,12 +14300,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { @@ -14194,8 +14320,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "lru-cache": { @@ -14204,7 +14330,7 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "^3.0.2" + "yallist": "3.0.3" } }, "mississippi": { @@ -14213,16 +14339,16 @@ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.3", + "through2": "2.0.3" } }, "p-limit": { @@ -14231,7 +14357,7 @@ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.0.0" } }, "p-locate": { @@ -14240,7 +14366,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.1.0" } }, "p-try": { @@ -14255,7 +14381,7 @@ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "3.0.0" } }, "pump": { @@ -14264,8 +14390,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "source-map": { @@ -14280,7 +14406,7 @@ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1" + "figgy-pudding": "3.5.1" } }, "yallist": { @@ -14309,8 +14435,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.6", + "xtend": "4.0.1" } }, "through2-filter": { @@ -14319,8 +14445,8 @@ "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" + "through2": "2.0.3", + "xtend": "4.0.1" } }, "thunky": { @@ -14347,7 +14473,7 @@ "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", "dev": true, "requires": { - "setimmediate": "^1.0.4" + "setimmediate": "1.0.5" } }, "tmp": { @@ -14356,7 +14482,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "to-absolute-glob": { @@ -14365,8 +14491,8 @@ "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "is-absolute": "1.0.0", + "is-negated-glob": "1.0.0" } }, "to-array": { @@ -14392,7 +14518,7 @@ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -14400,7 +14526,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -14410,10 +14536,10 @@ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -14421,8 +14547,8 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } }, "to-through": { @@ -14431,7 +14557,7 @@ "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", "dev": true, "requires": { - "through2": "^2.0.3" + "through2": "2.0.3" } }, "tough-cookie": { @@ -14440,8 +14566,8 @@ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.1.31", + "punycode": "1.4.1" }, "dependencies": { "punycode": { @@ -14476,7 +14602,7 @@ "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", "dev": true, "requires": { - "glob": "^6.0.4" + "glob": "6.0.4" }, "dependencies": { "glob": { @@ -14485,11 +14611,11 @@ "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } } } @@ -14505,16 +14631,16 @@ "integrity": "sha1-u9KOOK9Kqj6WB2xGbhsiAZfBo84=", "dev": true, "requires": { - "arrify": "^1.0.0", - "chalk": "^2.0.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.0", - "tsconfig": "^6.0.0", - "v8flags": "^3.0.0", - "yn": "^2.0.0" + "arrify": "1.0.1", + "chalk": "2.2.2", + "diff": "3.5.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18", + "tsconfig": "6.0.0", + "v8flags": "3.1.0", + "yn": "2.0.0" }, "dependencies": { "minimist": { @@ -14529,7 +14655,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.6" } } } @@ -14540,8 +14666,8 @@ "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", "dev": true, "requires": { - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "strip-bom": { @@ -14564,9 +14690,9 @@ "integrity": "sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==", "dev": true, "requires": { - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map": "^0.7.3" + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map": "0.7.3" }, "dependencies": { "minimist": { @@ -14594,19 +14720,19 @@ "integrity": "sha512-ECOOQRxXCYnUUePG5h/+Z1Zouobk3KFpIHA9aKBB/nnMxs97S1JJPDGt5J4cGm1y9U9VmVlfboOxA8n1kSNzGw==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.4.2", + "commander": "2.15.1", + "diff": "3.5.0", + "glob": "7.1.2", + "js-yaml": "3.13.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "resolve": "1.7.1", + "semver": "5.5.0", + "tslib": "1.9.3", + "tsutils": "2.29.0" }, "dependencies": { "chalk": { @@ -14615,9 +14741,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "tsutils": { @@ -14626,7 +14752,7 @@ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, "requires": { - "tslib": "^1.8.1" + "tslib": "1.9.3" } } } @@ -14637,7 +14763,7 @@ "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", "dev": true, "requires": { - "tslib": "^1.8.1" + "tslib": "1.9.3" } }, "tty-browserify": { @@ -14652,7 +14778,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -14668,7 +14794,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-is": { @@ -14678,7 +14804,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.18" } }, "typedarray": { @@ -14699,8 +14825,8 @@ "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", "dev": true, "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" + "commander": "2.17.1", + "source-map": "0.6.1" }, "dependencies": { "commander": { @@ -14748,15 +14874,15 @@ "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" + "arr-flatten": "1.1.0", + "arr-map": "2.0.2", + "bach": "1.2.0", + "collection-map": "1.0.0", + "es6-weak-map": "2.0.2", + "last-run": "1.1.1", + "object.defaults": "1.1.0", + "object.reduce": "1.0.1", + "undertaker-registry": "1.0.1" } }, "undertaker-registry": { @@ -14770,10 +14896,10 @@ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -14781,7 +14907,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -14789,10 +14915,10 @@ "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -14803,7 +14929,7 @@ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "2.0.1" } }, "unique-slug": { @@ -14812,7 +14938,7 @@ "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", "dev": true, "requires": { - "imurmurhash": "^0.1.4" + "imurmurhash": "0.1.4" } }, "unique-stream": { @@ -14821,8 +14947,8 @@ "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, "requires": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" + "json-stable-stringify-without-jsonify": "1.0.1", + "through2-filter": "3.0.0" } }, "unique-string": { @@ -14831,14 +14957,13 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", - "dev": true + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" }, "unpipe": { "version": "1.0.0", @@ -14851,8 +14976,8 @@ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -14860,9 +14985,9 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -14899,16 +15024,16 @@ "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "boxen": "1.3.0", + "chalk": "2.2.2", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.2.1", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" } }, "uri-js": { @@ -14917,7 +15042,7 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } }, "urix": { @@ -14949,8 +15074,8 @@ "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", "dev": true, "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" + "querystringify": "2.1.0", + "requires-port": "1.0.0" } }, "url-parse-lax": { @@ -14959,7 +15084,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "urlgrey": { @@ -14973,7 +15098,7 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "user-home": { @@ -14982,7 +15107,7 @@ "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } }, "useragent": { @@ -14991,8 +15116,8 @@ "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", "dev": true, "requires": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" + "lru-cache": "4.1.3", + "tmp": "0.0.33" } }, "util": { @@ -15035,7 +15160,7 @@ "integrity": "sha512-0m69VIK2dudEf2Ub0xwLQhZkDZu85OmiOpTw+UGDt56ibviYICHziM/3aE+oVg7IjGPp0c83w3eSVqa+lYZ9UQ==", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.1" } }, "validate-npm-package-license": { @@ -15044,8 +15169,8 @@ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "validate-npm-package-name": { @@ -15054,7 +15179,7 @@ "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", "dev": true, "requires": { - "builtins": "^1.0.3" + "builtins": "1.0.3" } }, "value-or-function": { @@ -15075,9 +15200,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "vinyl": { @@ -15086,12 +15211,12 @@ "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", "dev": true, "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.2", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" } }, "vinyl-fs": { @@ -15100,23 +15225,23 @@ "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, "requires": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" + "fs-mkdirp-stream": "1.0.0", + "glob-stream": "6.1.0", + "graceful-fs": "4.1.11", + "is-valid-glob": "1.0.0", + "lazystream": "1.0.0", + "lead": "1.0.0", + "object.assign": "4.1.0", + "pumpify": "1.5.1", + "readable-stream": "2.3.6", + "remove-bom-buffer": "3.0.0", + "remove-bom-stream": "1.2.0", + "resolve-options": "1.1.0", + "through2": "2.0.3", + "to-through": "2.0.0", + "value-or-function": "3.0.0", + "vinyl": "2.1.0", + "vinyl-sourcemap": "1.1.0" }, "dependencies": { "is-valid-glob": { @@ -15133,13 +15258,13 @@ "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", "dev": true, "requires": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" + "append-buffer": "1.0.2", + "convert-source-map": "1.5.1", + "graceful-fs": "4.1.11", + "normalize-path": "2.1.1", + "now-and-later": "2.0.1", + "remove-bom-buffer": "3.0.0", + "vinyl": "2.1.0" } }, "viz.js": { @@ -15169,9 +15294,9 @@ "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", "dev": true, "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "chokidar": "2.0.3", + "graceful-fs": "4.1.11", + "neo-async": "2.6.0" } }, "wbuf": { @@ -15180,7 +15305,7 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "requires": { - "minimalistic-assert": "^1.0.0" + "minimalistic-assert": "1.0.1" } }, "web-animations-js": { @@ -15194,8 +15319,8 @@ "integrity": "sha512-fbyKiVu3azzIc5d4+26YfuPQcFTlgFQV5yQ/0OQj4Ybkl4g1YQuIPskf5v5wqwRJhHJnPHthB6tqCjWHOKLWag==", "dev": true, "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" + "@types/selenium-webdriver": "3.0.10", + "selenium-webdriver": "3.6.0" } }, "webpack": { @@ -15208,26 +15333,26 @@ "@webassemblyjs/helper-module-context": "1.7.11", "@webassemblyjs/wasm-edit": "1.7.11", "@webassemblyjs/wasm-parser": "1.7.11", - "acorn": "^6.0.5", - "acorn-dynamic-import": "^4.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.4", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" + "acorn": "6.1.0", + "acorn-dynamic-import": "4.0.0", + "ajv": "6.9.1", + "ajv-keywords": "3.4.0", + "chrome-trace-event": "1.0.0", + "enhanced-resolve": "4.1.0", + "eslint-scope": "4.0.0", + "json-parse-better-errors": "1.0.2", + "loader-runner": "2.4.0", + "loader-utils": "1.2.3", + "memory-fs": "0.4.1", + "micromatch": "3.1.10", + "mkdirp": "0.5.1", + "neo-async": "2.6.0", + "node-libs-browser": "2.2.0", + "schema-utils": "0.4.7", + "tapable": "1.1.1", + "terser-webpack-plugin": "1.2.2", + "watchpack": "1.6.0", + "webpack-sources": "1.3.0" }, "dependencies": { "acorn": { @@ -15242,8 +15367,8 @@ "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "ajv": "6.9.1", + "ajv-keywords": "3.4.0" } } } @@ -15254,8 +15379,8 @@ "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", "dev": true, "requires": { - "source-list-map": "~0.1.7", - "source-map": "~0.4.1" + "source-list-map": "0.1.8", + "source-map": "0.4.4" }, "dependencies": { "source-list-map": { @@ -15270,7 +15395,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -15281,10 +15406,10 @@ "integrity": "sha512-4dwCh/AyMOYAybggUr8fiCkRnjVDp+Cqlr9c+aaNB3GJYgRGYQWJ1YX/WAKUNA9dPNHZ6QSN2lYDKqjKSI8Vqw==", "dev": true, "requires": { - "memory-fs": "~0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" + "memory-fs": "0.4.1", + "mime": "2.4.0", + "range-parser": "1.2.0", + "webpack-log": "2.0.0" }, "dependencies": { "mime": { @@ -15302,34 +15427,34 @@ "dev": true, "requires": { "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^3.1.0", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "~0.18.0", - "import-local": "^2.0.0", - "internal-ip": "^3.0.1", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "schema-utils": "^1.0.0", - "selfsigned": "^1.9.1", - "semver": "^5.6.0", - "serve-index": "^1.7.2", + "bonjour": "3.5.0", + "chokidar": "2.0.3", + "compression": "1.7.3", + "connect-history-api-fallback": "1.6.0", + "debug": "3.2.6", + "del": "3.0.0", + "express": "4.16.4", + "html-entities": "1.2.1", + "http-proxy-middleware": "0.18.0", + "import-local": "2.0.0", + "internal-ip": "3.0.1", + "ip": "1.1.5", + "killable": "1.0.1", + "loglevel": "1.6.1", + "opn": "5.3.0", + "portfinder": "1.0.20", + "schema-utils": "1.0.0", + "selfsigned": "1.10.4", + "semver": "5.6.0", + "serve-index": "1.9.1", "sockjs": "0.3.19", "sockjs-client": "1.3.0", - "spdy": "^4.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^5.1.0", - "url": "^0.11.0", + "spdy": "4.0.0", + "strip-ansi": "3.0.1", + "supports-color": "5.4.0", + "url": "0.11.0", "webpack-dev-middleware": "3.4.0", - "webpack-log": "^2.0.0", + "webpack-log": "2.0.0", "yargs": "12.0.2" }, "dependencies": { @@ -15356,9 +15481,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" }, "dependencies": { "strip-ansi": { @@ -15367,7 +15492,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -15378,11 +15503,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.6.0", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "debug": { @@ -15391,7 +15516,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "decamelize": { @@ -15409,13 +15534,13 @@ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "find-up": { @@ -15424,7 +15549,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "get-stream": { @@ -15433,7 +15558,7 @@ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "pump": "^3.0.0" + "pump": "3.0.0" } }, "invert-kv": { @@ -15454,7 +15579,7 @@ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { - "invert-kv": "^2.0.0" + "invert-kv": "2.0.0" } }, "locate-path": { @@ -15463,8 +15588,8 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "mime": { @@ -15485,9 +15610,9 @@ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" + "execa": "1.0.0", + "lcid": "2.0.0", + "mem": "4.0.0" } }, "p-limit": { @@ -15496,7 +15621,7 @@ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.0.0" } }, "p-locate": { @@ -15505,7 +15630,7 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.1.0" } }, "p-try": { @@ -15520,8 +15645,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "semver": { @@ -15536,8 +15661,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "strip-ansi": { @@ -15546,7 +15671,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -15557,10 +15682,10 @@ "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", "dev": true, "requires": { - "memory-fs": "~0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" + "memory-fs": "0.4.1", + "mime": "2.4.0", + "range-parser": "1.2.0", + "webpack-log": "2.0.0" } }, "which-module": { @@ -15575,18 +15700,18 @@ "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" + "cliui": "4.1.0", + "decamelize": "2.0.0", + "find-up": "3.0.0", + "get-caller-file": "1.0.2", + "os-locale": "3.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "4.0.0", + "yargs-parser": "10.1.0" } }, "yargs-parser": { @@ -15595,7 +15720,7 @@ "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -15606,8 +15731,8 @@ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", "dev": true, "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" + "ansi-colors": "3.2.3", + "uuid": "3.3.2" }, "dependencies": { "ansi-colors": { @@ -15630,7 +15755,7 @@ "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==", "dev": true, "requires": { - "lodash": "^4.17.5" + "lodash": "4.17.13" } }, "webpack-sources": { @@ -15639,8 +15764,8 @@ "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", "dev": true, "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "source-list-map": "2.0.1", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -15657,7 +15782,7 @@ "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==", "dev": true, "requires": { - "webpack-core": "^0.6.8" + "webpack-core": "0.6.9" } }, "websocket-driver": { @@ -15666,8 +15791,8 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" + "http-parser-js": "0.5.0", + "websocket-extensions": "0.1.3" } }, "websocket-extensions": { @@ -15688,7 +15813,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -15703,7 +15828,7 @@ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "string-width": "^1.0.2 || 2" + "string-width": "1.0.2" } }, "widest-line": { @@ -15712,7 +15837,7 @@ "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { - "string-width": "^2.1.1" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -15733,8 +15858,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -15743,7 +15868,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -15767,7 +15892,7 @@ "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", "dev": true, "requires": { - "errno": "~0.1.7" + "errno": "0.1.7" } }, "wrap-ansi": { @@ -15776,8 +15901,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" } }, "wrappy": { @@ -15792,7 +15917,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "write-file-atomic": { @@ -15801,9 +15926,9 @@ "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "ws": { @@ -15812,9 +15937,9 @@ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "async-limiter": "1.0.0", + "safe-buffer": "5.1.2", + "ultron": "1.1.1" } }, "xdg-basedir": { @@ -15834,8 +15959,8 @@ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "dev": true, "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "sax": "1.2.4", + "xmlbuilder": "9.0.7" }, "dependencies": { "sax": { @@ -15886,7 +16011,7 @@ "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", "dev": true, "requires": { - "cuint": "^0.2.2" + "cuint": "0.2.2" } }, "y18n": { @@ -15908,9 +16033,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } }, @@ -15920,7 +16045,7 @@ "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", "dev": true, "requires": { - "camelcase": "^3.0.0" + "camelcase": "3.0.0" }, "dependencies": { "camelcase": { @@ -15937,7 +16062,7 @@ "integrity": "sha1-7CblzIfVYBud+EMtvdPNLlFzoHE=", "dev": true, "requires": { - "buffer-crc32": "~0.2.3" + "buffer-crc32": "0.2.13" } }, "yeast": { diff --git a/package.json b/package.json index 9f60f242f4..c720db818d 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "test-frontend:core": "ng test core --code-coverage --watch=false", "test-frontend:store": "ng test store --code-coverage --watch=false", "test-frontend:cloud-foundry": "ng test cloud-foundry --code-coverage --watch=false", + "test-frontend:cf-autoscaler": "ng test cf-autoscaler --code-coverage --watch=false", "posttest": "istanbul report json && node build/combine-coverage.js", "codecov": "codecov -f coverage/coverage-final.json", "lint": "ng lint --format stylish", @@ -61,15 +62,18 @@ "@ngrx/store": "^7.2.0", "@ngrx/store-devtools": "^7.2.0", "@swimlane/ngx-charts": "^10.1.0", + "@types/moment-timezone": "^0.5.12", "@types/marked": "^0.6.5", "angular2-virtual-scroll": "^0.3.1", "core-js": "^2.6.5", "hammerjs": "^2.0.8", "immer": "^3.1.3", - "lodash-es": "^4.17.11", + "lodash-es": "^4.17.14", "mappy-breakpoints": "^0.2.3", - "marked": "^0.6.2", + "marked": "^0.7.0", + "intersect": "^1.0.1", "moment": "^2.24.0", + "moment-timezone": "^0.5.12", "ngrx-store-localstorage": "cf-stratos/ngrx-store-localstorage#lodash-dep-update", "ngx-moment": "^3.3.0", "normalizr": "^3.2.3", @@ -113,13 +117,13 @@ "jasmine-spec-reporter": "~4.2.1", "js-yaml": "~3.13.1", "karma": "~4.0.0", - "karma-chrome-launcher": "~2.1.1", + "karma-chrome-launcher": "~2.2.0", "karma-cli": "~1.0.1", "karma-coverage-istanbul-reporter": "^2.0.1", "karma-jasmine": "~2.0.1", "karma-jasmine-html-reporter": "^1.4.0", "karma-spec-reporter": "0.0.31", - "lodash": "^4.17.10", + "lodash": "^4.17.13", "mem": "4.0.0", "mktemp": "^0.4.0", "ng-packagr": "^4.7.1", diff --git a/protractor.conf.js b/protractor.conf.js index 9a3e14d635..6d8eb722f5 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -11,6 +11,7 @@ const skipPlugin = require('./src/test-e2e/skip-plugin.js'); const globby = require('globby'); const timeReporterPlugin = require('./src/test-e2e/time-reporter-plugin.js'); const browserReporterPlugin = require('./src/test-e2e/browser-reporter-plugin.js'); +const https = require('https'); // Test report folder name var timestamp = moment().format('YYYYDDMM-hh.mm.ss'); @@ -87,7 +88,7 @@ const longSuite2 = globby.sync([ './src/test-e2e/cloud-foundry/space-level/space-invite-user-e2e.spec.ts' ]) -const fullMinusLongSuites= globby.sync([ +const fullMinusLongSuites = globby.sync([ ...fullSuite, ...longSuite.map(file => '!' + file), ...longSuite2.map(file => '!' + file), @@ -167,6 +168,45 @@ exports.config = { browserReporterPlugin.install(jasmine, browser); jasmine.getEnv().addReporter(browserReporterPlugin.reporter()); } + + // Validate that the Github API url that the client will use during e2e tests is responding + const githubApiUrl = secrets.stratosGitHubApiUrl || 'https://api.github.com'; + const path = '/repos/nwmac/cf-quick-app' + console.log(`Validating Github API Url Using: '${githubApiUrl + path}'`) + + // This chunk can disappear when we update node to include the version of http that accepts `get(url, option, callback)` + const hasHttps = githubApiUrl.indexOf('https://') === 0; + const tempHost = hasHttps ? githubApiUrl.substring(8, githubApiUrl.length) : githubApiUrl + const hasPort = tempHost.indexOf(':') >= 0; + const port = hasPort ? parseInt(tempHost.substring(tempHost.indexOf(':') + 1, tempHost.length)) : hasHttps ? 443 : null; + const host = hasPort ? tempHost.replace(':' + port, '') : tempHost; + + const options = { + host, + port, + path, + accept: '*/*', + method: 'GET', + // Required by github to avoid 403 + headers: { + 'User-Agent': 'request' + }, + rejectUnauthorized: false + } + + var defer = protractor.promise.defer(); + https + .get(options, (resp) => { + if (resp.statusCode >= 400) { + defer.reject('Failed to validate Github API Url. Status Code: ' + resp.statusCode); + } else { + defer.fulfill('Github API Url responding'); + } + }) + .on("error", (err) => { + defer.reject('Failed to validate Github API Url: ' + err.message); + }); + return defer.promise; } }; diff --git a/src/frontend/packages/cf-autoscaler/karma.conf.js b/src/frontend/packages/cf-autoscaler/karma.conf.js new file mode 100644 index 0000000000..aa31921f71 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/karma.conf.js @@ -0,0 +1,8 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + ...require('../../../../build/karma.conf.creator.js')('cf-autoscaler')(config) + }) +} diff --git a/src/frontend/packages/cf-autoscaler/ng-package.json b/src/frontend/packages/cf-autoscaler/ng-package.json new file mode 100644 index 0000000000..dc5592ade4 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../../dist/cf-autoscaler", + "lib": { + "entryFile": "src/public_api.ts" + } +} diff --git a/src/frontend/packages/cf-autoscaler/package.json b/src/frontend/packages/cf-autoscaler/package.json new file mode 100644 index 0000000000..5258e6ebc3 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/package.json @@ -0,0 +1,8 @@ +{ + "name": "cf-autoscaler", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^6.0.0-rc.0 || ^6.0.0", + "@angular/core": "^6.0.0-rc.0 || ^6.0.0" + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/cf-autoscaler-testing.module.ts b/src/frontend/packages/cf-autoscaler/src/cf-autoscaler-testing.module.ts new file mode 100644 index 0000000000..cd4586dc0b --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/cf-autoscaler-testing.module.ts @@ -0,0 +1,16 @@ +import { CfAutoscalerModule } from './cf-autoscaler.module'; +import { registerEntitiesForTesting } from '../../core/test-framework/store-test-helper'; +import { autoscalerEntities, AutoscalerStoreModule } from './store/autoscaler.store.module'; +import { NgModule } from '@angular/core'; + +@NgModule({ + imports: [ + AutoscalerStoreModule + ] +}) +export class CfAutoscalerTestingModule { + + constructor() { + registerEntitiesForTesting(autoscalerEntities); + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/cf-autoscaler.module.ts b/src/frontend/packages/cf-autoscaler/src/cf-autoscaler.module.ts new file mode 100644 index 0000000000..b4764adbff --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/cf-autoscaler.module.ts @@ -0,0 +1,46 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { of } from 'rxjs'; + +import { CoreModule } from '../../core/src/core/core.module'; +import { MDAppModule } from '../../core/src/core/md.module'; +import { SharedModule } from '../../core/src/shared/shared.module'; +import { AutoscalerModule } from './core/autoscaler.module'; +import { AutoscalerTabExtensionComponent } from './features/autoscaler-tab-extension/autoscaler-tab-extension.component'; +import { registerAutoscalerEntities } from './store/autoscaler-entity-generator'; + +registerAutoscalerEntities(); + +const customRoutes: Routes = [ + { + path: 'autoscaler', + loadChildren: './core/autoscaler.module#AutoscalerModule', + data: { + stratosNavigation: { + text: 'Applications', + matIcon: 'apps', + position: 20, + hidden: of(true) + } + }, + }, +]; + +@NgModule({ + imports: [ + CoreModule, + CommonModule, + SharedModule, + MDAppModule, + NgxChartsModule, + AutoscalerModule, + RouterModule.forRoot(customRoutes), + ], + declarations: [ + AutoscalerTabExtensionComponent + ], + entryComponents: [AutoscalerTabExtensionComponent] +}) +export class CfAutoscalerModule { } diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-metric.spec.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-metric.spec.ts new file mode 100644 index 0000000000..4ad5bb4cb2 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-metric.spec.ts @@ -0,0 +1,488 @@ +import { + isEqual +} from './autoscaler-util'; +import { + insertEmptyMetrics, buildMetricData +} from './autoscaler-transform-metric'; + +describe('Autoscaler Transform Metric Helper', () => { + it('insertEmptyMetrics', () => { + const descTarget = [20, 40, 60, 80, 100]; + const descSource1 = insertEmptyMetrics([], 100, 10, -20); + descSource1.map((item, index) => { + expect(isEqual(item.time, descTarget[index])).toBe(true); + }); + const descSource2 = insertEmptyMetrics([], 100, 20, -20); + descSource2.map((item, index) => { + expect(isEqual(item.time, descTarget[index])).toBe(true); + }); + const ascTarget = [10, 30, 50, 70, 90]; + const ascSource1 = insertEmptyMetrics([], 10, 90, 20); + ascSource1.map((item, index) => { + expect(isEqual(item.time, ascTarget[index])).toBe(true); + }); + const ascSource2 = insertEmptyMetrics([], 10, 100, 20); + ascSource2.map((item, index) => { + expect(isEqual(item.time, ascTarget[index])).toBe(true); + }); + }); + it('buildMetricData', () => { + const metricName = 'throughput'; + const data = { + total_results: 12, + total_pages: 1, + page: 1, + prev_url: null, + next_url: null, + resources: [ + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '6', + unit: 'rps', + timestamp: 1557026418641445600 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '5', + unit: 'rps', + timestamp: 1557026457387453200 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '5', + unit: 'rps', + timestamp: 1557026497600111000 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '5', + unit: 'rps', + timestamp: 1557026538904853200 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '6', + unit: 'rps', + timestamp: 1557026577882890500 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '8', + unit: 'rps', + timestamp: 1557026616931637000 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '12', + unit: 'rps', + timestamp: 1557026657849241600 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '18', + unit: 'rps', + timestamp: 1557026697503883000 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '21', + unit: 'rps', + timestamp: 1557026737224771000 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '18', + unit: 'rps', + timestamp: 1557026778997745700 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '16', + unit: 'rps', + timestamp: 1557026817953504800 + }, + { + app_id: '2bd98ff4-99f4-422a-a037-172298277c8b', + name: 'throughput', + value: '14', + unit: 'rps', + timestamp: 1557026857236819500 + } + ] + }; + const startTime = 1557026400000000000; + const endTime = 1557026880000000000; + const skipFormat = false; + const trigger = { + lower: [ + { + adjustment: '-1', + breach_duration_secs: 60, + cool_down_secs: 60, + metric_type: 'throughput', + operator: '<=', + threshold: 5, + color: 'rgba(51, 204, 255, 0.6)' + } + ], + upper: [ + { + adjustment: '+2', + breach_duration_secs: 60, + cool_down_secs: 60, + metric_type: 'throughput', + operator: '>', + threshold: 20, + color: 'rgba(255, 0, 0, 0.6)' + }, + { + adjustment: '+1', + breach_duration_secs: 60, + cool_down_secs: 60, + metric_type: 'throughput', + operator: '>', + threshold: 10, + color: 'rgba(255, 128, 0, 0.6)' + } + ], + query: { + metric: 'policy', + params: { + start: 1557026400, + end: 1557026880, + step: 9.6 + } + } + }; + const expectedResult = { + latest: { + target: [ + { + name: 'throughput', + value: 14 + } + ], + colorTarget: [ + { + name: 'throughput', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: 'upper threshold: > 20', + value: 'rgba(255, 0, 0, 0.6)' + }, + { + name: 'upper threshold: > 10', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: 'lower threshold: <= 5', + value: 'rgba(51, 204, 255, 0.6)' + } + ] + }, + formated: { + target: [ + { + time: 1557026419, + name: '03:20:19', + value: 6 + }, + { + time: 1557026458, + name: '03:20:58', + value: 5 + }, + { + time: 1557026497, + name: '03:21:37', + value: 5 + }, + { + time: 1557026536, + name: '03:22:16', + value: 5 + }, + { + time: 1557026575, + name: '03:22:55', + value: 6 + }, + { + time: 1557026614, + name: '03:23:34', + value: 8 + }, + { + time: 1557026653, + name: '03:24:13', + value: 12 + }, + { + time: 1557026692, + name: '03:24:52', + value: 18 + }, + { + time: 1557026731, + name: '03:25:31', + value: 21 + }, + { + time: 1557026770, + name: '03:26:10', + value: 18 + }, + { + time: 1557026809, + name: '03:26:49', + value: 16 + }, + { + time: 1557026848, + name: '03:27:28', + value: 14 + } + ], + colorTarget: [ + { + name: '03:20:19', + value: 'rgba(90,167,0,0.6)' + }, + { + name: '03:20:58', + value: 'rgba(51, 204, 255, 0.6)' + }, + { + name: '03:21:37', + value: 'rgba(51, 204, 255, 0.6)' + }, + { + name: '03:22:16', + value: 'rgba(51, 204, 255, 0.6)' + }, + { + name: '03:22:55', + value: 'rgba(90,167,0,0.6)' + }, + { + name: '03:23:34', + value: 'rgba(90,167,0,0.6)' + }, + { + name: '03:24:13', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: '03:24:52', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: '03:25:31', + value: 'rgba(255, 0, 0, 0.6)' + }, + { + name: '03:26:10', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: '03:26:49', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: '03:27:28', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: 'upper threshold: > 20', + value: 'rgba(255, 0, 0, 0.6)' + }, + { + name: 'upper threshold: > 10', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: 'lower threshold: <= 5', + value: 'rgba(51, 204, 255, 0.6)' + } + ] + }, + markline: [ + { + name: 'upper threshold: > 20', + series: [ + { + name: '03:20:19', + value: 20 + }, + { + name: '03:20:58', + value: 20 + }, + { + name: '03:21:37', + value: 20 + }, + { + name: '03:22:16', + value: 20 + }, + { + name: '03:22:55', + value: 20 + }, + { + name: '03:23:34', + value: 20 + }, + { + name: '03:24:13', + value: 20 + }, + { + name: '03:24:52', + value: 20 + }, + { + name: '03:25:31', + value: 20 + }, + { + name: '03:26:10', + value: 20 + }, + { + name: '03:26:49', + value: 20 + }, + { + name: '03:27:28', + value: 20 + } + ] + }, + { + name: 'upper threshold: > 10', + series: [ + { + name: '03:20:19', + value: 10 + }, + { + name: '03:20:58', + value: 10 + }, + { + name: '03:21:37', + value: 10 + }, + { + name: '03:22:16', + value: 10 + }, + { + name: '03:22:55', + value: 10 + }, + { + name: '03:23:34', + value: 10 + }, + { + name: '03:24:13', + value: 10 + }, + { + name: '03:24:52', + value: 10 + }, + { + name: '03:25:31', + value: 10 + }, + { + name: '03:26:10', + value: 10 + }, + { + name: '03:26:49', + value: 10 + }, + { + name: '03:27:28', + value: 10 + } + ] + }, + { + name: 'lower threshold: <= 5', + series: [ + { + name: '03:20:19', + value: 5 + }, + { + name: '03:20:58', + value: 5 + }, + { + name: '03:21:37', + value: 5 + }, + { + name: '03:22:16', + value: 5 + }, + { + name: '03:22:55', + value: 5 + }, + { + name: '03:23:34', + value: 5 + }, + { + name: '03:24:13', + value: 5 + }, + { + name: '03:24:52', + value: 5 + }, + { + name: '03:25:31', + value: 5 + }, + { + name: '03:26:10', + value: 5 + }, + { + name: '03:26:49', + value: 5 + }, + { + name: '03:27:28', + value: 5 + } + ] + } + ], + unit: 'rps', + chartMaxValue: 30 + }; + const result = buildMetricData(metricName, data, startTime, endTime, skipFormat, trigger, 'UTC'); + expect(isEqual(expectedResult, result)).toBe(true); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-metric.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-metric.ts new file mode 100644 index 0000000000..88b6e9cb64 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-metric.ts @@ -0,0 +1,276 @@ +import * as moment from 'moment-timezone'; + +import { PaginationResponse } from '../../../../store/src/types/api.types'; +import { + AppAutoscalerMetricBasicInfo, + AppAutoscalerMetricData, + AppAutoscalerMetricDataLine, + AppAutoscalerMetricDataLocal, + AppAutoscalerMetricDataPoint, + AppScalingRule, + AppScalingTrigger, +} from '../../store/app-autoscaler.types'; +import { AutoscalerConstants, getScaleType } from './autoscaler-util'; + +function initMetricData(metricName: string): AppAutoscalerMetricDataLocal { + return { + latest: { + target: [{ + name: metricName, + value: 0 + }], + colorTarget: [ + { + name: metricName, + value: 'rgba(0,0,0,0.2)' + } + ] + }, + formated: { + target: [], + colorTarget: [] + }, + markline: [], + unit: AutoscalerConstants.metricMap[metricName].unit_internal, + chartMaxValue: 0, + }; +} + +export function buildMetricData( + metricName: string, + data: PaginationResponse, + startTime: number, + endTime: number, + skipFormat: boolean, + trigger: AppScalingTrigger, + timezone?: string +): AppAutoscalerMetricDataLocal { + const result = initMetricData(metricName); + if (data.resources.length > 0) { + const basicInfo = getMetricBasicInfo(metricName, data.resources, trigger); + result.unit = basicInfo.unit; + result.chartMaxValue = basicInfo.chartMaxValue; + if (!skipFormat) { + startTime = Math.round(startTime / AutoscalerConstants.S2NS); + endTime = Math.round(endTime / AutoscalerConstants.S2NS); + const target = transformMetricData(data.resources, basicInfo.interval, startTime, endTime, timezone); + const colorTarget = buildMetricColorData(target, trigger); + result.formated = { + target, + colorTarget + }; + result.markline = buildMarkLineData(target, trigger); + } + result.latest.target = [{ + name: metricName, + value: Number(data.resources[data.resources.length - 1].value) + }]; + result.latest.colorTarget = buildMetricColorData(result.latest.target, trigger); + } + return result; +} + +export function insertEmptyMetrics( + data: AppAutoscalerMetricDataPoint[], + startTime: number, + endTime: number, + interval: number, + timezone?: string +): AppAutoscalerMetricDataPoint[] { + const insertEmptyNumber = Math.floor((endTime - startTime) / interval) + 1; + for (let i = 0; i < insertEmptyNumber; i++) { + const emptyMetric = buildSingleMetricData(startTime + i * interval, 0, timezone); + if (interval < 0) { + data.unshift(emptyMetric); + } else { + data.push(emptyMetric); + } + } + return data; +} + +function buildSingleMetricData(timestamp: number, value: number | string, timezone: string): AppAutoscalerMetricDataPoint { + const name = (() => { + if (timezone) { + return moment(timestamp * 1000).tz(timezone).format(AutoscalerConstants.MomentFormateTimeS); + } else { + return moment(timestamp * 1000).format(AutoscalerConstants.MomentFormateTimeS); + } + })(); + return { + time: timestamp, + name, + value + }; +} + +function transformMetricData( + source: AppAutoscalerMetricData[], + interval: number, + startTime: number, + endTime: number, + timezone: string): AppAutoscalerMetricDataPoint[] { + if (source.length === 0) { + return []; + } + const scope = Math.round(interval / 2); + const target: AppAutoscalerMetricDataPoint[] = []; + let targetTimestamp = Math.round(source[0].timestamp / AutoscalerConstants.S2NS); + let targetIndex = insertEmptyMetrics(target, targetTimestamp - interval, startTime, -interval, timezone).length; + let sourceIndex = 0; + while (sourceIndex < source.length) { + if (!target[targetIndex]) { + target[targetIndex] = buildSingleMetricData(targetTimestamp, 0, timezone); + } + const metric = source[sourceIndex]; + const sourceTimestamp = Math.round(metric.timestamp / AutoscalerConstants.S2NS); + if (sourceTimestamp < targetTimestamp - scope) { + sourceIndex++; + } else if (sourceTimestamp > targetTimestamp + scope) { + targetIndex++; + targetTimestamp += interval; + } else { + target[targetIndex].value = Number(metric.value); + sourceIndex++; + } + } + return insertEmptyMetrics(target, target[targetIndex].time + interval, endTime, interval, timezone); +} + +function buildMetricColorData(metricData: AppAutoscalerMetricDataPoint[], trigger: AppScalingTrigger): AppAutoscalerMetricDataPoint[] { + const colorTarget: AppAutoscalerMetricDataPoint[] = []; + metricData.map((item) => { + colorTarget.push({ + name: item.name, + value: getColor(trigger, item.value), + }); + }); + if (trigger.upper.length > 0) { + buildSingleColor(colorTarget, trigger.upper); + } + if (trigger.lower.length > 0) { + buildSingleColor(colorTarget, trigger.lower); + } + return colorTarget; +} + +function buildSingleColor(lineChartSeries: AppAutoscalerMetricDataPoint[], ul: AppScalingRule[]) { + ul.forEach((item) => { + const lineData = { + name: buildTriggerName(item), + value: item.color + }; + lineChartSeries.push(lineData); + }); +} + +function buildMarkLineData(metricData: AppAutoscalerMetricDataPoint[], trigger: AppScalingTrigger): AppAutoscalerMetricDataLine[] { + return buildSingleMarkLine(metricData, trigger.upper).concat(buildSingleMarkLine(metricData, trigger.lower)); +} + +function buildTriggerName(item: AppScalingRule): string { + const type = getScaleType(item.operator); + return `${type} threshold: ${item.operator} ${item.threshold}`; +} + +function buildSingleMarkLine(metricData: AppAutoscalerMetricDataPoint[], ul: AppScalingRule[]) { + return ul.reduce((lineChartSeries, item) => { + const lineData = { + name: buildTriggerName(item), + series: metricData.map((data) => { + return { + name: data.name, + value: item.threshold, + }; + }) + }; + lineChartSeries.push(lineData); + return lineChartSeries; + }, []); +} + +function executeCompare(val1: number, operator: string, val2: number): boolean { + switch (operator) { + case '>': + return val1 > val2; + case '>=': + return val1 >= val2; + case '<': + return val1 < val2; + default: + return val1 <= val2; + } +} + +function getColorCommon(triggerRules: AppScalingRule[], value: number, isLower?: boolean) { + for (let i = 0; triggerRules && triggerRules.length > 0 && i < triggerRules.length; i++) { + const index = isLower ? triggerRules.length - 1 - i : i; + if (executeCompare(value, triggerRules[index].operator, triggerRules[index].threshold)) { + return triggerRules[index].color; + } + } + return ''; +} + +function getColor(trigger: AppScalingTrigger, value: any): string { + if (Number.isNaN(value)) { + return AutoscalerConstants.normalColor; + } + return getColorCommon(trigger.upper, value) || getColorCommon(trigger.lower, value, true) || AutoscalerConstants.normalColor; +} + +function getMetricBasicInfo( + metricName: string, + source: AppAutoscalerMetricData[], + trigger: AppScalingTrigger +): AppAutoscalerMetricBasicInfo { + const intervalMap = {}; + let maxCount = 1; + let preTimestamp = 0; + let maxValue = -1; + const unit = AutoscalerConstants.metricMap[metricName].unit_internal; + intervalMap[AutoscalerConstants.metricMap[metricName].interval] = 1; + const resultInterval = source.reduce((interval, item) => { + maxValue = Math.max(Number(item.value), maxValue); + const thisTimestamp = Math.round(item.timestamp / AutoscalerConstants.S2NS); + const currentInterval = thisTimestamp - preTimestamp; + intervalMap[currentInterval] = intervalMap[currentInterval] ? intervalMap[currentInterval] + 1 : 1; + if (intervalMap[currentInterval] > maxCount) { + interval = currentInterval; + maxCount = intervalMap[currentInterval]; + } + preTimestamp = thisTimestamp; + // unit = item.unit === '' ? unit : item.unit; + return interval; + }, AutoscalerConstants.metricMap[metricName].interval); + return { + interval: resultInterval, + unit, + chartMaxValue: getChartMax(trigger, maxValue, metricName) + }; +} + +function getMaxThreshod(rules: AppScalingRule[]) { + return rules.length > 0 ? rules[0].threshold : 0; +} + +function getChartMax(trigger: AppScalingTrigger, maxValue: number, metricName: string): number { + if (AutoscalerConstants.MetricPercentageTypes.indexOf(metricName) >= 0) { + return 100; + } + const thresholdCount = trigger.upper.length + trigger.lower.length; + const maxThreshold = Math.max(getMaxThreshod(trigger.upper), getMaxThreshod(trigger.lower)); + const thresholdmax = Math.ceil(maxThreshold * (thresholdCount + 1) / (thresholdCount)); + return getTrimmedInteger(Math.max(maxValue, thresholdmax, 10)); +} + +function getTrimmedInteger(thresholdmax: number): number { + for (let i = 10; i < Number.MAX_VALUE && i < thresholdmax; i = i * 10) { + if (thresholdmax / i >= 1 && thresholdmax / i < 10 && thresholdmax > 100) { + return (Math.ceil(thresholdmax / i * 10)) * i / 10; + } else if (thresholdmax / i >= 1 && thresholdmax / i < 10 && thresholdmax <= 100) { + return (Math.ceil(thresholdmax / i)) * i; + } + } + return thresholdmax; +} diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-policy.spec.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-policy.spec.ts new file mode 100644 index 0000000000..9512064caf --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-policy.spec.ts @@ -0,0 +1,329 @@ +import { isEqual } from './autoscaler-util'; +import { + autoscalerTransformArrayToMap, + autoscalerTransformMapToArray, +} from './autoscaler-transform-policy'; +import { AppAutoscalerPolicy, AppAutoscalerPolicyLocal } from '../../store/app-autoscaler.types'; + +describe('Autoscaler Transform Policy Helper', () => { + it('Test policy transformation', () => { + const arrayPolicy: AppAutoscalerPolicy = { + instance_min_count: 1, + instance_max_count: 10, + scaling_rules: [ + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 10, + operator: '<=', + cool_down_secs: 300, + adjustment: '-2' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 30, + operator: '<', + cool_down_secs: 300, + adjustment: '-1' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 120, + operator: '>', + cool_down_secs: 300, + adjustment: '+3' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 90, + operator: '>=', + cool_down_secs: 300, + adjustment: '+2' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 200, + operator: '>=', + cool_down_secs: 300, + adjustment: '+4' + }, + { + metric_type: 'memoryutil', + breach_duration_secs: 600, + threshold: 20, + operator: '<', + cool_down_secs: 300, + adjustment: '-3' + }, + { + metric_type: 'responsetime', + breach_duration_secs: 600, + threshold: 50, + operator: '>=', + cool_down_secs: 300, + adjustment: '+4' + }, + { + metric_type: 'responsetime', + breach_duration_secs: 600, + threshold: 40, + operator: '<', + cool_down_secs: 300, + adjustment: '-5' + }, + { + metric_type: 'memoryutil', + breach_duration_secs: 600, + threshold: 90, + operator: '>=', + cool_down_secs: 300, + adjustment: '+6' + } + ], + schedules: { + timezone: 'Asia/Shanghai', + recurring_schedule: [ + { + start_time: '10:00', + end_time: '18:00', + days_of_week: [ + 1, + 2, + 3 + ], + instance_min_count: 1, + instance_max_count: 10, + initial_min_instance_count: 5 + }, + { + start_date: '2099-06-27', + end_date: '2099-07-23', + start_time: '11:00', + end_time: '19:30', + days_of_month: [ + 5, + 15, + 25 + ], + instance_min_count: 3, + instance_max_count: 10 + } + ], + specific_date: [ + { + start_date_time: '2099-06-02T10:00', + end_date_time: '2099-06-15T13:59', + instance_min_count: 1, + instance_max_count: 4, + initial_min_instance_count: 2 + }, + { + start_date_time: '2099-01-04T20:00', + end_date_time: '2099-02-19T23:15', + instance_min_count: 2, + instance_max_count: 5 + } + ] + } + }; + const { ...mapPolicy }: AppAutoscalerPolicyLocal = { + ...arrayPolicy, + enabled: true, + scaling_rules_map: { + memoryused: { + lower: [ + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 30, + operator: '<', + cool_down_secs: 300, + adjustment: '-1', + color: 'rgba(51, 204, 255, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 10, + operator: '<=', + cool_down_secs: 300, + adjustment: '-2', + color: 'rgba(51, 136, 255, 0.6)' + } + ], + upper: [ + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 200, + operator: '>=', + cool_down_secs: 300, + adjustment: '+4', + color: 'rgba(255, 0, 0, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 120, + operator: '>', + cool_down_secs: 300, + adjustment: '+3', + color: 'rgba(255, 85, 0, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 90, + operator: '>=', + cool_down_secs: 300, + adjustment: '+2', + color: 'rgba(255, 170, 0, 0.6)' + } + ] + }, + memoryutil: { + lower: [ + { + metric_type: 'memoryutil', + breach_duration_secs: 600, + threshold: 20, + operator: '<', + cool_down_secs: 300, + adjustment: '-3', + color: 'rgba(51, 204, 255, 0.6)' + } + ], + upper: [ + { + metric_type: 'memoryutil', + breach_duration_secs: 600, + threshold: 90, + operator: '>=', + cool_down_secs: 300, + adjustment: '+6', + color: 'rgba(255, 0, 0, 0.6)' + } + ] + }, + responsetime: { + upper: [ + { + metric_type: 'responsetime', + breach_duration_secs: 600, + threshold: 50, + operator: '>=', + cool_down_secs: 300, + adjustment: '+4', + color: 'rgba(255, 0, 0, 0.6)' + } + ], + lower: [ + { + metric_type: 'responsetime', + breach_duration_secs: 600, + threshold: 40, + operator: '<', + cool_down_secs: 300, + adjustment: '-5', + color: 'rgba(51, 204, 255, 0.6)' + } + ] + } + }, + scaling_rules_form: [ + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 200, + operator: '>=', + cool_down_secs: 300, + adjustment: '+4', + color: 'rgba(255, 0, 0, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 120, + operator: '>', + cool_down_secs: 300, + adjustment: '+3', + color: 'rgba(255, 85, 0, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 90, + operator: '>=', + cool_down_secs: 300, + adjustment: '+2', + color: 'rgba(255, 170, 0, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 30, + operator: '<', + cool_down_secs: 300, + adjustment: '-1', + color: 'rgba(51, 204, 255, 0.6)' + }, + { + metric_type: 'memoryused', + breach_duration_secs: 600, + threshold: 10, + operator: '<=', + cool_down_secs: 300, + adjustment: '-2', + color: 'rgba(51, 136, 255, 0.6)' + }, + { + metric_type: 'memoryutil', + breach_duration_secs: 600, + threshold: 90, + operator: '>=', + cool_down_secs: 300, + adjustment: '+6', + color: 'rgba(255, 0, 0, 0.6)' + }, + { + metric_type: 'memoryutil', + breach_duration_secs: 600, + threshold: 20, + operator: '<', + cool_down_secs: 300, + adjustment: '-3', + color: 'rgba(51, 204, 255, 0.6)' + }, + { + metric_type: 'responsetime', + breach_duration_secs: 600, + threshold: 50, + operator: '>=', + cool_down_secs: 300, + adjustment: '+4', + color: 'rgba(255, 0, 0, 0.6)' + }, + { + metric_type: 'responsetime', + breach_duration_secs: 600, + threshold: 40, + operator: '<', + cool_down_secs: 300, + adjustment: '-5', + color: 'rgba(51, 204, 255, 0.6)' + } + ] + }; + const mapPolicyFromArray = autoscalerTransformArrayToMap(arrayPolicy); + const arrayPolicyFromMap = autoscalerTransformMapToArray(mapPolicy); + expect(isEqual(mapPolicyFromArray, mapPolicy)).toBe(true); + expect(isEqual(arrayPolicyFromMap, autoscalerTransformMapToArray(autoscalerTransformArrayToMap(arrayPolicy)))).toBe(true); + delete arrayPolicy.scaling_rules; + mapPolicy.scaling_rules_form = []; + expect(isEqual(arrayPolicy, autoscalerTransformMapToArray(mapPolicy))).toBe(true); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-policy.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-policy.ts new file mode 100644 index 0000000000..ef37ce3d6d --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-transform-policy.ts @@ -0,0 +1,152 @@ +import * as moment from 'moment-timezone'; + +import { + AppAutoscalerPolicy, + AppAutoscalerPolicyLocal, + AppScalingRule, + AppScalingTrigger, +} from '../../store/app-autoscaler.types'; +import { AutoscalerConstants, getScaleType, isEqual } from './autoscaler-util'; + +export function autoscalerTransformArrayToMap(policy: AppAutoscalerPolicy) { + const newPolicy: AppAutoscalerPolicyLocal = { + ...policy, + enabled: true, + scaling_rules_map: {}, + scaling_rules_form: [] + }; + newPolicy.scaling_rules = newPolicy.scaling_rules || []; + newPolicy.scaling_rules.map((trigger) => { + pushAndSortTrigger(newPolicy.scaling_rules_map, trigger.metric_type, trigger); + }); + let maxThreshold = 0; + Object.keys(newPolicy.scaling_rules_map).map((metricName) => { + if (newPolicy.scaling_rules_map[metricName].upper && newPolicy.scaling_rules_map[metricName].upper.length > 0) { + maxThreshold = newPolicy.scaling_rules_map[metricName].upper[0].threshold; + setUpperColor(newPolicy.scaling_rules_map[metricName].upper); + } + if (newPolicy.scaling_rules_map[metricName].lower && newPolicy.scaling_rules_map[metricName].lower.length > 0) { + maxThreshold = Math.max(newPolicy.scaling_rules_map[metricName].lower[0].threshold, maxThreshold); + setLowerColor(newPolicy.scaling_rules_map[metricName].lower); + } + buildFormUponMap(newPolicy, metricName); + }); + newPolicy.schedules = newPolicy.schedules || { timezone: moment.tz.guess() }; + newPolicy.schedules.recurring_schedule = newPolicy.schedules.recurring_schedule || []; + newPolicy.schedules.specific_date = newPolicy.schedules.specific_date || []; + return newPolicy; +} + +function setUpperColor(array: AppScalingRule[]) { + // from FF0000 to FFFF00 + // from rgba(255,0,0,0.6) to rgba(255,255,0,0.6) + const max = 255; // parseInt('FF', 16) + const min = 0; // parseInt('00', 16) + const scope = max - min; + if (array && array.length > 0) { + const interval = Math.round(scope / array.length); + for (let i = 0; i < array.length; i++) { + let color10 = 0 + i * interval; + if (color10 > max) { + color10 = max; + } + // let color16 = color10.toString(16) + // if (color16.length === 1) color16 = '0' + color16 + array[i].color = 'rgba(255, ' + color10 + ', 0, 0.6)'; // '#ff' + color16 + '00' + } + } +} + +function setLowerColor(array: AppScalingRule[]) { + // from 3344ff to 33ccff + // from rgba(51,68,255,0.6) to rgba(51,204,255,0.6) + const max = 204; // parseInt('CC', 16) + const min = 68; // parseInt('44', 16) + const scope = max - min; + if (array && array.length > 0) { + const interval = Math.round(scope / array.length); + for (let i = 0; i < array.length; i++) { + let color10 = max - i * interval; + if (color10 < min) { + color10 = min; + } + // let color16 = color10.toString(16) + // if (color16.length === 1) color16 = '0' + color16 + array[i].color = 'rgba(51, ' + color10 + ', 255, 0.6)'; // '#33' + color16 + 'ff' + } + } +} + +export function autoscalerTransformMapToArray(oldPolicy: AppAutoscalerPolicyLocal) { + const newPolicy: AppAutoscalerPolicy = { + instance_min_count: oldPolicy.instance_min_count, + instance_max_count: oldPolicy.instance_max_count + }; + const scalingRules: AppScalingRule[] = oldPolicy.scaling_rules_form.map((trigger) => { + return { + adjustment: trigger.adjustment, + breach_duration_secs: trigger.breach_duration_secs, + cool_down_secs: trigger.breach_duration_secs, + metric_type: trigger.metric_type, + operator: trigger.operator, + threshold: trigger.threshold + }; + }); + if (scalingRules.length > 0) { + newPolicy.scaling_rules = scalingRules; + } + if (oldPolicy.schedules && + (hasNamedSchedule(oldPolicy.schedules.recurring_schedule) || hasNamedSchedule(oldPolicy.schedules.specific_date))) { + newPolicy.schedules = { + timezone: oldPolicy.schedules.timezone + }; + if (hasNamedSchedule(oldPolicy.schedules.recurring_schedule)) { + newPolicy.schedules.recurring_schedule = oldPolicy.schedules.recurring_schedule; + } + if (hasNamedSchedule(oldPolicy.schedules.specific_date)) { + newPolicy.schedules.specific_date = oldPolicy.schedules.specific_date; + } + } + return newPolicy; +} + +function hasNamedSchedule(schedule: any) { + return schedule !== undefined && schedule !== null && schedule.length > 0; +} + +function pushAndSortTrigger(map: { [metricName: string]: AppScalingTrigger }, metricName: string, newTrigger: AppScalingRule) { + const scaleType = getScaleType(newTrigger.operator); + newTrigger.breach_duration_secs = + newTrigger.breach_duration_secs || AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_default; + newTrigger.cool_down_secs = newTrigger.cool_down_secs || AutoscalerConstants.PolicyDefaultSetting.cool_down_secs_default; + if (!map[metricName]) { + map[metricName] = { + upper: [], + lower: [] + }; + } + if (!map[metricName][scaleType]) { + map[metricName][scaleType] = []; + } + for (let i = 0; i < map[metricName][scaleType].length; i++) { + if (newTrigger.threshold > map[metricName][scaleType][i].threshold) { + map[metricName][scaleType].splice(i, 0, newTrigger); + return; + } + } + map[metricName][scaleType].push(newTrigger); +} + +function buildFormUponMap(newPolicy: AppAutoscalerPolicyLocal, metricName: string) { + AutoscalerConstants.ScaleTypes.forEach((triggerType) => { + if (newPolicy.scaling_rules_map[metricName][triggerType]) { + newPolicy.scaling_rules_map[metricName][triggerType].forEach((trigger: AppScalingRule) => { + newPolicy.scaling_rules_form.push(trigger); + }); + } + }); +} + +export function isPolicyMapEqual(a: AppAutoscalerPolicyLocal, b: AppAutoscalerPolicyLocal) { + return isEqual(autoscalerTransformMapToArray(a), autoscalerTransformMapToArray(b)); +} diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-util.spec.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-util.spec.ts new file mode 100644 index 0000000000..9d62652ac1 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-util.spec.ts @@ -0,0 +1,73 @@ +import { + isEqual, buildLegendData, shiftArray +} from './autoscaler-util'; + +describe('Autoscaler Util Helper', () => { + it('buildLegendData', () => { + const trigger = { + lower: [ + { + adjustment: '-1', + breach_duration_secs: 60, + cool_down_secs: 60, + metric_type: 'throughput', + operator: '<=', + threshold: 5, + color: 'rgba(51, 204, 255, 0.6)' + } + ], + upper: [ + { + adjustment: '+2', + breach_duration_secs: 60, + cool_down_secs: 60, + metric_type: 'throughput', + operator: '>', + threshold: 20, + color: 'rgba(255, 0, 0, 0.6)' + }, + { + adjustment: '+1', + breach_duration_secs: 60, + cool_down_secs: 60, + metric_type: 'throughput', + operator: '>', + threshold: 10, + color: 'rgba(255, 128, 0, 0.6)' + } + ], + query: { + metric: 'policy', + params: { + start: 1557026400, + end: 1557026880, + step: 9.6 + } + } + }; + const expectedLegend = [ + { + name: 'throughput > 20', + value: 'rgba(255, 0, 0, 0.6)' + }, + { + name: '10 < throughput <= 20', + value: 'rgba(255, 128, 0, 0.6)' + }, + { + name: '5 < throughput <= 10', + value: 'rgba(90,167,0,0.6)' + }, + { + name: 'throughput <= 5', + value: 'rgba(51, 204, 255, 0.6)' + } + ]; + const legend = buildLegendData(trigger); + expect(isEqual(expectedLegend, legend)).toBe(true); + }); + it('shiftArray', () => { + expect(shiftArray([0, 2, 3], 1)).toEqual([1, 3, 4]); + expect(shiftArray([1, 6, 9], -1)).toEqual([0, 5, 8]); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-util.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-util.ts new file mode 100644 index 0000000000..46fd4b6d21 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-util.ts @@ -0,0 +1,269 @@ +import * as moment from 'moment-timezone'; + +import { + AppAutoscalerMetricDataPoint, + AppAutoscalerMetricLegend, + AppAutoscalerMetricMapInfo, + AppScalingRule, + AppScalingTrigger, +} from '../../store/app-autoscaler.types'; + + +export class AutoscalerConstants { + public static S2NS = 1000000000; + public static MetricTypes = ['memoryused', 'memoryutil', 'responsetime', 'throughput', 'cpu']; + public static MetricPercentageTypes = ['memoryutil']; + public static ScaleTypes = ['upper', 'lower']; + public static UpperOperators = ['>', '>=']; + public static LowerOperators = ['<', '<=']; + public static WeekdayOptions = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + public static MonthdayOptions = (() => { + const days = []; + for (let i = 0; i < 31; i++) { + days[i] = i + 1; + } + return days; + })(); + + public static normalColor = 'rgba(90,167,0,0.6)'; + public static MomentFormateDate = 'YYYY-MM-DD'; + public static MomentFormateDateTimeT = 'YYYY-MM-DDTHH:mm'; + public static MomentFormateTime = 'HH:mm'; + public static MomentFormateTimeS = 'HH:mm:ss'; + + public static PolicyDefaultSetting = { + breach_duration_secs_default: 120, + breach_duration_secs_min: 60, + breach_duration_secs_max: 3600, + cool_down_secs_default: 300, + cool_down_secs_min: 60, + cool_down_secs_max: 3600, + }; + public static PolicyDefaultTrigger = { + metric_type: 'memoryused', + breach_duration_secs: AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_default, + threshold: 10, + operator: '<=', + cool_down_secs: AutoscalerConstants.PolicyDefaultSetting.cool_down_secs_default, + adjustment: '-1' + }; + public static PolicyDefaultRecurringSchedule = { + start_time: '10:00', + end_time: '18:00', + days_of_week: [ + 1, 2, 3 + ], + instance_min_count: 1, + instance_max_count: 10, + initial_min_instance_count: 5 + }; + public static PolicyDefaultSpecificDate = { + start_date_time: moment().add(1, 'days').set('hour', 10).set('minute', 0).format(AutoscalerConstants.MomentFormateDateTimeT), + end_date_time: moment().add(1, 'days').set('hour', 18).set('minute', 0).format(AutoscalerConstants.MomentFormateDateTimeT), + instance_min_count: 1, + instance_max_count: 10, + initial_min_instance_count: 5 + }; + + public static metricMap: { [metricName: string]: AppAutoscalerMetricMapInfo } = { + memoryused: { + unit_internal: 'MB', + interval: 40, + }, + memoryutil: { + unit_internal: ' % ', + interval: 40, + }, + responsetime: { + unit_internal: 'ms', + interval: 40, + }, + throughput: { + unit_internal: 'rps', + interval: 40, + }, + cpu: { + unit_internal: ' % ', + interval: 40, + } + }; + + public static getMetricUnit(metricType: string) { + if (AutoscalerConstants.metricMap[metricType]) { + return AutoscalerConstants.metricMap[metricType].unit_internal; + } else { + return ''; + } + } + + public static createMetricId(appGuid: string, metricType: string): string { + return appGuid + ':' + metricType; + } + + public static getMetricFromMetricId(metricId: string): string { + return metricId.slice(metricId.indexOf(':') + 1, metricId.length); + } +} + +export const PolicyAlert = { + alertInvalidPolicyMinimumRange: 'The Minimum Instance Count must be a integer less than the Maximum Instance Count.', + alertInvalidPolicyMaximumRange: 'The Maximum Instance Count must be a integer greater than the Minimum Instance Count.', + alertInvalidPolicyInitialMaximumRange: + 'The Initial Minimum Instance Count must be a integer in the range of Minimum Instance Count to Maximum Instance Count.', + alertInvalidPolicyTriggerUpperThresholdRange: 'The Upper Threshold value must be an integer greater than the Lower Threshold value.', + alertInvalidPolicyTriggerLowerThresholdRange: 'The Lower Threshold value must be an integer in the range of 1 to (Upper Threshold-1).', + alertInvalidPolicyTriggerThreshold100: 'The Lower/Upper Threshold value of memoryutil must be an integer below or equal to 100.', + alertInvalidPolicyTriggerStepPercentageRange: 'The Instance Step Up/Down percentage must be a integer greater than 1.', + alertInvalidPolicyTriggerStepRange: 'The Instance Step Up/Down value must be a integer in the range of 1 to (Maximum Instance-1).', + alertInvalidPolicyTriggerBreachDurationRange: + `The breach duration value must be an integer in the range of ${AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_min} to + ${AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_max} seconds.`, + alertInvalidPolicyTriggerCooldownRange: + `The cooldown period value must be an integer in the range of ${AutoscalerConstants.PolicyDefaultSetting.cool_down_secs_min} to + ${AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_max} seconds.`, + alertInvalidPolicyScheduleDateBeforeNow: 'Start/End date should be after or equal to current date.', + alertInvalidPolicyScheduleEndDateBeforeStartDate: 'Start date must be earlier than the end date.', + alertInvalidPolicyScheduleEndTimeBeforeStartTime: 'Start time must be earlier than the end time.', + alertInvalidPolicyScheduleRepeatOn: 'Please select at least one "Repeat On" day.', + alertInvalidPolicyScheduleEndDateTimeBeforeStartDateTime: 'Start date and time must be earlier than the end date and time.', + alertInvalidPolicyScheduleStartDateTimeBeforeNow: 'Start date and time must be after or equal to current date time.', + alertInvalidPolicyScheduleEndDateTimeBeforeNow: 'End date and time must be after or equal the current date and time.', + alertInvalidPolicyScheduleRecurringConflict: 'Recurring schedule configuration conflict occurs.', + alertInvalidPolicyScheduleSpecificConflict: 'Specific date configuration conflict occurs.', + alertInvalidPolicyTriggerScheduleEmpty: 'At least one Scaling Rule or Schedule should be defined.', +}; + +export function isEqual(a: any, b: any): boolean { + if (typeof a !== typeof b) { + return false; + } else { + if (typeof a === 'object') { + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + let equal = true; + Object.keys(a).map((key) => { + equal = equal && isEqual(a[key], b[key]); + }); + return equal; + } else { + return JSON.stringify(a) === JSON.stringify(b); + } + } +} + +export function getScaleType(operator: string): string { + if (AutoscalerConstants.LowerOperators.indexOf(operator) >= 0) { + return 'lower'; + } else { + return 'upper'; + } +} + +export function getAdjustmentType(adjustment: string): string { + return adjustment.indexOf('%') >= 0 ? 'percentage' : 'value'; +} + +export function buildLegendData(trigger: AppScalingTrigger): AppAutoscalerMetricLegend[] { + const legendData: AppAutoscalerMetricLegend[] = []; + let latestUl: AppScalingRule = null; + if (trigger.upper && trigger.upper.length > 0) { + const noLowerRule = !trigger.lower || trigger.lower.length === 0; + latestUl = buildUpperLegendData(legendData, trigger.upper, noLowerRule); + } + if (trigger.lower && trigger.lower.length > 0) { + latestUl = buildLowerLegendData(legendData, trigger.lower, latestUl); + } + return legendData; +} + +function getLegendName(currentRule: AppScalingRule, latestRule: AppScalingRule, singleRange: boolean, isLowerRule: boolean) { + if (singleRange) { + const operator = isLowerRule ? getOppositeOperator(currentRule.operator) : currentRule.operator; + return `${currentRule.metric_type} ${operator} ${currentRule.threshold}`; + } else { + return `${currentRule.threshold} ${getLeftOperator(currentRule.operator)} ${currentRule. + metric_type} ${getRightOperator(latestRule.operator)} ${latestRule.threshold}`; + } +} + +function buildUpperLegendData(legendData: any, upper: AppScalingRule[], noLower: boolean): AppScalingRule { + let latestUl: AppScalingRule; + upper.forEach((item, index) => { + const name = getLegendName(item, latestUl, index === 0, false); + legendData.push({ + name, + value: item.color + }); + latestUl = item; + }); + if (noLower) { + legendData.push({ + name: `${upper[0].metric_type} ${getOppositeOperator(latestUl.operator)} ${latestUl.threshold}`, + value: AutoscalerConstants.normalColor + }); + } + return latestUl; +} + +function buildLowerLegendData( + legendData: AppAutoscalerMetricDataPoint[], + lower: AppScalingRule[], + latestUl: AppScalingRule +): AppScalingRule { + lower.forEach((item, index) => { + const isSingleRange = !latestUl || !latestUl.threshold; + const name = getLegendName(item, latestUl, isSingleRange, true); + legendData.push({ + name, + value: index === 0 ? AutoscalerConstants.normalColor : latestUl.color + }); + latestUl = item; + }); + legendData.push({ + name: `${lower[0].metric_type} ${latestUl.operator} ${latestUl.threshold}`, + value: latestUl.color + }); + return latestUl; +} + +function getOppositeOperator(operator: string): string { + switch (operator) { + case '>': + return '<='; + case '>=': + return '<'; + case '<': + return '>='; + default: + return '>'; + } +} + +function getRightOperator(operator: string): string { + switch (operator) { + case '>': + return '<='; + case '>=': + return '<'; + default: + return operator; + } +} + +function getLeftOperator(operator: string): string { + switch (operator) { + case '>': + return '<'; + case '>=': + return '<='; + case '<': + return '<='; + default: + return '<'; + } +} + +export function shiftArray(array: number[], step: number): number[] { + return array.map(value => value + step); +} diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-validation.spec.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-validation.spec.ts new file mode 100644 index 0000000000..90c63b9e4e --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-validation.spec.ts @@ -0,0 +1,115 @@ +import { AppRecurringSchedule, AppSpecificDate } from '../../store/app-autoscaler.types'; +import { + dateIsAfter, + dateTimeIsSameOrAfter, + numberWithFractionOrExceedRange, + recurringSchedulesInvalidRepeatOn, + recurringSchedulesOverlapping, + specificDateRangeOverlapping, + timeIsSameOrAfter, +} from './autoscaler-validation'; + +describe('Autoscaler Util Helper', () => { + it('numberWithFractionOrExceedRange', () => { + expect(numberWithFractionOrExceedRange(undefined, 1, 10, false)).toBe(false); + expect(numberWithFractionOrExceedRange(undefined, 1, 10, true)).toBe(true); + expect(numberWithFractionOrExceedRange(5, 1, 10, true)).toBe(false); + expect(numberWithFractionOrExceedRange(1, 5, 10, true)).toBe(true); + }); + it('timeIsSameOrAfter', () => { + expect(timeIsSameOrAfter('10:00', '12:00')).toBe(false); + expect(timeIsSameOrAfter('10:00', '10:00')).toBe(true); + expect(timeIsSameOrAfter('10:00', '08:00')).toBe(true); + }); + it('dateIsAfter', () => { + expect(dateIsAfter('2020-01-01', '2020-01-02')).toBe(false); + expect(dateIsAfter('2020-01-01', '2020-01-01')).toBe(false); + expect(dateIsAfter('2020-01-01', '2019-12-31')).toBe(true); + }); + it('dateTimeIsSameOrAfter', () => { + expect(dateTimeIsSameOrAfter('2020-01-01 10:00', '2020-01-01 12:00')).toBe(false); + expect(dateTimeIsSameOrAfter('2020-01-01 10:00', '2020-01-01 10:00')).toBe(true); + expect(dateTimeIsSameOrAfter('2020-01-01 10:00', '2020-01-01 08:00')).toBe(true); + }); + it('recurringSchedulesInvalidRepeatOn', () => { + const recurring1: AppRecurringSchedule = { + days_of_month: [1, 2], + days_of_week: [3, 4], + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }; + const recurring2: AppRecurringSchedule = { + days_of_week: [3, 4], + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }; + const recurring3: AppRecurringSchedule = { + days_of_month: [1, 2], + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }; + const recurring4: AppRecurringSchedule = { + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }; + expect(recurringSchedulesInvalidRepeatOn(recurring1)).toBe(true); + expect(recurringSchedulesInvalidRepeatOn(recurring2)).toBe(false); + expect(recurringSchedulesInvalidRepeatOn(recurring3)).toBe(false); + expect(recurringSchedulesInvalidRepeatOn(recurring4)).toBe(true); + }); + it('recurringSchedulesOverlapping', () => { + const recurring1: AppRecurringSchedule = { + days_of_week: [1, 2], + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }; + const recurring2: AppRecurringSchedule = { + days_of_month: [3, 4], + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }; + const recurrings: AppRecurringSchedule[] = [{ + days_of_month: [4, 5], + instance_min_count: 1, + instance_max_count: 2, + start_time: '10:00', + end_time: '18:00' + }, recurring1, recurring2]; + expect(recurringSchedulesOverlapping(recurring1, 1, recurrings, 'days_of_week')).toBe(false); + expect(recurringSchedulesOverlapping(recurring2, 2, recurrings, 'days_of_month')).toBe(true); + }); + it('specificDateRangeOverlapping', () => { + const specific: AppSpecificDate = { + instance_min_count: 1, + instance_max_count: 2, + start_date_time: '2020-01-01T10:00', + end_date_time: '2020-01-01T20:00' + }; + const specifics1: AppSpecificDate[] = [{ + instance_min_count: 1, + instance_max_count: 2, + start_date_time: '2020-01-01T08:00', + end_date_time: '2020-01-01T18:00' + }, specific]; + const specifics2: AppSpecificDate[] = [{ + instance_min_count: 1, + instance_max_count: 2, + start_date_time: '2020-02-01T08:00', + end_date_time: '2020-02-01T18:00' + }, specific]; + expect(specificDateRangeOverlapping(specific, 1, specifics1)).toBe(true); + expect(specificDateRangeOverlapping(specific, 1, specifics2)).toBe(false); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-validation.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-validation.ts new file mode 100644 index 0000000000..2f9f60de3f --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler-helpers/autoscaler-validation.ts @@ -0,0 +1,135 @@ +import * as intersect from 'intersect'; +import * as moment from 'moment-timezone'; + +import { AppRecurringSchedule, AppScalingRule, AppSpecificDate } from '../../store/app-autoscaler.types'; +import { AutoscalerConstants } from './autoscaler-util'; + +export function numberWithFractionOrExceedRange(value: any, min: number, max: number, required: boolean) { + if ((!value || isNaN(value)) && !required) { + return false; + } + if ((!value || isNaN(value)) && required) { + return true; + } + return value.toString().indexOf('.') > -1 || value > max || value < min; +} + +export function timeIsSameOrAfter(startTime: string, endTime: string) { + const startTimeMoment = moment('2000-01-01T' + startTime, AutoscalerConstants.MomentFormateDateTimeT); + const endTimeMoment = moment('2000-01-01T' + endTime, AutoscalerConstants.MomentFormateDateTimeT); + return startTimeMoment.isSameOrAfter(endTimeMoment); +} + +export function dateIsAfter(startDate: string, endDate: string) { + return moment(startDate, AutoscalerConstants.MomentFormateDate).isAfter(moment(endDate, AutoscalerConstants.MomentFormateDate)); +} + +export function dateTimeIsSameOrAfter(startDateTime: string, endDateTime: string) { + return moment(startDateTime).isSameOrAfter(moment(endDateTime)); +} + +export function recurringSchedulesInvalidRepeatOn(inputRecurringSchedules: AppRecurringSchedule) { + const weekdayCount = inputRecurringSchedules.hasOwnProperty('days_of_week') ? inputRecurringSchedules.days_of_week.length : 0; + const monthdayCount = inputRecurringSchedules.hasOwnProperty('days_of_month') ? inputRecurringSchedules.days_of_month.length : 0; + return (weekdayCount > 0 && monthdayCount > 0) || (weekdayCount === 0 && monthdayCount === 0); +} + +export function recurringSchedulesOverlapping( + newSchedule: AppRecurringSchedule, index: number, + inputRecurringSchedules: AppRecurringSchedule[], property: string) { + if (!inputRecurringSchedules) { + return false; + } + const overlappingSchedule = inputRecurringSchedules.find((value, i) => { + if (index === i || !inputRecurringSchedules[i].hasOwnProperty(property) || + inputRecurringSchedules[i].start_date && newSchedule.start_date && !dateOverlaps(inputRecurringSchedules[i], newSchedule)) { + return false; + } + if (timeOverlaps(inputRecurringSchedules[i], newSchedule)) { + const intersects = intersect(inputRecurringSchedules[i][property], newSchedule[property]); + return intersects.length > 0; + } + }); + return !!overlappingSchedule; +} + +export function specificDateRangeOverlapping(newSchedule: AppSpecificDate, index: number, inputSpecificDates: AppSpecificDate[]) { + const start = moment(newSchedule.start_date_time, AutoscalerConstants.MomentFormateDateTimeT); + const end = moment(newSchedule.end_date_time, AutoscalerConstants.MomentFormateDateTimeT); + if (inputSpecificDates) { + const dateRangeList = inputSpecificDates.map((value, i) => { + if (i !== index) { + const starti = moment(value.start_date_time, AutoscalerConstants.MomentFormateDateTimeT); + const endi = moment(value.end_date_time, AutoscalerConstants.MomentFormateDateTimeT); + return { + start: starti, + end: endi + }; + } + }); + const overlappingSchedule = dateRangeList.find((item) => { + if (item && dateTimeOverlaps(start, end, item.start, item.end)) { + return true; + } + }); + return !!overlappingSchedule; + } else { + return false; + } +} + +function timeOverlaps(timeI: AppRecurringSchedule, tiemJ: AppRecurringSchedule) { + const startDateTimeI = moment('1970-01-01T' + timeI.start_time, AutoscalerConstants.MomentFormateDateTimeT); + const endDateTimeI = moment('1970-01-01T' + timeI.end_time, AutoscalerConstants.MomentFormateDateTimeT); + const startDateTimeJ = moment('1970-01-01T' + tiemJ.start_time, AutoscalerConstants.MomentFormateDateTimeT); + const endDateTimeJ = moment('1970-01-01T' + tiemJ.end_time, AutoscalerConstants.MomentFormateDateTimeT); + return dateTimeOverlaps(startDateTimeI, endDateTimeI, startDateTimeJ, endDateTimeJ); +} + +function dateOverlaps(dateI: AppRecurringSchedule, dateJ: AppRecurringSchedule) { + const startDateTimeI = moment(dateI.start_date + 'T00:00', AutoscalerConstants.MomentFormateDateTimeT); + const endDateTimeI = moment(dateI.end_date + 'T23:59', AutoscalerConstants.MomentFormateDateTimeT); + const startDateTimeJ = moment(dateJ.start_date + 'T00:00', AutoscalerConstants.MomentFormateDateTimeT); + const endDateTimeJ = moment(dateJ.end_date + 'T23:59', AutoscalerConstants.MomentFormateDateTimeT); + return dateTimeOverlaps(startDateTimeI, endDateTimeI, startDateTimeJ, endDateTimeJ); +} + +function dateTimeOverlaps( + startDateTimeI: moment.Moment, endDateTimeI: moment.Moment, + startDateTimeJ: moment.Moment, endDateTimeJ: moment.Moment) { + if (startDateTimeJ.isAfter(startDateTimeI)) { + return endDateTimeI.isAfter(startDateTimeJ); + } else { + return endDateTimeJ.isAfter(startDateTimeI); + } +} + +export function getThresholdMin(policyTriggers: AppScalingRule[], metricType: string, scaleType: string, index: number) { + if (scaleType === 'upper') { + return policyTriggers.reduce((thresholdMin, trigger, triggerIndex) => { + if (triggerIndex !== index && trigger.metric_type === metricType && + AutoscalerConstants.LowerOperators.indexOf(trigger.operator) >= 0) { + return Math.max(trigger.threshold + 1, thresholdMin); + } else { + return thresholdMin; + } + }, 1); + } else { + return 1; + } +} + +export function getThresholdMax(policyTriggers: AppScalingRule[], metricType: string, scaleType: string, index: number) { + if (scaleType === 'lower') { + return policyTriggers.reduce((thresholdMax, trigger, triggerIndex) => { + if (triggerIndex !== index && trigger.metric_type === metricType && + AutoscalerConstants.UpperOperators.indexOf(trigger.operator) >= 0) { + return Math.min(trigger.threshold - 1, thresholdMax); + } else { + return thresholdMax; + } + }, Number.MAX_VALUE); + } else { + return Number.MAX_VALUE; + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler.module.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler.module.ts new file mode 100644 index 0000000000..ff6272debd --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler.module.ts @@ -0,0 +1,96 @@ +import { NgModule } from '@angular/core'; +import { EffectsModule } from '@ngrx/effects'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; + +import { CoreModule } from '../../../core/src/core/core.module'; +import { ApplicationService } from '../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../core/src/shared/shared.module'; +import { AutoscalerBaseComponent } from '../features/autoscaler-base.component'; +import { AutoscalerMetricPageComponent } from '../features/autoscaler-metric-page/autoscaler-metric-page.component'; +import { + AutoscalerScaleHistoryPageComponent, +} from '../features/autoscaler-scale-history-page/autoscaler-scale-history-page.component'; +import { + EditAutoscalerPolicyStep1Component, +} from '../features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component'; +import { + EditAutoscalerPolicyStep2Component, +} from '../features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component'; +import { + EditAutoscalerPolicyStep3Component, +} from '../features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component'; +import { + EditAutoscalerPolicyStep4Component, +} from '../features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component'; +import { EditAutoscalerPolicyComponent } from '../features/edit-autoscaler-policy/edit-autoscaler-policy.component'; +import { CardAutoscalerDefaultComponent } from '../shared/card-autoscaler-default/card-autoscaler-default.component'; +import { + TableCellAutoscalerEventChangeIconPipe, +} from '../shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change-icon.pipe'; +import { + TableCellAutoscalerEventChangeComponent, +} from '../shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component'; +import { + TableCellAutoscalerEventStatusIconPipe, +} from '../shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status-icon.pipe'; +import { + TableCellAutoscalerEventStatusComponent, +} from '../shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component'; +import { + AppAutoscalerMetricChartCardComponent, +} from '../shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component'; +import { + AppAutoscalerComboChartComponent, +} from '../shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component'; +import { + AppAutoscalerComboSeriesVerticalComponent, +} from '../shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component'; +import { AutoscalerEffects } from '../store/autoscaler.effects'; +import { AutoscalerStoreModule } from '../store/autoscaler.store.module'; +import { AutoscalerRoutingModule } from './autoscaler.routing'; + + +@NgModule({ + imports: [ + CoreModule, + SharedModule, + AutoscalerRoutingModule, + AutoscalerStoreModule, + NgxChartsModule, + EffectsModule.forFeature([ + AutoscalerEffects + ]) + ], + declarations: [ + AutoscalerBaseComponent, + AutoscalerMetricPageComponent, + AutoscalerScaleHistoryPageComponent, + EditAutoscalerPolicyComponent, + EditAutoscalerPolicyStep1Component, + EditAutoscalerPolicyStep2Component, + EditAutoscalerPolicyStep3Component, + EditAutoscalerPolicyStep4Component, + CardAutoscalerDefaultComponent, + AppAutoscalerMetricChartCardComponent, + AppAutoscalerComboChartComponent, + AppAutoscalerComboSeriesVerticalComponent, + TableCellAutoscalerEventChangeComponent, + TableCellAutoscalerEventStatusComponent, + TableCellAutoscalerEventStatusIconPipe, + TableCellAutoscalerEventChangeIconPipe, + ], + exports: [ + CardAutoscalerDefaultComponent + ], + providers: [ + ApplicationService + ], + entryComponents: [ + AppAutoscalerMetricChartCardComponent, + AppAutoscalerComboChartComponent, + AppAutoscalerComboSeriesVerticalComponent, + TableCellAutoscalerEventChangeComponent, + TableCellAutoscalerEventStatusComponent + ] +}) +export class AutoscalerModule { } diff --git a/src/frontend/packages/cf-autoscaler/src/core/autoscaler.routing.ts b/src/frontend/packages/cf-autoscaler/src/core/autoscaler.routing.ts new file mode 100644 index 0000000000..f4a0685688 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/core/autoscaler.routing.ts @@ -0,0 +1,55 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { DynamicExtensionRoutes } from '../../../core/src/core/extension/dynamic-extension-routes'; +import { StratosActionType } from '../../../core/src/core/extension/extension-service'; +import { + PageNotFoundComponentComponent, +} from '../../../core/src/core/page-not-found-component/page-not-found-component.component'; +import { AutoscalerBaseComponent } from '../features/autoscaler-base.component'; +import { AutoscalerMetricPageComponent } from '../features/autoscaler-metric-page/autoscaler-metric-page.component'; +import { + AutoscalerScaleHistoryPageComponent, +} from '../features/autoscaler-scale-history-page/autoscaler-scale-history-page.component'; +import { EditAutoscalerPolicyComponent } from '../features/edit-autoscaler-policy/edit-autoscaler-policy.component'; + +const autoscalerRoutes: Routes = [ + { + path: '', + children: [ + { + path: ':endpointId/:id', + component: AutoscalerBaseComponent, + children: [ + { + path: 'edit-autoscaler-policy', + component: EditAutoscalerPolicyComponent, + }, + { + path: 'app-autoscaler-metric-page', + component: AutoscalerMetricPageComponent, + }, + { + path: 'app-autoscaler-scale-history-page', + component: AutoscalerScaleHistoryPageComponent, + }, + { + path: '**', + component: PageNotFoundComponentComponent, + canActivate: [DynamicExtensionRoutes], + data: { + stratosRouteGroup: StratosActionType.Application + } + } + ] + } + ] + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(autoscalerRoutes) + ] +}) +export class AutoscalerRoutingModule { } diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.html b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.html new file mode 100644 index 0000000000..0680b43f9c --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.html @@ -0,0 +1 @@ + diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.scss b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.scss new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.scss @@ -0,0 +1 @@ + diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.spec.ts new file mode 100644 index 0000000000..9f78819d24 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.spec.ts @@ -0,0 +1,48 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../core/src/core/core.module'; +import { ApplicationService } from '../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../cf-autoscaler-testing.module'; +import { AutoscalerBaseComponent } from './autoscaler-base.component'; + +describe('AutoscalerBaseComponent', () => { + let component: AutoscalerBaseComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AutoscalerBaseComponent], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AutoscalerBaseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.ts new file mode 100644 index 0000000000..3f81cc895e --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-base.component.ts @@ -0,0 +1,27 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { ApplicationService } from '../../../core/src/features/applications/application.service'; +import { getGuids } from '../../../core/src/features/applications/application/application-base.component'; +import { APP_GUID, CF_GUID } from '../../../core/src/shared/entity.tokens'; + +@Component({ + selector: 'app-autoscaler-base', + templateUrl: './autoscaler-base.component.html', + styleUrls: ['./autoscaler-base.component.scss'], + providers: [ + ApplicationService, + { + provide: CF_GUID, + useFactory: getGuids('cf'), + deps: [ActivatedRoute] + }, + { + provide: APP_GUID, + useFactory: getGuids(), + deps: [ActivatedRoute] + }, + ] +}) +export class AutoscalerBaseComponent { +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.html b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.html new file mode 100644 index 0000000000..690ac8e5c0 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.html @@ -0,0 +1,13 @@ + +

AutoScaler Metric Charts: {{ applicationName$ | async }}

+
+ +
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.scss b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.scss new file mode 100644 index 0000000000..208b05bac8 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.scss @@ -0,0 +1,5 @@ +app-list { + flex: 1; +} + + diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.spec.ts new file mode 100644 index 0000000000..0972987d63 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.spec.ts @@ -0,0 +1,48 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../cf-autoscaler-testing.module'; +import { AutoscalerMetricPageComponent } from './autoscaler-metric-page.component'; + +describe('AutoscalerMetricPageComponent', () => { + let component: AutoscalerMetricPageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AutoscalerMetricPageComponent], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AutoscalerMetricPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.ts new file mode 100644 index 0000000000..b5954384ac --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-metric-page/autoscaler-metric-page.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map, publishReplay, refCount } from 'rxjs/operators'; + +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { ListConfig } from '../../../../core/src/shared/components/list/list.component.types'; +import { + AppAutoscalerMetricChartListConfigService, +} from '../../shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service'; + +@Component({ + selector: 'app-autoscaler-metric-page', + templateUrl: './autoscaler-metric-page.component.html', + styleUrls: ['./autoscaler-metric-page.component.scss'], + providers: [ + { + provide: ListConfig, + useClass: AppAutoscalerMetricChartListConfigService + } + ] +}) +export class AutoscalerMetricPageComponent implements OnInit { + + parentUrl = `/applications/${this.applicationService.cfGuid}/${this.applicationService.appGuid}/autoscale`; + applicationName$: Observable; + + constructor( + public applicationService: ApplicationService, + ) { + } + + ngOnInit() { + this.applicationName$ = this.applicationService.app$.pipe( + map(({ entity }) => entity ? entity.entity.name : null), + publishReplay(1), + refCount() + ); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.html b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.html new file mode 100644 index 0000000000..453d846ab8 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.html @@ -0,0 +1,13 @@ + +

AutoScaler Scaling Events: {{ applicationName$ | async }}

+
+ +
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.scss b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.spec.ts new file mode 100644 index 0000000000..48028069df --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.spec.ts @@ -0,0 +1,48 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../cf-autoscaler-testing.module'; +import { AutoscalerScaleHistoryPageComponent } from './autoscaler-scale-history-page.component'; + +describe('AutoscalerScaleHistoryPageComponent', () => { + let component: AutoscalerScaleHistoryPageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AutoscalerScaleHistoryPageComponent], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AutoscalerScaleHistoryPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.ts new file mode 100644 index 0000000000..211b2377d6 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-scale-history-page/autoscaler-scale-history-page.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map, publishReplay, refCount } from 'rxjs/operators'; + +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { ListConfig } from '../../../../core/src/shared/components/list/list.component.types'; +import { + CfAppAutoscalerEventsConfigService, +} from '../../shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service'; + +@Component({ + selector: 'app-autoscaler-scale-history-page', + templateUrl: './autoscaler-scale-history-page.component.html', + styleUrls: ['./autoscaler-scale-history-page.component.scss'], + providers: [{ + provide: ListConfig, + useClass: CfAppAutoscalerEventsConfigService, + }] +}) +export class AutoscalerScaleHistoryPageComponent implements OnInit { + + parentUrl = `/applications/${this.applicationService.cfGuid}/${this.applicationService.appGuid}/autoscale`; + applicationName$: Observable; + + constructor( + public applicationService: ApplicationService, + ) { + } + + ngOnInit() { + this.applicationName$ = this.applicationService.app$.pipe( + map(({ entity }) => entity ? entity.entity.name : null), + publishReplay(1), + refCount() + ); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.html b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.html new file mode 100644 index 0000000000..1ea6e15f7a --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.html @@ -0,0 +1,263 @@ +
+ + + + + + Status + + + + + + + + + + + + + + + + Status + + + + + + + + + + + + + + + + + Latest Metrics + + + + + + + + + + + + +
+ + + + + Scaling Rules + + + + + + + + + + + + + + Scheduled Limit Rules + + in {{policy.schedules.timezone}} + + + + + +
+ +
+ + + +
+
+
+
+ + + + + Latest Events + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.scss b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.scss new file mode 100644 index 0000000000..2388c6fd13 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.scss @@ -0,0 +1,71 @@ +.autoscaler-tab { + display: flex; + position: relative; + width: 100%; + &__actions { + bottom: 10px; + padding-top: 0; + position: absolute; + right: 24px; + text-align: right; + } + .mat-header-cell:first-of-type { + padding-left: unset; + } + .autoscaler-tab-table-no-record { + margin-top: .5em; + } + + &__latest-metrics { + min-height: 200px; + } +} + +.app-metadata { + display: flex; + flex-direction: row; + &__two-cols { + flex: 1; + app-metadata-item:first-child { + margin-top: 0; + } + } + app-metadata-item { + margin-bottom: 0; + } +} + +.app-metadata-table { + display: block; + padding-bottom: 8px; +} + +.app-autoscaler-tile-grid-100 { + width: 100%; +} + +table { + width: 100%; + td { + padding-left: .5em; + } +} + +.autoscaler-tile-events { + &__header { + display: flex; + flex: 1; + height: 40px; + justify-content: space-between; + padding-top: 0; + + mat-card-title { + margin-bottom: 0; + } + + button { + margin-right: -10px; + margin-top: -10px; + } + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.spec.ts new file mode 100644 index 0000000000..be3c53d2be --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.spec.ts @@ -0,0 +1,54 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; + +import { CoreModule } from '../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../cf-autoscaler-testing.module'; +import { AutoscalerTabExtensionComponent } from './autoscaler-tab-extension.component'; +import { CardAutoscalerDefaultComponent } from '../../shared/card-autoscaler-default/card-autoscaler-default.component'; + +describe('AutoscalerTabExtensionComponent', () => { + let component: AutoscalerTabExtensionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AutoscalerTabExtensionComponent, + CardAutoscalerDefaultComponent, + ], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + NgxChartsModule, + RouterTestingModule, + CfAutoscalerTestingModule, + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AutoscalerTabExtensionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts new file mode 100644 index 0000000000..8772cec66b --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts @@ -0,0 +1,330 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material'; +import { ActivatedRoute } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { Observable, Subscription } from 'rxjs'; +import { distinctUntilChanged, filter, first, map, publishReplay, refCount, startWith } from 'rxjs/operators'; + +import { applicationEntityType } from '../../../../cloud-foundry/src/cf-entity-factory'; +import { EntityService } from '../../../../core/src/core/entity-service'; +import { EntityServiceFactory } from '../../../../core/src/core/entity-service-factory.service'; +import { StratosTab, StratosTabType } from '../../../../core/src/core/extension/extension-service'; +import { safeUnsubscribe } from '../../../../core/src/core/utils.service'; +import { ApplicationMonitorService } from '../../../../core/src/features/applications/application-monitor.service'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { getGuids } from '../../../../core/src/features/applications/application/application-base.component'; +import { ConfirmationDialogConfig } from '../../../../core/src/shared/components/confirmation-dialog.config'; +import { ConfirmationDialogService } from '../../../../core/src/shared/components/confirmation-dialog.service'; +import { PaginationMonitorFactory } from '../../../../core/src/shared/monitors/pagination-monitor.factory'; +import { RouterNav } from '../../../../store/src/actions/router.actions'; +import { AppState } from '../../../../store/src/app-state'; +import { createEntityRelationPaginationKey } from '../../../../store/src/helpers/entity-relations/entity-relations.types'; +import { ActionState } from '../../../../store/src/reducers/api-request-reducer/types'; +import { getPaginationObservables } from '../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; +import { selectUpdateInfo } from '../../../../store/src/selectors/api.selectors'; +import { APIResource } from '../../../../store/src/types/api.types'; +import { AutoscalerConstants } from '../../core/autoscaler-helpers/autoscaler-util'; +import { + AutoscalerPaginationParams, + DetachAppAutoscalerPolicyAction, + GetAppAutoscalerAppMetricAction, + GetAppAutoscalerPolicyAction, + GetAppAutoscalerScalingHistoryAction, + UpdateAppAutoscalerPolicyAction, +} from '../../store/app-autoscaler.actions'; +import { + AppAutoscalerFetchPolicyFailedResponse, + AppAutoscalerMetricData, + AppAutoscalerPolicy, + AppAutoscalerPolicyLocal, + AppAutoscalerScalingHistory, + AppScalingTrigger, +} from '../../store/app-autoscaler.types'; +import { + appAutoscalerAppMetricEntityType, + appAutoscalerPolicyEntityType, + autoscalerEntityFactory, +} from '../../store/autoscaler-entity-factory'; + +const enableAutoscaler = (appGuid: string, endpointGuid: string, esf: EntityServiceFactory): Observable => { + // This will eventual be moved out into a service and made generic to the cf (one call per cf, rather than one call per app - See #3583) + const action = new GetAppAutoscalerPolicyAction(appGuid, endpointGuid); + const entityService = esf.create(action.guid, action); + return entityService.entityObs$.pipe( + filter(entityInfo => + !!entityInfo && !!entityInfo.entityRequestInfo && !!entityInfo.entityRequestInfo.response && + !entityInfo.entityRequestInfo.fetching), + map(entityInfo => { + // Autoscaler feature should be enabled if either .. + // 1) There's an autoscaler policy + // 2) There's a 404 no policy error (as opposed to a 404 url not found error) + const noPolicySet = (entityInfo.entityRequestInfo.response as AppAutoscalerFetchPolicyFailedResponse).noPolicy; + return !!entityInfo.entity || noPolicySet; + }), + startWith(true) + ); +}; + +@StratosTab({ + type: StratosTabType.Application, + label: 'Autoscale', + link: 'autoscale', + icon: 'meter', + iconFont: 'stratos-icons', + hidden: (store: Store, esf: EntityServiceFactory, activatedRoute: ActivatedRoute) => { + const endpointGuid = getGuids('cf')(activatedRoute) || window.location.pathname.split('/')[2]; + const appGuid = getGuids()(activatedRoute) || window.location.pathname.split('/')[3]; + return enableAutoscaler(appGuid, endpointGuid, esf).pipe(map(enabled => !enabled)); + } +}) +@Component({ + selector: 'app-autoscaler-tab-extension', + templateUrl: './autoscaler-tab-extension.component.html', + styleUrls: ['./autoscaler-tab-extension.component.scss'], + providers: [ + ApplicationMonitorService + ] +}) +export class AutoscalerTabExtensionComponent implements OnInit, OnDestroy { + + scalingRuleColumns: string[] = ['metric', 'condition', 'action']; + specificDateColumns: string[] = ['from', 'to', 'init', 'min', 'max']; + recurringScheduleColumns: string[] = ['effect', 'repeat', 'from', 'to', 'init', 'min', 'max']; + scalingHistoryColumns: string[] = ['event', 'trigger', 'date', 'error']; + metricTypes: string[] = AutoscalerConstants.MetricTypes; + + appAutoscalerPolicyService: EntityService>; + public appAutoscalerScalingHistoryService: EntityService>; + appAutoscalerPolicy$: Observable; + appAutoscalerPolicySafe$: Observable; + appAutoscalerScalingHistory$: Observable; + appAutoscalerAppMetricNames$: Observable; + + private appAutoscalerPolicyErrorSub: Subscription; + private appAutoscalerScalingHistoryErrorSub: Subscription; + private appAutoscalerPolicySnackBarRef: MatSnackBarRef; + private appAutoscalerScalingHistorySnackBarRef: MatSnackBarRef; + private scalingHistoryAction: GetAppAutoscalerScalingHistoryAction; + + private detachConfirmOk = 0; + + appAutoscalerAppMetrics = {}; + + paramsMetrics: AutoscalerPaginationParams = { + 'start-time': ((new Date()).getTime() - 60000).toString() + '000000', + 'end-time': (new Date()).getTime().toString() + '000000', + page: '1', + 'results-per-page': '1', + 'order-direction': 'desc' + }; + paramsHistory: AutoscalerPaginationParams = { + 'start-time': '0', + 'end-time': (new Date()).getTime().toString() + '000000', + page: '1', + 'results-per-page': '5', + 'order-direction': 'desc' + }; + + ngOnDestroy(): void { + if (this.appAutoscalerPolicySnackBarRef) { + this.appAutoscalerPolicySnackBarRef.dismiss(); + } + if (this.appAutoscalerScalingHistorySnackBarRef) { + this.appAutoscalerScalingHistorySnackBarRef.dismiss(); + } + safeUnsubscribe(this.appAutoscalerPolicyErrorSub, this.appAutoscalerScalingHistoryErrorSub); + } + + constructor( + private store: Store, + private applicationService: ApplicationService, + private entityServiceFactory: EntityServiceFactory, + private paginationMonitorFactory: PaginationMonitorFactory, + private appAutoscalerPolicySnackBar: MatSnackBar, + private appAutoscalerScalingHistorySnackBar: MatSnackBar, + private confirmDialog: ConfirmationDialogService, + ) { } + + ngOnInit() { + this.appAutoscalerPolicyService = this.entityServiceFactory.create( + this.applicationService.appGuid, + new GetAppAutoscalerPolicyAction(this.applicationService.appGuid, this.applicationService.cfGuid), + false + ); + this.appAutoscalerPolicy$ = this.appAutoscalerPolicyService.entityObs$.pipe( + map(({ entity }) => entity ? entity.entity : null), + publishReplay(1), + refCount() + ); + this.appAutoscalerPolicySafe$ = this.appAutoscalerPolicyService.waitForEntity$.pipe( + map(({ entity }) => entity && entity.entity), + publishReplay(1), + refCount() + ); + + this.loadLatestMetricsUponPolicy(); + + this.appAutoscalerAppMetricNames$ = this.appAutoscalerPolicySafe$.pipe( + map(entity => Object.keys(entity.scaling_rules_map)), + ); + + this.scalingHistoryAction = new GetAppAutoscalerScalingHistoryAction( + createEntityRelationPaginationKey(applicationEntityType, this.applicationService.appGuid, 'latest'), + this.applicationService.appGuid, + this.applicationService.cfGuid, + true, + this.paramsHistory + ); + this.appAutoscalerScalingHistoryService = this.entityServiceFactory.create( + this.applicationService.appGuid, + this.scalingHistoryAction, + false + ); + this.appAutoscalerScalingHistory$ = this.appAutoscalerScalingHistoryService.entityObs$.pipe( + map(({ entity }) => entity && entity.entity), + publishReplay(1), + refCount() + ); + this.initErrorSub(); + } + + getAppMetric(metricName: string, trigger: AppScalingTrigger, params: AutoscalerPaginationParams) { + const action = new GetAppAutoscalerAppMetricAction(this.applicationService.appGuid, + this.applicationService.cfGuid, metricName, true, trigger, params); + this.store.dispatch(action); + return getPaginationObservables({ + store: this.store, + action, + paginationMonitor: this.paginationMonitorFactory.create( + action.paginationKey, + autoscalerEntityFactory(appAutoscalerAppMetricEntityType) + ) + }, false).entities$; + } + + loadLatestMetricsUponPolicy() { + this.appAutoscalerPolicySafe$.pipe( + first(), + ).subscribe(appAutoscalerPolicy => { + this.paramsMetrics['start-time'] = ((new Date()).getTime() - 60000).toString() + '000000'; + this.paramsMetrics['end-time'] = (new Date()).getTime().toString() + '000000'; + if (appAutoscalerPolicy.scaling_rules_map) { + this.appAutoscalerAppMetrics = Object.keys(appAutoscalerPolicy.scaling_rules_map).reduce((metricMap, metricName) => { + metricMap[metricName] = this.getAppMetric(metricName, appAutoscalerPolicy.scaling_rules_map[metricName], this.paramsMetrics); + return metricMap; + }, {}); + } + }); + } + + initErrorSub() { + if (this.appAutoscalerPolicyErrorSub) { + this.appAutoscalerScalingHistoryErrorSub.unsubscribe(); + } + + this.appAutoscalerPolicyErrorSub = this.appAutoscalerPolicyService.entityMonitor.entityRequest$.pipe( + filter(request => !!request.error), + map(request => { + const msg = request.message; + request.error = false; + request.message = ''; + return msg; + }), + distinctUntilChanged(), + ).subscribe(errorMessage => { + if (this.appAutoscalerPolicySnackBarRef) { + this.appAutoscalerPolicySnackBarRef.dismiss(); + } + this.appAutoscalerPolicySnackBarRef = this.appAutoscalerPolicySnackBar.open(errorMessage, 'Dismiss'); + }); + + if (this.appAutoscalerScalingHistoryErrorSub) { + this.appAutoscalerScalingHistoryErrorSub.unsubscribe(); + } + this.appAutoscalerScalingHistoryErrorSub = this.appAutoscalerScalingHistoryService.entityMonitor.entityRequest$.pipe( + filter(request => !!request.error), + map(request => request.message), + distinctUntilChanged(), + ).subscribe(errorMessage => { + if (this.appAutoscalerScalingHistorySnackBarRef) { + this.appAutoscalerScalingHistorySnackBarRef.dismiss(); + } + this.appAutoscalerScalingHistorySnackBarRef = this.appAutoscalerScalingHistorySnackBar.open(errorMessage, 'Dismiss'); + }); + } + + disableAutoscaler() { + const confirmation = new ConfirmationDialogConfig( + 'Detach And Delete Policy', + 'Are you sure you want to detach and delete the policy?', + 'Detach and Delete', + true + ); + this.detachConfirmOk = this.detachConfirmOk === 1 ? 0 : 1; + this.confirmDialog.open(confirmation, () => { + this.detachConfirmOk = 2; + const doUpdate = () => this.detachPolicy(); + doUpdate().pipe( + first(), + ).subscribe(actionState => { + if (actionState.error) { + this.appAutoscalerPolicySnackBarRef = + this.appAutoscalerPolicySnackBar.open(`Failed to detach policy: ${actionState.message}`, 'Dismiss'); + } + }); + }); + } + + detachPolicy(): Observable { + this.store.dispatch( + new DetachAppAutoscalerPolicyAction(this.applicationService.appGuid, this.applicationService.cfGuid) + ); + const actionState = selectUpdateInfo(appAutoscalerPolicyEntityType, + this.applicationService.appGuid, + UpdateAppAutoscalerPolicyAction.updateKey); + return this.store.select(actionState).pipe(filter(item => !!item)); + } + + updatePolicyPage = () => { + this.store.dispatch(new RouterNav({ + path: [ + 'autoscaler', + this.applicationService.cfGuid, + this.applicationService.appGuid, + 'edit-autoscaler-policy' + ] + })); + } + + metricChartPage() { + this.store.dispatch(new RouterNav({ + path: [ + 'autoscaler', + this.applicationService.cfGuid, + this.applicationService.appGuid, + 'app-autoscaler-metric-page' + ] + })); + } + + scaleHistoryPage() { + this.store.dispatch(new RouterNav({ + path: [ + 'autoscaler', + this.applicationService.cfGuid, + this.applicationService.appGuid, + 'app-autoscaler-scale-history-page' + ] + })); + } + + fetchScalingHistory() { + this.paramsHistory['end-time'] = (new Date()).getTime().toString() + '000000'; + this.store.dispatch(this.scalingHistoryAction); + } + + getMetricUnit(metricType: string) { + return AutoscalerConstants.getMetricUnit(metricType); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-base-step.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-base-step.ts new file mode 100644 index 0000000000..1a0f24e54a --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-base-step.ts @@ -0,0 +1,28 @@ +import { OnInit } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { StepOnNextFunction } from '../../../../core/src/shared/components/stepper/step/step.component'; +import { AppAutoscalerPolicy, AppAutoscalerPolicyLocal } from '../../store/app-autoscaler.types'; +import { EditAutoscalerPolicyService } from './edit-autoscaler-policy-service'; + +export abstract class EditAutoscalerPolicy implements OnInit { + public currentPolicy: AppAutoscalerPolicyLocal; + public appAutoscalerPolicy$: Observable; + + constructor(protected service: EditAutoscalerPolicyService) { } + + ngOnInit() { + this.appAutoscalerPolicy$ = this.service.getState().pipe( + map(state => { + this.currentPolicy = state; + return this.currentPolicy; + }) + ); + } + + onNext: StepOnNextFunction = () => { + this.service.setState(this.currentPolicy); + return of({ success: true }); + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-service.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-service.spec.ts new file mode 100644 index 0000000000..a2ce67df14 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-service.spec.ts @@ -0,0 +1,27 @@ +import { inject, TestBed } from '@angular/core/testing'; + +import { EntityServiceFactory } from '../../../../core/src/core/entity-service-factory.service'; +import { ApplicationsModule } from '../../../../core/src/features/applications/applications.module'; +import { createBasicStoreModule } from '../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../cf-autoscaler-testing.module'; +import { EditAutoscalerPolicyService } from './edit-autoscaler-policy-service'; + +describe('EditAutoscalerPolicyService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + EditAutoscalerPolicyService, + EntityServiceFactory, + ], + imports: [ + ApplicationsModule, + createBasicStoreModule(), + CfAutoscalerTestingModule + ] + }); + }); + + it('should be created', inject([EditAutoscalerPolicyService], (service: EditAutoscalerPolicyService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-service.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-service.ts new file mode 100644 index 0000000000..de61f059d3 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@angular/core'; +import * as moment from 'moment-timezone'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { filter, first } from 'rxjs/operators'; + +import { EntityServiceFactory } from '../../../../core/src/core/entity-service-factory.service'; +import { EntityInfo } from '../../../../store/src/types/api.types'; +import { autoscalerTransformArrayToMap } from '../../core/autoscaler-helpers/autoscaler-transform-policy'; +import { GetAppAutoscalerPolicyAction } from '../../store/app-autoscaler.actions'; +import { AppAutoscalerPolicyLocal } from '../../store/app-autoscaler.types'; + +@Injectable() +export class EditAutoscalerPolicyService { + + private initialState: AppAutoscalerPolicyLocal = autoscalerTransformArrayToMap({ + instance_min_count: 1, + instance_max_count: 10, + scaling_rules: [], + schedules: { + timezone: moment.tz.guess(), + recurring_schedule: [], + specific_date: [] + } + }); + + private stateSubject = new BehaviorSubject(this.initialState); + + + constructor(private entityServiceFactory: EntityServiceFactory) { } + + updateFromStore(appGuid: string, cfGuid: string) { + const appAutoscalerPolicyService = this.entityServiceFactory.create>( + appGuid, + new GetAppAutoscalerPolicyAction(appGuid, cfGuid), + false + ); + + appAutoscalerPolicyService.entityObs$.pipe( + // Stop if we've failed to fetch a policy or we've finished fetching a policy + filter(({ entity, entityRequestInfo }) => + entityRequestInfo && + (entityRequestInfo.error || (!entityRequestInfo.fetching && !!entity))), + first(), + ).subscribe((({ entity }) => { + if (entity && entity.entity) { + this.stateSubject.next(entity.entity); + } + })); + } + + setState(state: AppAutoscalerPolicyLocal) { + const { ...newState } = state; + this.stateSubject.next(newState); + } + + getState(): Observable { + return this.stateSubject.asObservable(); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.html b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.html new file mode 100644 index 0000000000..eab22c9e5a --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.html @@ -0,0 +1,23 @@ +
+
+ + + + {{policyAlert.alertInvalidPolicyMinimumRange}} + + + + + + {{policyAlert.alertInvalidPolicyMaximumRange}} + + + + + + {{ timezone }} + + + +
+
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.scss b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.scss new file mode 100644 index 0000000000..3e0f7a6dc2 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.scss @@ -0,0 +1,4 @@ + +.autoscaler-policy-edit-limit-max { + margin-top: .5em; +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.spec.ts new file mode 100644 index 0000000000..ce9dd0097a --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.spec.ts @@ -0,0 +1,50 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../../cf-autoscaler-testing.module'; +import { EditAutoscalerPolicyStep1Component } from './edit-autoscaler-policy-step1.component'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +describe('EditAutoscalerPolicyStep1Component', () => { + let component: EditAutoscalerPolicyStep1Component; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EditAutoscalerPolicyStep1Component], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService, + EditAutoscalerPolicyService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditAutoscalerPolicyStep1Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.ts new file mode 100644 index 0000000000..ef98dc334d --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component.ts @@ -0,0 +1,96 @@ +import { Component, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material'; +import * as moment from 'moment-timezone'; +import { of as observableOf } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { StepOnNextFunction } from '../../../../../core/src/shared/components/stepper/step/step.component'; +import { autoscalerTransformArrayToMap } from '../../../core/autoscaler-helpers/autoscaler-transform-policy'; +import { PolicyAlert } from '../../../core/autoscaler-helpers/autoscaler-util'; +import { numberWithFractionOrExceedRange } from '../../../core/autoscaler-helpers/autoscaler-validation'; +import { EditAutoscalerPolicy } from '../edit-autoscaler-policy-base-step'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +@Component({ + selector: 'app-edit-autoscaler-policy-step1', + templateUrl: './edit-autoscaler-policy-step1.component.html', + styleUrls: ['./edit-autoscaler-policy-step1.component.scss'], + providers: [ + { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher } + ] +}) +export class EditAutoscalerPolicyStep1Component extends EditAutoscalerPolicy implements OnInit { + + policyAlert = PolicyAlert; + timezoneOptions = moment.tz.names(); + editLimitForm: FormGroup; + + private editLimitValid = true; + + constructor( + public applicationService: ApplicationService, + private fb: FormBuilder, + service: EditAutoscalerPolicyService + ) { + super(service); + this.editLimitForm = this.fb.group({ + instance_min_count: [0, [Validators.required, this.validateGlobalLimitMin()]], + instance_max_count: [0, [Validators.required, this.validateGlobalLimitMax()]], + timezone: [0, [Validators.required]] + }); + } + + ngOnInit() { + this.service.updateFromStore(this.applicationService.appGuid, this.applicationService.cfGuid); + this.appAutoscalerPolicy$ = this.service.getState().pipe( + map(policy => { + this.currentPolicy = policy; + if (!this.currentPolicy.scaling_rules_form) { + this.currentPolicy = autoscalerTransformArrayToMap(this.currentPolicy); + } + this.editLimitForm.controls.timezone.setValue(this.currentPolicy.schedules.timezone); + this.editLimitForm.controls.instance_min_count.setValue(this.currentPolicy.instance_min_count); + this.editLimitForm.controls.instance_max_count.setValue(this.currentPolicy.instance_max_count); + this.editLimitForm.controls.instance_min_count.setValidators([Validators.required, this.validateGlobalLimitMin()]); + this.editLimitForm.controls.instance_max_count.setValidators([Validators.required, this.validateGlobalLimitMax()]); + return this.currentPolicy; + }) + ); + } + + onNext: StepOnNextFunction = () => { + this.currentPolicy.instance_min_count = Math.floor(this.editLimitForm.get('instance_min_count').value); + this.currentPolicy.instance_max_count = Math.floor(this.editLimitForm.get('instance_max_count').value); + this.currentPolicy.schedules.timezone = this.editLimitForm.get('timezone').value; + this.service.setState(this.currentPolicy); + return observableOf({ success: true }); + } + + validateGlobalLimitMin(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = this.editLimitForm ? + numberWithFractionOrExceedRange(control.value, 1, this.editLimitForm.get('instance_max_count').value - 1, true) : false; + const lastValid = this.editLimitValid; + this.editLimitValid = this.editLimitForm && control.value < this.editLimitForm.get('instance_max_count').value; + if (this.editLimitForm && this.editLimitValid !== lastValid) { + this.editLimitForm.controls.instance_max_count.updateValueAndValidity(); + } + return invalid ? { alertInvalidPolicyMinimumRange: { value: control.value } } : null; + }; + } + + validateGlobalLimitMax(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = this.editLimitForm ? numberWithFractionOrExceedRange(control.value, + this.editLimitForm.get('instance_min_count').value + 1, Number.MAX_VALUE, true) : false; + const lastValid = this.editLimitValid; + this.editLimitValid = this.editLimitForm && this.editLimitForm.get('instance_min_count').value < control.value; + if (this.editLimitForm && this.editLimitValid !== lastValid) { + this.editLimitForm.controls.instance_min_count.updateValueAndValidity(); + } + return invalid ? { alertInvalidPolicyMaximumRange: { value: control.value } } : null; + }; + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.html b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.html new file mode 100644 index 0000000000..5f15c5934d --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.html @@ -0,0 +1,110 @@ +
+ + + + + + + Rule {{index+1}}: + + + +
+ If average {{rule.metric_type}} {{rule.operator}} {{rule.threshold}} {{getMetricUnit(rule.metric_type)}} + for {{rule.breach_duration_secs}} seconds, then {{rule.adjustment}} instances. Cooldown: + {{rule.cool_down_secs}} seconds. +
+
+
+ If average + + + + {{ metricType }} + + + + + + + {{ operator }} + + + + + + + {{getMetricUnit(rule.metric_type)}} for + + + + seconsds, then {{editScaleType=='upper'?'add':'remove'}} + + + + + + instances + % instances + + + . Cooldown: + + + + seconds. +
+ + Threshold is required + + + {{policyAlert.alertInvalidPolicyTriggerThreshold100}} + + + + {{policyAlert.alertInvalidPolicyTriggerUpperThresholdRange}} + + + {{policyAlert.alertInvalidPolicyTriggerLowerThresholdRange}} + + + + {{policyAlert.alertInvalidPolicyTriggerBreachDurationRange}} + + + Adjustment is required + + + + {{policyAlert.alertInvalidPolicyTriggerStepRange}} + + + {{policyAlert.alertInvalidPolicyTriggerStepPercentageRange}} + + + + {{policyAlert.alertInvalidPolicyTriggerCooldownRange}} + +
+
+ + + + + +
+
+
+
+ +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.scss b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.scss new file mode 100644 index 0000000000..2e060b8936 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.scss @@ -0,0 +1,25 @@ +.app-autoscaler-tile-grid-100 { + width: 100%; +} + +.autoscaler-policy-edit { + display: flex; + position: relative; + width: 100%; + &__actions { + bottom: 10px; + padding-top: 0; + position: absolute; + right: 24px; + text-align: right; + } +} + +.autoscaler-policy-edit-trigger-item { + .autoscaler-policy-edit-trigger-item-desc { + margin-bottom: .5em; + } + mat-error { + font-size: 85%; + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.spec.ts new file mode 100644 index 0000000000..8cbb1fa5a7 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.spec.ts @@ -0,0 +1,50 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../../cf-autoscaler-testing.module'; +import { EditAutoscalerPolicyStep2Component } from './edit-autoscaler-policy-step2.component'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +describe('EditAutoscalerPolicyStep2Component', () => { + let component: EditAutoscalerPolicyStep2Component; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EditAutoscalerPolicyStep2Component], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService, + EditAutoscalerPolicyService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditAutoscalerPolicyStep2Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.ts new file mode 100644 index 0000000000..29165b0c00 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component.ts @@ -0,0 +1,188 @@ +import { Component, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material'; +import { Observable } from 'rxjs'; + +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { + AutoscalerConstants, + getAdjustmentType, + getScaleType, + PolicyAlert, +} from '../../../core/autoscaler-helpers/autoscaler-util'; +import { + getThresholdMax, + getThresholdMin, + numberWithFractionOrExceedRange, +} from '../../../core/autoscaler-helpers/autoscaler-validation'; +import { AppAutoscalerPolicy, AppAutoscalerPolicyLocal, AppAutoscalerInvalidPolicyError } from '../../../store/app-autoscaler.types'; +import { EditAutoscalerPolicy } from '../edit-autoscaler-policy-base-step'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +@Component({ + selector: 'app-edit-autoscaler-policy-step2', + templateUrl: './edit-autoscaler-policy-step2.component.html', + styleUrls: ['./edit-autoscaler-policy-step2.component.scss'], + providers: [ + { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher } + ] +}) +export class EditAutoscalerPolicyStep2Component extends EditAutoscalerPolicy implements OnInit { + + policyAlert = PolicyAlert; + metricTypes = AutoscalerConstants.MetricTypes; + operatorTypes = AutoscalerConstants.UpperOperators.concat(AutoscalerConstants.LowerOperators); + editTriggerForm: FormGroup; + appAutoscalerPolicy$: Observable; + + public currentPolicy: AppAutoscalerPolicyLocal; + public testing = false; + private editIndex = -1; + private editMetricType = ''; + private editScaleType = 'upper'; + private editAdjustmentType = 'value'; + + constructor( + public applicationService: ApplicationService, + private fb: FormBuilder, + service: EditAutoscalerPolicyService + ) { + super(service); + this.editTriggerForm = this.fb.group({ + metric_type: [0, this.validateTriggerMetricType()], + operator: [0, this.validateTriggerOperator()], + threshold: [0, [Validators.required, Validators.min(1), this.validateTriggerThreshold()]], + adjustment: [0, [Validators.required, Validators.min(1), this.validateTriggerAdjustment()]], + breach_duration_secs: [0, [ + Validators.min(AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_min), + Validators.max(AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_max) + ]], + cool_down_secs: [0, [ + Validators.min(AutoscalerConstants.PolicyDefaultSetting.cool_down_secs_min), + Validators.max(AutoscalerConstants.PolicyDefaultSetting.cool_down_secs_max) + ]], + adjustment_type: [0, this.validateTriggerAdjustmentType()] + }); + } + + addTrigger = () => { + const { ...newTrigger } = AutoscalerConstants.PolicyDefaultTrigger; + this.currentPolicy.scaling_rules_form.push(newTrigger); + this.editTrigger(this.currentPolicy.scaling_rules_form.length - 1); + } + + removeTrigger(index: number) { + if (this.editIndex === index) { + this.editIndex = -1; + } + this.currentPolicy.scaling_rules_form.splice(index, 1); + } + + editTrigger(index: number) { + this.editIndex = index; + this.editMetricType = this.currentPolicy.scaling_rules_form[index].metric_type; + this.editScaleType = getScaleType(this.currentPolicy.scaling_rules_form[index].operator); + this.editAdjustmentType = getAdjustmentType(this.currentPolicy.scaling_rules_form[index].adjustment); + this.editTriggerForm.setValue({ + metric_type: this.editMetricType, + operator: this.currentPolicy.scaling_rules_form[index].operator, + threshold: this.currentPolicy.scaling_rules_form[index].threshold, + adjustment: Math.abs(Number(this.currentPolicy.scaling_rules_form[index].adjustment)), + breach_duration_secs: this.currentPolicy.scaling_rules_form[index].breach_duration_secs, + cool_down_secs: this.currentPolicy.scaling_rules_form[index].cool_down_secs, + adjustment_type: this.editAdjustmentType + }); + } + + finishTrigger() { + const adjustmentP = this.editTriggerForm.get('adjustment_type').value === 'value' ? '' : '%'; + const adjustmentI = this.editTriggerForm.get('adjustment').value; + const adjustmentM = this.editScaleType === 'upper' ? `+${adjustmentI}${adjustmentP}` : `-${adjustmentI}${adjustmentP}`; + this.currentPolicy.scaling_rules_form[this.editIndex].metric_type = this.editTriggerForm.get('metric_type').value; + this.currentPolicy.scaling_rules_form[this.editIndex].operator = this.editTriggerForm.get('operator').value; + this.currentPolicy.scaling_rules_form[this.editIndex].threshold = this.editTriggerForm.get('threshold').value; + this.currentPolicy.scaling_rules_form[this.editIndex].adjustment = adjustmentM; + if (this.editTriggerForm.get('breach_duration_secs').value) { + this.currentPolicy.scaling_rules_form[this.editIndex].breach_duration_secs = + this.editTriggerForm.get('breach_duration_secs').value; + } else { + this.currentPolicy.scaling_rules_form[this.editIndex].breach_duration_secs = + AutoscalerConstants.PolicyDefaultSetting.breach_duration_secs_default; + } + if (this.editTriggerForm.get('cool_down_secs').value) { + this.currentPolicy.scaling_rules_form[this.editIndex].cool_down_secs = this.editTriggerForm.get('cool_down_secs').value; + } else { + this.currentPolicy.scaling_rules_form[this.editIndex].cool_down_secs = + AutoscalerConstants.PolicyDefaultSetting.cool_down_secs_default; + } + this.editIndex = -1; + } + + validateTriggerMetricType(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (this.editTriggerForm) { + this.editMetricType = control.value; + this.editTriggerForm.controls.threshold.updateValueAndValidity(); + } + return null; + }; + } + + validateTriggerOperator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (this.editTriggerForm) { + this.editScaleType = getScaleType(control.value); + this.editTriggerForm.controls.threshold.updateValueAndValidity(); + } + return null; + }; + } + + validateTriggerThreshold(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (!this.editTriggerForm) { + return null; + } + const errors: AppAutoscalerInvalidPolicyError = {}; + if (AutoscalerConstants.MetricPercentageTypes.indexOf(this.editMetricType) >= 0) { + if (numberWithFractionOrExceedRange(control.value, 1, 100, true)) { + errors.alertInvalidPolicyTriggerThreshold100 = { value: control.value }; + } + } + const thresholdMin = getThresholdMin(this.currentPolicy.scaling_rules_form, this.editMetricType, this.editScaleType, this.editIndex); + const thresholdMax = getThresholdMax(this.currentPolicy.scaling_rules_form, this.editMetricType, this.editScaleType, this.editIndex); + if (numberWithFractionOrExceedRange(control.value, thresholdMin, thresholdMax, true)) { + errors.alertInvalidPolicyTriggerThresholdRange = { value: control.value }; + } + return Object.keys(errors).length === 0 ? null : errors; + }; + } + + validateTriggerAdjustment(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (!this.editTriggerForm) { + return null; + } + const errors: AppAutoscalerInvalidPolicyError = {}; + const max = this.editAdjustmentType === 'value' ? this.currentPolicy.instance_max_count - 1 : Number.MAX_VALUE; + if (numberWithFractionOrExceedRange(control.value, 1, max, true)) { + errors.alertInvalidPolicyTriggerStepRange = {}; + } + return Object.keys(errors).length === 0 ? null : errors; + }; + } + + validateTriggerAdjustmentType(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (this.editTriggerForm) { + this.editAdjustmentType = control.value; + this.editTriggerForm.controls.adjustment.updateValueAndValidity(); + } + return null; + }; + } + + getMetricUnit(metricType: string) { + return AutoscalerConstants.getMetricUnit(metricType); + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.html b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.html new file mode 100644 index 0000000000..4ed111d0e6 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.html @@ -0,0 +1,157 @@ +
+ + + + + + + Schedule {{index+1}}: + + + +
+ The initial number of application instances is + {{rule.initial_min_instance_count}}, and the range + The number of application instances + is limited from {{rule.instance_min_count}} to {{rule.instance_max_count}} during + {{rule.start_time}} ~ {{rule.end_time}} every + {{rule.days_of_month}} of the month + {{rule.days_of_week}} of the week + , effective from {{rule.start_date}} to {{rule.end_date}}. + . +
+
+
+ +
+ + {{policyAlert.alertInvalidPolicyMinimumRange}} + + + {{policyAlert.alertInvalidPolicyMaximumRange}} + + + {{policyAlert.alertInvalidPolicyInitialMaximumRange}} + + + {{policyAlert.alertInvalidPolicyScheduleDateBeforeNow}} + + + {{policyAlert.alertInvalidPolicyScheduleEndDateBeforeStartDate}} + + + {{policyAlert.alertInvalidPolicyScheduleEndTimeBeforeStartTime}} + + + {{policyAlert.alertInvalidPolicyScheduleRepeatOn}} + + + days_of_week {{policyAlert.alertInvalidPolicyScheduleRecurringConflict}} + + + {{policyAlert.alertInvalidPolicyScheduleRepeatOn}} + + + days_of_month {{policyAlert.alertInvalidPolicyScheduleRecurringConflict}} + +
+
+ + + + + +
+
+
+
+ +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.scss b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.scss new file mode 100644 index 0000000000..59c4ea6d60 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.scss @@ -0,0 +1,47 @@ +.mat-radio-outer-circle { + height: 15px; + top: 2.5px; + width: 15px; +} + +.app-autoscaler-tile-grid-100 { + width: 100%; +} + +.autoscaler-policy-edit { + display: flex; + position: relative; + width: 100%; + &__actions { + bottom: 10px; + padding-top: 0; + position: absolute; + right: 24px; + text-align: right; + } +} + +.app-metadata { + display: flex; + flex-direction: row; + &__two-cols { + flex: 1; + margin-right: 3em; + app-metadata-item:first-child { + margin-top: 0; + } + } +} + +.autoscaler-policy-edit-recurring { + max-width: 100%; + .form-field-left { + margin-right: .5em; + } + .form-field-30 { + width: 30%; + } + .form-field-50 { + width: 50%; + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.spec.ts new file mode 100644 index 0000000000..186a4dac47 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.spec.ts @@ -0,0 +1,50 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../../cf-autoscaler-testing.module'; +import { EditAutoscalerPolicyStep3Component } from './edit-autoscaler-policy-step3.component'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +describe('EditAutoscalerPolicyStep3Component', () => { + let component: EditAutoscalerPolicyStep3Component; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EditAutoscalerPolicyStep3Component], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService, + EditAutoscalerPolicyService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditAutoscalerPolicyStep3Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.ts new file mode 100644 index 0000000000..34eebe9b2b --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component.ts @@ -0,0 +1,247 @@ +import { Component, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material'; +import * as moment from 'moment-timezone'; +import { Observable } from 'rxjs'; + +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { AutoscalerConstants, PolicyAlert, shiftArray } from '../../../core/autoscaler-helpers/autoscaler-util'; +import { + dateIsAfter, + numberWithFractionOrExceedRange, + recurringSchedulesOverlapping, + timeIsSameOrAfter, +} from '../../../core/autoscaler-helpers/autoscaler-validation'; +import { AppAutoscalerPolicy, AppAutoscalerPolicyLocal, AppAutoscalerInvalidPolicyError } from '../../../store/app-autoscaler.types'; +import { EditAutoscalerPolicy } from '../edit-autoscaler-policy-base-step'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; +import { + validateRecurringSpecificMax, + validateRecurringSpecificMin, +} from '../edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component'; + +@Component({ + selector: 'app-edit-autoscaler-policy-step3', + templateUrl: './edit-autoscaler-policy-step3.component.html', + styleUrls: ['./edit-autoscaler-policy-step3.component.scss'], + providers: [ + { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher } + ] +}) +export class EditAutoscalerPolicyStep3Component extends EditAutoscalerPolicy implements OnInit { + + policyAlert = PolicyAlert; + weekdayOptions = AutoscalerConstants.WeekdayOptions; + monthdayOptions = AutoscalerConstants.MonthdayOptions; + editRecurringScheduleForm: FormGroup; + appAutoscalerPolicy$: Observable; + + public currentPolicy: AppAutoscalerPolicyLocal; + private editIndex = -1; + private editEffectiveType = 'always'; + private editRepeatType = 'week'; + private editMutualValidation = { + limit: true, + date: true, + time: true + }; + + constructor( + public applicationService: ApplicationService, + private fb: FormBuilder, + service: EditAutoscalerPolicyService + ) { + super(service); + this.editRecurringScheduleForm = this.fb.group({ + days_of_week: [0], + days_of_month: [0], + instance_min_count: [0], + instance_max_count: [0], + initial_min_instance_count: [0, [this.validateRecurringScheduleInitialMin()]], + start_date: [0, [this.validateRecurringScheduleGlobal()]], + end_date: [0, [this.validateRecurringScheduleGlobal()]], + start_time: [0, [Validators.required, this.validateRecurringScheduleTime('end_time'), this.validateRecurringScheduleGlobal()]], + end_time: [0, [Validators.required, this.validateRecurringScheduleTime('start_time'), this.validateRecurringScheduleGlobal()]], + effective_type: [0, [Validators.required, this.validateRecurringScheduleGlobal()]], + repeat_type: [0, [Validators.required, this.validateRecurringScheduleGlobal()]], + }); + } + + addRecurringSchedule = () => { + const {...newSchedule} = AutoscalerConstants.PolicyDefaultRecurringSchedule; + this.currentPolicy.schedules.recurring_schedule.push(newSchedule); + this.editRecurringSchedule(this.currentPolicy.schedules.recurring_schedule.length - 1); + } + + removeRecurringSchedule(index: number) { + if (this.editIndex === index) { + this.editIndex = -1; + } + this.currentPolicy.schedules.recurring_schedule.splice(index, 1); + } + + editRecurringSchedule(index: number) { + const editSchedule = this.currentPolicy.schedules.recurring_schedule[index]; + this.editIndex = index; + this.editEffectiveType = editSchedule.start_date ? 'custom' : 'always'; + this.editRepeatType = editSchedule.days_of_week ? 'week' : 'month'; + this.editRecurringScheduleForm.setValue({ + days_of_week: shiftArray(editSchedule.days_of_week || [], -1), + days_of_month: shiftArray(editSchedule.days_of_month || [], -1), + instance_min_count: editSchedule.instance_min_count, + instance_max_count: Math.abs(Number(editSchedule.instance_max_count)), + initial_min_instance_count: editSchedule.initial_min_instance_count, + start_date: editSchedule.start_date || '', + end_date: editSchedule.end_date || '', + start_time: editSchedule.start_time, + end_time: editSchedule.end_time, + effective_type: this.editEffectiveType, + repeat_type: this.editRepeatType, + }); + this.setRecurringScheduleValidator(); + } + + setRecurringScheduleValidator() { + this.editRecurringScheduleForm.controls.instance_min_count.setValidators([Validators.required, + validateRecurringSpecificMin(this.editRecurringScheduleForm, this.editMutualValidation)]); + this.editRecurringScheduleForm.controls.instance_max_count.setValidators([Validators.required, + validateRecurringSpecificMax(this.editRecurringScheduleForm, this.editMutualValidation)]); + if (this.editEffectiveType === 'custom') { + if (!this.currentPolicy.schedules.recurring_schedule[this.editIndex].start_date && + !this.editRecurringScheduleForm.get('start_date').value) { + this.editRecurringScheduleForm.controls.start_date.setValue(moment().add(1, 'days').format(AutoscalerConstants.MomentFormateDate)); + this.editRecurringScheduleForm.controls.end_date.setValue(moment().add(1, 'days').format(AutoscalerConstants.MomentFormateDate)); + } + this.editRecurringScheduleForm.controls.start_date.setValidators([Validators.required, + this.validateRecurringScheduleDate('end_date'), this.validateRecurringScheduleGlobal()]); + this.editRecurringScheduleForm.controls.end_date.setValidators([Validators.required, + this.validateRecurringScheduleDate('start_date'), this.validateRecurringScheduleGlobal()]); + } else { + this.clearValidatorsThenRevalidate(this.editRecurringScheduleForm.controls.start_date); + this.clearValidatorsThenRevalidate(this.editRecurringScheduleForm.controls.end_date); + } + if (this.editRepeatType === 'week') { + this.editRecurringScheduleForm.controls.days_of_week.setValidators([Validators.required, this.validateRecurringScheduleWeekMonth()]); + this.clearValidatorsThenRevalidate(this.editRecurringScheduleForm.controls.days_of_month); + } else { + this.editRecurringScheduleForm.controls.days_of_month.setValidators([Validators.required, this.validateRecurringScheduleWeekMonth()]); + this.clearValidatorsThenRevalidate(this.editRecurringScheduleForm.controls.days_of_week); + } + } + + clearValidatorsThenRevalidate(input) { + input.clearValidators(); + input.updateValueAndValidity(); + } + + finishRecurringSchedule() { + const currentSchedule = this.currentPolicy.schedules.recurring_schedule[this.editIndex]; + const repeatOn = 'days_of_' + this.editRepeatType; + if (this.editRecurringScheduleForm.get('effective_type').value === 'custom') { + currentSchedule.start_date = this.editRecurringScheduleForm.get('start_date').value; + currentSchedule.end_date = this.editRecurringScheduleForm.get('end_date').value; + } else { + delete currentSchedule.start_date; + delete currentSchedule.end_date; + } + delete currentSchedule.days_of_month; + delete currentSchedule.days_of_week; + currentSchedule[repeatOn] = shiftArray(this.editRecurringScheduleForm.get(repeatOn).value, 1); + if (this.editRecurringScheduleForm.get('initial_min_instance_count').value) { + currentSchedule.initial_min_instance_count = this.editRecurringScheduleForm.get('initial_min_instance_count').value; + } else { + delete currentSchedule.initial_min_instance_count; + } + currentSchedule.instance_min_count = this.editRecurringScheduleForm.get('instance_min_count').value; + currentSchedule.instance_max_count = this.editRecurringScheduleForm.get('instance_max_count').value; + currentSchedule.start_time = this.editRecurringScheduleForm.get('start_time').value; + currentSchedule.end_time = this.editRecurringScheduleForm.get('end_time').value; + this.editIndex = -1; + } + + validateRecurringScheduleGlobal(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (this.editRecurringScheduleForm) { + if (this.editRepeatType === 'week') { + this.editRecurringScheduleForm.controls.days_of_week.updateValueAndValidity(); + } else { + this.editRecurringScheduleForm.controls.days_of_month.updateValueAndValidity(); + } + } + return null; + }; + } + + validateRecurringScheduleInitialMin(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = this.editRecurringScheduleForm && + numberWithFractionOrExceedRange(control.value, this.editRecurringScheduleForm.get('instance_min_count').value, + this.editRecurringScheduleForm.get('instance_max_count').value + 1, false); + return invalid ? { alertInvalidPolicyInitialMaximumRange: { value: control.value } } : null; + }; + } + + validateRecurringScheduleDate(mutualName: string): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (this.editEffectiveType === 'always') { + return null; + } + const errors: AppAutoscalerInvalidPolicyError = {}; + if (dateIsAfter(moment().format(AutoscalerConstants.MomentFormateDate), control.value)) { + errors.alertInvalidPolicyScheduleDateBeforeNow = { value: control.value }; + } + const lastValid = this.editMutualValidation.date; + this.editMutualValidation.date = + !dateIsAfter(this.editRecurringScheduleForm.get('start_date').value, this.editRecurringScheduleForm.get('end_date').value); + if (!this.editMutualValidation.date) { + errors.alertInvalidPolicyScheduleEndDateBeforeStartDate = { value: control.value }; + } + this.mutualValidate(mutualName, lastValid, this.editMutualValidation.date); + return Object.keys(errors).length === 0 ? null : errors; + }; + } + + validateRecurringScheduleTime(mutualName: string): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = this.editRecurringScheduleForm && + timeIsSameOrAfter(this.editRecurringScheduleForm.get('start_time').value, this.editRecurringScheduleForm.get('end_time').value); + const lastValid = this.editMutualValidation.time; + this.editMutualValidation.time = !invalid; + this.mutualValidate(mutualName, lastValid, this.editMutualValidation.time); + return invalid ? { alertInvalidPolicyScheduleEndTimeBeforeStartTime: { value: control.value } } : null; + }; + } + + validateRecurringScheduleWeekMonth(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const newSchedule: any = { + start_time: this.editRecurringScheduleForm.get('start_time').value, + end_time: this.editRecurringScheduleForm.get('end_time').value + }; + newSchedule['days_of_' + this.editRepeatType] = shiftArray(control.value, 1); + if (this.editEffectiveType === 'custom') { + newSchedule.start_date = this.editRecurringScheduleForm.get('start_date').value; + newSchedule.end_date = this.editRecurringScheduleForm.get('end_date').value; + } + const invalid = recurringSchedulesOverlapping(newSchedule, this.editIndex, + this.currentPolicy.schedules.recurring_schedule, 'days_of_' + this.editRepeatType); + return invalid ? { alertInvalidPolicyScheduleRecurringConflict: { value: control.value } } : null; + }; + } + + resetEffectiveType(key: string) { + this.editEffectiveType = key; + this.setRecurringScheduleValidator(); + } + + resetRepeatType(key: string) { + this.editRepeatType = key; + this.setRecurringScheduleValidator(); + } + + mutualValidate(inputName: string, lastValid: boolean, currentValid: boolean) { + if (this.editRecurringScheduleForm && lastValid !== currentValid) { + this.editRecurringScheduleForm.controls[inputName].updateValueAndValidity(); + } + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.html b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.html new file mode 100644 index 0000000000..948cf3574a --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.html @@ -0,0 +1,101 @@ +
+ + + + + + + Schedule {{index+1}}: + + + +
+ The initial number of application instances is + {{rule.initial_min_instance_count}}, and the range + The number of application instances + is limited from {{rule.instance_min_count}} to {{rule.instance_max_count}} from + {{rule.start_date_time}} to {{rule.end_date_time}}. +
+
+
+ +
+ + {{policyAlert.alertInvalidPolicyMinimumRange}} + + + {{policyAlert.alertInvalidPolicyMaximumRange}} + + + {{policyAlert.alertInvalidPolicyInitialMaximumRange}} + + + {{policyAlert.alertInvalidPolicyScheduleStartDateTimeBeforeNow}} + + + {{policyAlert.alertInvalidPolicyScheduleEndDateTimeBeforeStartDateTime}} + + + {{policyAlert.alertInvalidPolicyScheduleSpecificConflict}} + + + {{policyAlert.alertInvalidPolicyScheduleEndDateTimeBeforeNow}} + +
+
+ + + + + +
+
+
+
+ +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.scss b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.scss new file mode 100644 index 0000000000..7cb3f43091 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.scss @@ -0,0 +1,39 @@ +.app-autoscaler-tile-grid-100 { + width: 100%; +} + +.autoscaler-policy-edit { + display: flex; + position: relative; + width: 100%; + &__actions { + bottom: 10px; + padding-top: 0; + position: absolute; + right: 24px; + text-align: right; + } +} + +.app-metadata { + display: flex; + flex-direction: row; + &__two-cols { + flex: 1; + margin-right: 3em; + app-metadata-item:first-child { + margin-top: 0; + } + } +} + +.autoscaler-policy-edit-specific { + max-width: 100%; + .form-field-left { + margin-right: .5em; + } + .form-field-30 { + width: 30%; + } +} + diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.spec.ts new file mode 100644 index 0000000000..b965864158 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.spec.ts @@ -0,0 +1,50 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../../cf-autoscaler-testing.module'; +import { EditAutoscalerPolicyStep4Component } from './edit-autoscaler-policy-step4.component'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +describe('EditAutoscalerPolicyStep4Component', () => { + let component: EditAutoscalerPolicyStep4Component; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EditAutoscalerPolicyStep4Component], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService, + EditAutoscalerPolicyService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditAutoscalerPolicyStep4Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.ts new file mode 100644 index 0000000000..b0b345b885 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component.ts @@ -0,0 +1,281 @@ +import { Component, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material'; +import { Store } from '@ngrx/store'; +import * as moment from 'moment-timezone'; +import { Observable, of as observableOf } from 'rxjs'; +import { distinctUntilChanged, filter, map, take } from 'rxjs/operators'; + +import { EntityService } from '../../../../../core/src/core/entity-service'; +import { EntityServiceFactory } from '../../../../../core/src/core/entity-service-factory.service'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { StepOnNextFunction } from '../../../../../core/src/shared/components/stepper/step/step.component'; +import { AppState } from '../../../../../store/src/app-state'; +import { AutoscalerConstants, PolicyAlert } from '../../../core/autoscaler-helpers/autoscaler-util'; +import { + dateTimeIsSameOrAfter, + numberWithFractionOrExceedRange, + specificDateRangeOverlapping, +} from '../../../core/autoscaler-helpers/autoscaler-validation'; +import { UpdateAppAutoscalerPolicyAction } from '../../../store/app-autoscaler.actions'; +import { + AppAutoscalerInvalidPolicyError, + AppAutoscalerPolicy, + AppAutoscalerPolicyLocal, + AppSpecificDate, +} from '../../../store/app-autoscaler.types'; +import { EditAutoscalerPolicy } from '../edit-autoscaler-policy-base-step'; +import { EditAutoscalerPolicyService } from '../edit-autoscaler-policy-service'; + +@Component({ + selector: 'app-edit-autoscaler-policy-step4', + templateUrl: './edit-autoscaler-policy-step4.component.html', + styleUrls: ['./edit-autoscaler-policy-step4.component.scss'], + providers: [ + { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher } + ] +}) +export class EditAutoscalerPolicyStep4Component extends EditAutoscalerPolicy implements OnInit { + + policyAlert = PolicyAlert; + editSpecificDateForm: FormGroup; + appAutoscalerPolicy$: Observable; + + private updateAppAutoscalerPolicyService: EntityService; + public currentPolicy: AppAutoscalerPolicyLocal; + private editIndex = -1; + private editMutualValidation = { + limit: true, + datetime: true + }; + + constructor( + public applicationService: ApplicationService, + private store: Store, + private fb: FormBuilder, + private entityServiceFactory: EntityServiceFactory, + service: EditAutoscalerPolicyService + ) { + super(service); + this.editSpecificDateForm = this.fb.group({ + instance_min_count: [0], + instance_max_count: [0], + initial_min_instance_count: [0, [this.validateSpecificDateInitialMin()]], + start_date_time: [0, [Validators.required, this.validateSpecificDateStartDateTime()]], + end_date_time: [0, [Validators.required, this.validateSpecificDateEndDateTime()]] + }); + } + + ngOnInit() { + super.ngOnInit(); + this.updateAppAutoscalerPolicyService = this.entityServiceFactory.create( + this.applicationService.appGuid, + new UpdateAppAutoscalerPolicyAction(this.applicationService.appGuid, this.applicationService.cfGuid, this.currentPolicy), + false + ); + } + + updatePolicy: StepOnNextFunction = () => { + if (this.validateGlobalSetting()) { + return observableOf({ + success: false, + message: `Could not update policy: ${PolicyAlert.alertInvalidPolicyTriggerScheduleEmpty}`, + }); + } + this.store.dispatch( + new UpdateAppAutoscalerPolicyAction(this.applicationService.appGuid, this.applicationService.cfGuid, this.currentPolicy) + ); + const waitForAppAutoscalerUpdateStatus$ = this.updateAppAutoscalerPolicyService.entityMonitor.entityRequest$.pipe( + filter(request => { + if (request.message && request.message.indexOf('fetch policy') >= 0) { + request.message = ''; + return false; + } else { + return !!request.error || !!request.response; + } + }), + map(request => { + const msg = request.message; + request.error = false; + request.response = null; + request.message = ''; + return msg; + }), + distinctUntilChanged(), + ).pipe(map( + errorMessage => { + if (errorMessage) { + return { + success: false, + message: `Could not update policy: ${errorMessage}`, + }; + } else { + return { + success: true, + redirect: true + }; + } + })); + return waitForAppAutoscalerUpdateStatus$.pipe(take(1), map(res => { + return { + ...res, + }; + })); + } + + addSpecificDate = () => { + const { ...newSchedule } = AutoscalerConstants.PolicyDefaultSpecificDate; + this.currentPolicy.schedules.specific_date.push(newSchedule); + this.editSpecificDate(this.currentPolicy.schedules.specific_date.length - 1); + } + + removeSpecificDate(index: number) { + if (this.editIndex === index) { + this.editIndex = -1; + } + this.currentPolicy.schedules.specific_date.splice(index, 1); + } + + editSpecificDate(index: number) { + this.editIndex = index; + this.editSpecificDateForm.setValue({ + instance_min_count: this.currentPolicy.schedules.specific_date[index].instance_min_count, + instance_max_count: Math.abs(Number(this.currentPolicy.schedules.specific_date[index].instance_max_count)), + initial_min_instance_count: this.currentPolicy.schedules.specific_date[index].initial_min_instance_count, + start_date_time: this.currentPolicy.schedules.specific_date[index].start_date_time, + end_date_time: this.currentPolicy.schedules.specific_date[index].end_date_time, + }); + this.editSpecificDateForm.controls.instance_min_count.setValidators([Validators.required, + validateRecurringSpecificMin(this.editSpecificDateForm, this.editMutualValidation)]); + this.editSpecificDateForm.controls.instance_max_count.setValidators([Validators.required, + validateRecurringSpecificMax(this.editSpecificDateForm, this.editMutualValidation)]); + } + + finishSpecificDate() { + if (this.editSpecificDateForm.get('initial_min_instance_count').value) { + this.currentPolicy.schedules.specific_date[this.editIndex].initial_min_instance_count = + this.editSpecificDateForm.get('initial_min_instance_count').value; + } else { + delete this.currentPolicy.schedules.specific_date[this.editIndex].initial_min_instance_count; + } + this.currentPolicy.schedules.specific_date[this.editIndex].instance_min_count = + this.editSpecificDateForm.get('instance_min_count').value; + this.currentPolicy.schedules.specific_date[this.editIndex].instance_max_count = + this.editSpecificDateForm.get('instance_max_count').value; + this.currentPolicy.schedules.specific_date[this.editIndex].start_date_time = this.editSpecificDateForm.get('start_date_time').value; + this.currentPolicy.schedules.specific_date[this.editIndex].end_date_time = this.editSpecificDateForm.get('end_date_time').value; + this.editIndex = -1; + } + + validateSpecificDateInitialMin(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = this.editSpecificDateForm && numberWithFractionOrExceedRange(control.value, + this.editSpecificDateForm.get('instance_min_count').value, this.editSpecificDateForm.get('instance_max_count').value + 1, false); + return invalid ? { alertInvalidPolicyInitialMaximumRange: { value: control.value } } : null; + }; + } + + validateSpecificDateStartDateTime(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (!this.editSpecificDateForm) { + return null; + } + const errors: AppAutoscalerInvalidPolicyError = {}; + const newSchedule: AppSpecificDate = { + instance_min_count: 0, + instance_max_count: 0, + start_date_time: control.value, + end_date_time: this.editSpecificDateForm.get('end_date_time').value + }; + const lastValid = this.editMutualValidation.datetime; + this.editMutualValidation.datetime = true; + if (dateTimeIsSameOrAfter(moment().tz(this.currentPolicy.schedules.timezone) + .format(AutoscalerConstants.MomentFormateDateTimeT), control.value)) { + errors.alertInvalidPolicyScheduleStartDateTimeBeforeNow = { value: control.value }; + } + if (dateTimeIsSameOrAfter(control.value, this.editSpecificDateForm.get('end_date_time').value)) { + this.editMutualValidation.datetime = false; + errors.alertInvalidPolicyScheduleEndDateTimeBeforeStartDateTime = { value: control.value }; + } + if (specificDateRangeOverlapping(newSchedule, this.editIndex, this.currentPolicy.schedules.specific_date)) { + this.editMutualValidation.datetime = false; + errors.alertInvalidPolicyScheduleSpecificConflict = { value: control.value }; + } + if (this.editSpecificDateForm && lastValid !== this.editMutualValidation.datetime) { + this.editSpecificDateForm.controls.end_date_time.updateValueAndValidity(); + } + return Object.keys(errors).length === 0 ? null : errors; + }; + } + + validateSpecificDateEndDateTime(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + if (!this.editSpecificDateForm) { + return null; + } + const errors: AppAutoscalerInvalidPolicyError = {}; + const newSchedule = { + instance_min_count: 0, + instance_max_count: 0, + start_date_time: this.editSpecificDateForm.get('start_date_time').value, + end_date_time: control.value + }; + const lastValid = this.editMutualValidation.datetime; + this.editMutualValidation.datetime = true; + if (dateTimeIsSameOrAfter(moment().tz(this.currentPolicy.schedules.timezone). + format(AutoscalerConstants.MomentFormateDateTimeT), control.value)) { + errors.alertInvalidPolicyScheduleEndDateTimeBeforeNow = { value: control.value }; + } + if (dateTimeIsSameOrAfter(this.editSpecificDateForm.get('start_date_time').value, control.value)) { + this.editMutualValidation.datetime = false; + errors.alertInvalidPolicyScheduleEndDateTimeBeforeStartDateTime = { value: control.value }; + } + if (specificDateRangeOverlapping(newSchedule, this.editIndex, this.currentPolicy.schedules.specific_date)) { + this.editMutualValidation.datetime = false; + errors.alertInvalidPolicyScheduleSpecificConflict = { value: control.value }; + } + if (this.editSpecificDateForm && lastValid !== this.editMutualValidation.datetime) { + this.editSpecificDateForm.controls.start_date_time.updateValueAndValidity(); + } + return Object.keys(errors).length === 0 ? null : errors; + }; + } + + validateGlobalSetting() { + return this.currentPolicy.scaling_rules_form.length === 0 + && this.currentPolicy.schedules.recurring_schedule.length === 0 + && this.currentPolicy.schedules.specific_date.length === 0; + } +} + +export function validateRecurringSpecificMin(editForm, editMutualValidation): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = editForm && + numberWithFractionOrExceedRange(control.value, 1, editForm.get('instance_max_count').value - 1, true); + const lastValid = editMutualValidation.limit; + editMutualValidation.limit = editForm && control.value < editForm.get('instance_max_count').value; + if (editForm && lastValid !== editMutualValidation.limit) { + editForm.controls.instance_max_count.updateValueAndValidity(); + } + if (editForm) { + editForm.controls.initial_min_instance_count.updateValueAndValidity(); + } + return invalid ? { alertInvalidPolicyMinimumRange: { value: control.value } } : null; + }; +} + +export function validateRecurringSpecificMax(editForm, editMutualValidation): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const invalid = editForm && numberWithFractionOrExceedRange(control.value, + editForm.get('instance_min_count').value + 1, Number.MAX_VALUE, true); + const lastValid = editMutualValidation.limit; + editMutualValidation.limit = editForm && editForm.get('instance_min_count').value < control.value; + if (editForm && lastValid !== editMutualValidation.limit) { + editForm.controls.instance_min_count.updateValueAndValidity(); + } + if (editForm) { + editForm.controls.initial_min_instance_count.updateValueAndValidity(); + } + return invalid ? { alertInvalidPolicyMaximumRange: { value: control.value } } : null; + }; +} diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.html b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.html new file mode 100644 index 0000000000..adc4db13ee --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.html @@ -0,0 +1,25 @@ +
+ +

Edit AutoScaler Policy: {{ (applicationService.application$ | async)?.app.entity.name }}

+
+ +
+
+ + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.scss b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.scss new file mode 100644 index 0000000000..7b687f60c5 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.scss @@ -0,0 +1,16 @@ +.autoscaler-policy-edit-steppers { + height: 100%; + app-edit-autoscaler-policy-step1 { + width: 50%; + } + app-edit-autoscaler-policy-step2 { + width: 100%; + } + app-edit-autoscaler-policy-step3 { + width: 100%; + } + app-edit-autoscaler-policy-step4 { + width: 100%; + } +} + diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.spec.ts new file mode 100644 index 0000000000..7069cac31b --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.spec.ts @@ -0,0 +1,60 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../cf-autoscaler-testing.module'; +import { EditAutoscalerPolicyComponent } from './edit-autoscaler-policy.component'; +import { EditAutoscalerPolicyStep1Component } from './edit-autoscaler-policy-step1/edit-autoscaler-policy-step1.component'; +import { EditAutoscalerPolicyStep2Component } from './edit-autoscaler-policy-step2/edit-autoscaler-policy-step2.component'; +import { EditAutoscalerPolicyStep3Component } from './edit-autoscaler-policy-step3/edit-autoscaler-policy-step3.component'; +import { EditAutoscalerPolicyStep4Component } from './edit-autoscaler-policy-step4/edit-autoscaler-policy-step4.component'; +import { EditAutoscalerPolicyService } from './edit-autoscaler-policy-service'; + +describe('EditAutoscalerPolicyComponent', () => { + let component: EditAutoscalerPolicyComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + EditAutoscalerPolicyComponent, + EditAutoscalerPolicyStep1Component, + EditAutoscalerPolicyStep2Component, + EditAutoscalerPolicyStep3Component, + EditAutoscalerPolicyStep4Component, + ], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService, + EditAutoscalerPolicyService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditAutoscalerPolicyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.ts b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.ts new file mode 100644 index 0000000000..1af999f6b9 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/features/edit-autoscaler-policy/edit-autoscaler-policy.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map, publishReplay, refCount } from 'rxjs/operators'; +import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material'; + +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { EditAutoscalerPolicyService } from './edit-autoscaler-policy-service'; + +@Component({ + selector: 'app-edit-autoscaler-policy', + templateUrl: './edit-autoscaler-policy.component.html', + styleUrls: ['./edit-autoscaler-policy.component.scss'], + providers: [ + { provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher }, + EditAutoscalerPolicyService + ] +}) +export class EditAutoscalerPolicyComponent implements OnInit { + + parentUrl = `/applications/${this.applicationService.cfGuid}/${this.applicationService.appGuid}/autoscale`; + applicationName$: Observable; + + constructor( + public applicationService: ApplicationService, + ) { + } + + ngOnInit() { + this.applicationName$ = this.applicationService.app$.pipe( + map(({ entity }) => entity ? entity.entity.name : null), + publishReplay(1), + refCount() + ); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/public_api.ts b/src/frontend/packages/cf-autoscaler/src/public_api.ts new file mode 100644 index 0000000000..24160374d0 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/public_api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of cloud-foundry + */ + +// export * from './lib/cloud-foundry.service'; +export * from './lib/cf-autoscaler.module'; diff --git a/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.html b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.html new file mode 100644 index 0000000000..0dc89a73d4 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.html @@ -0,0 +1,48 @@ + +
+ + Instances + + + + +
+
+ + Limits + + + + +
+ + + +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.scss b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.scss new file mode 100644 index 0000000000..03cff2642b --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.scss @@ -0,0 +1,52 @@ +.card-autoscaler-default { + &__left { + border-right: 1px solid #808080; + display: inline-block; + padding-right: 1em; + } + + &__right { + display: inline-block; + padding-left: 1em; + } + + &__actions { + bottom: 24px; + padding-top: 0; + position: absolute; + right: 24px; + text-align: right; + } + + app-metadata-item { + .metadata-item__content { + flex-direction: row; + } + } + + .metadata-item__content { + display: inline; + } + + .metadata-item__value { + display: inline; + margin-right: 10px; + } + + .metadata-item__label { + display: inline; + margin-right: 10px; + } +} + +.app-metadata { + display: flex; + flex-direction: row; + &__two-cols { + flex: 1; + margin-right: 1em; + app-metadata-item:first-child { + margin-top: 0; + } + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.spec.ts new file mode 100644 index 0000000000..b21958943d --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.spec.ts @@ -0,0 +1,56 @@ +import { CommonModule } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { CoreModule } from '../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { ApplicationStateService } from '../../../../core/src/shared/components/application-state/application-state.service'; +import { MetadataItemComponent } from '../../../../core/src/shared/components/metadata-item/metadata-item.component'; +import { + RunningInstancesComponent, +} from '../../../../core/src/shared/components/running-instances/running-instances.component'; +import { EntityMonitorFactory } from '../../../../core/src/shared/monitors/entity-monitor.factory.service'; +import { PaginationMonitorFactory } from '../../../../core/src/shared/monitors/pagination-monitor.factory'; +import { ApplicationServiceMock } from '../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../cf-autoscaler-testing.module'; +import { CardAutoscalerDefaultComponent } from './card-autoscaler-default.component'; + +describe('CardAutoscalerDefaultComponent', () => { + let component: CardAutoscalerDefaultComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + CardAutoscalerDefaultComponent, + MetadataItemComponent, + RunningInstancesComponent, + ], + imports: [ + CoreModule, + CommonModule, + BrowserAnimationsModule, + createBasicStoreModule(), + CfAutoscalerTestingModule + ], + providers: [ + { provide: ApplicationService, useClass: ApplicationServiceMock }, + ApplicationStateService, + EntityMonitorFactory, + PaginationMonitorFactory, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CardAutoscalerDefaultComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.ts b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.ts new file mode 100644 index 0000000000..1b615f1001 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/card-autoscaler-default/card-autoscaler-default.component.ts @@ -0,0 +1,54 @@ +import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map, publishReplay, refCount } from 'rxjs/operators'; + +import { EntityService } from '../../../../core/src/core/entity-service'; +import { EntityServiceFactory } from '../../../../core/src/core/entity-service-factory.service'; +import { ApplicationService } from '../../../../core/src/features/applications/application.service'; +import { APIResource } from '../../../../store/src/types/api.types'; +import { GetAppAutoscalerPolicyAction } from '../../store/app-autoscaler.actions'; +import { AppAutoscalerPolicyLocal } from '../../store/app-autoscaler.types'; + + +@Component({ + selector: 'app-card-autoscaler-default', + templateUrl: './card-autoscaler-default.component.html', + styleUrls: ['./card-autoscaler-default.component.scss'] +}) +export class CardAutoscalerDefaultComponent implements OnInit { + + @ViewChild('instanceField') instanceField: ElementRef; + + constructor( + public appService: ApplicationService, + private entityServiceFactory: EntityServiceFactory, + private applicationService: ApplicationService, + ) { + } + + appAutoscalerPolicyService: EntityService; + appAutoscalerPolicy$: Observable>; + applicationInstances$: Observable; + + @Input() + onUpdate: () => void = () => { } + + ngOnInit() { + this.appAutoscalerPolicyService = this.entityServiceFactory.create>( + this.applicationService.appGuid, + new GetAppAutoscalerPolicyAction(this.applicationService.appGuid, this.applicationService.cfGuid), + false + ); + this.appAutoscalerPolicy$ = this.appAutoscalerPolicyService.entityObs$.pipe( + map(({ entity }) => { + return entity && entity.entity; + }) + ); + this.applicationInstances$ = this.applicationService.app$.pipe( + map(({ entity }) => entity ? entity.entity.instances : null), + publishReplay(1), + refCount() + ); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service.spec.ts new file mode 100644 index 0000000000..651ce2e852 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service.spec.ts @@ -0,0 +1,54 @@ +import { CommonModule } from '@angular/common'; +import { inject, TestBed } from '@angular/core/testing'; +import { ConnectionBackend, Http } from '@angular/http'; +import { MockBackend } from '@angular/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { CoreModule } from '../../../../../core/src/core/core.module'; +import { EntityServiceFactory } from '../../../../../core/src/core/entity-service-factory.service'; +import { ApplicationsModule } from '../../../../../core/src/features/applications/applications.module'; +import { SharedModule } from '../../../../../core/src/shared/shared.module'; +import { generateTestApplicationServiceProvider } from '../../../../../core/test-framework/application-service-helper'; +import { generateTestEntityServiceProvider } from '../../../../../core/test-framework/entity-service.helper'; +import { createBasicStoreModule, getInitialTestStoreState } from '../../../../../core/test-framework/store-test-helper'; +import { GetApplication } from '../../../../../store/src/actions/application.actions'; +import { applicationSchemaKey, entityFactory } from '../../../../../store/src/helpers/entity-factory'; +import { endpointStoreNames } from '../../../../../store/src/types/endpoint.types'; +import { CfAutoscalerTestingModule } from '../../../cf-autoscaler-testing.module'; +import { CfAppAutoscalerEventsConfigService } from './cf-app-autoscaler-events-config.service'; + +describe('CfAppAutoscalerEventsConfigService', () => { + const initialState = getInitialTestStoreState(); + + const cfGuid = Object.keys(initialState.requestData[endpointStoreNames.type])[0]; + const appGuid = Object.keys(initialState.requestData.application)[0]; + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CfAppAutoscalerEventsConfigService, + EntityServiceFactory, + generateTestEntityServiceProvider( + appGuid, + entityFactory(applicationSchemaKey), + new GetApplication(appGuid, cfGuid) + ), + generateTestApplicationServiceProvider(appGuid, cfGuid), + Http, + { provide: ConnectionBackend, useClass: MockBackend }, + ], + imports: [ + CommonModule, + CoreModule, + SharedModule, + ApplicationsModule, + createBasicStoreModule(), + RouterTestingModule, + CfAutoscalerTestingModule + ] + }); + }); + + it('should be created', inject([CfAppAutoscalerEventsConfigService], (service: CfAppAutoscalerEventsConfigService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service.ts new file mode 100644 index 0000000000..9d0eb3fa4f --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-config.service.ts @@ -0,0 +1,145 @@ +import { DatePipe } from '@angular/common'; +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import * as moment from 'moment'; + +import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { ITableColumn } from '../../../../../core/src/shared/components/list/list-table/table.types'; +import { IListConfig, ListConfig, ListViewTypes } from '../../../../../core/src/shared/components/list/list.component.types'; +import { MetricsRangeSelectorService } from '../../../../../core/src/shared/services/metrics-range-selector.service'; +import { ITimeRange, MetricQueryType } from '../../../../../core/src/shared/services/metrics-range-selector.types'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { AppAutoscalerEvent } from '../../../store/app-autoscaler.types'; +import { CfAppAutoscalerEventsDataSource } from './cf-app-autoscaler-events-data-source'; +import { + TableCellAutoscalerEventChangeComponent, +} from './table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component'; +import { + TableCellAutoscalerEventStatusComponent, +} from './table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component'; + +@Injectable() +export class CfAppAutoscalerEventsConfigService + extends ListConfig> + implements IListConfig> { + autoscalerEventSource: CfAppAutoscalerEventsDataSource; + columns: Array>> = [ + { + columnId: 'timestamp', + headerCell: () => 'Timestamp', + cellDefinition: { + getValue: row => this.datePipe.transform(row.entity.timestamp / 1000000, 'medium') + }, + sort: true, + cellFlex: '3' + }, + { + columnId: 'status', headerCell: () => 'Status', cellComponent: TableCellAutoscalerEventStatusComponent, cellFlex: '2' + }, + { + columnId: 'type', + headerCell: () => 'Type', + cellDefinition: { + getValue: row => row.entity.scaling_type === 0 ? 'dynamic' : 'schedule' + }, + cellFlex: '2' + }, + { + columnId: 'change', headerCell: () => 'Instance Change', cellComponent: TableCellAutoscalerEventChangeComponent, cellFlex: '2' + }, + { + columnId: 'action', + headerCell: () => 'Action', + cellDefinition: { + getValue: row => { + if (row.entity.message) { + const change = row.entity.new_instances - row.entity.old_instances; + if (change >= 0) { + return '+' + change + ' instance(s) because ' + row.entity.message; + } else { + return change + ' instance(s) because ' + row.entity.message; + } + } else { + return row.entity.reason; + } + } + }, + cellFlex: '4' + }, + { + columnId: 'error', + headerCell: () => 'Error', + cellDefinition: { + valuePath: 'entity.error' + }, + cellFlex: '4' + }, + ]; + viewType = ListViewTypes.TABLE_ONLY; + text = { + title: null, + noEntries: 'There are no scaling events' + }; + isLocal = false; + + showCustomTime = true; + customTimePollingInterval = 120000; + customTimeInitialValue = '1:month'; + customTimeWindows: ITimeRange[] = [ + { + value: '1:day', + label: 'The past day', + queryType: MetricQueryType.QUERY + }, + { + value: '1:week', + label: 'The past week', + queryType: MetricQueryType.QUERY + }, + { + value: '1:month', + label: 'The past month', + queryType: MetricQueryType.QUERY + }, + { + label: 'Custom time window', + queryType: MetricQueryType.RANGE_QUERY + } + ]; + + private thirtyDays = 1000 * 60 * 60 * 24 * 30; + customTimeValidation = (start: moment.Moment, end: moment.Moment) => { + if (!end || !start) { + return null; + } + if (!start.isBefore(end)) { + return 'Start date must be before end date.'; + } + if (moment().diff(start) > this.thirtyDays) { + return 'Only recent 30 days data are support to be query.'; + } + } + + constructor( + private store: Store, + private appService: ApplicationService, + private datePipe: DatePipe, + metricsRangeService: MetricsRangeSelectorService) { + super(); + this.autoscalerEventSource = new CfAppAutoscalerEventsDataSource( + this.store, + this.appService.cfGuid, + this.appService.appGuid, + this, + metricsRangeService + ); + } + + getGlobalActions = () => null; + getMultiActions = () => null; + getSingleActions = () => null; + getColumns = () => this.columns; + getDataSource = () => this.autoscalerEventSource; + getMultiFiltersConfigs = () => []; +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-data-source.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-data-source.ts new file mode 100644 index 0000000000..6fb0728884 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/cf-app-autoscaler-events-data-source.ts @@ -0,0 +1,43 @@ +import { Store } from '@ngrx/store'; + +import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state'; +import { getRowMetadata } from '../../../../../core/src/features/cloud-foundry/cf.helpers'; +import { ListDataSource } from '../../../../../core/src/shared/components/list/data-sources-controllers/list-data-source'; +import { IListConfig } from '../../../../../core/src/shared/components/list/list.component.types'; +import { MetricsRangeSelectorService } from '../../../../../core/src/shared/services/metrics-range-selector.service'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { GetAppAutoscalerScalingHistoryAction } from '../../../store/app-autoscaler.actions'; +import { AppAutoscalerEvent } from '../../../store/app-autoscaler.types'; +import { appAutoscalerScalingHistoryEntityType, autoscalerEntityFactory } from '../../../store/autoscaler-entity-factory'; + + +export class CfAppAutoscalerEventsDataSource extends ListDataSource> { + action: any; + constructor( + store: Store, + cfGuid: string, + appGuid: string, + listConfig: IListConfig>, + metricsRangeService: MetricsRangeSelectorService + ) { + const action = new GetAppAutoscalerScalingHistoryAction(null, appGuid, cfGuid); + super( + { + store, + action, + schema: autoscalerEntityFactory(appAutoscalerScalingHistoryEntityType), + getRowUniqueId: getRowMetadata, + paginationKey: action.paginationKey, + isLocal: false, + listConfig, + refresh: () => { + if (this.metricsAction.windowValue) { + this.metricsAction = metricsRangeService.getNewTimeWindowAction(this.metricsAction, this.metricsAction.windowValue); + } + this.store.dispatch(this.metricsAction); + } + } + ); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change-icon.pipe.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change-icon.pipe.ts new file mode 100644 index 0000000000..a71f4bfa30 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change-icon.pipe.ts @@ -0,0 +1,31 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'tableCellAutoscalerEventChangeIcon' +}) +export class TableCellAutoscalerEventChangeIconPipe implements PipeTransform { + + private result(outputType: string, value: number, cssClass: string, icon: string) { + switch (outputType) { + case 'class': + return cssClass; + case 'icon': + return icon; + default: + return ''; + } + } + + transform(value: number, args?: string): string { + if (!args || !args.length) { + return ''; + } + if (value > 0) { + return this.result(args, value, 'text-danger', 'trending_up'); + } else if (value < 0) { + return this.result(args, value, 'text-success', 'trending_down'); + } else { + return this.result(args, value, 'text-tentative', 'trending_flat'); + } + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.html b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.html new file mode 100644 index 0000000000..cde0f6d000 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.html @@ -0,0 +1,6 @@ +
+ {{row.entity.old_instances}} + + {{row.entity.new_instances-row.entity.old_instances | tableCellAutoscalerEventChangeIcon:'icon'}} + {{row.entity.new_instances}} +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.scss b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.scss new file mode 100644 index 0000000000..7585cbff9a --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.scss @@ -0,0 +1,5 @@ +.app-autoscaler-event-change { + align-items: center; + display: flex; + flex-direction: row; +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.spec.ts new file mode 100644 index 0000000000..f9ee28447f --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.spec.ts @@ -0,0 +1,33 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatIcon } from '@angular/material'; + +import { EntityInfo } from '../../../../../../store/src/types/api.types'; +import { TableCellAutoscalerEventChangeIconPipe } from './table-cell-autoscaler-event-change-icon.pipe'; +import { TableCellAutoscalerEventChangeComponent } from './table-cell-autoscaler-event-change.component'; + +describe('TableCellAutoscalerEventChangeComponent', () => { + let component: TableCellAutoscalerEventChangeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TableCellAutoscalerEventChangeComponent, MatIcon, TableCellAutoscalerEventChangeIconPipe] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TableCellAutoscalerEventChangeComponent); + component = fixture.componentInstance; + component.row = { + entity: { + type: '' + } + } as EntityInfo; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.ts new file mode 100644 index 0000000000..284ee793f1 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-change/table-cell-autoscaler-event-change.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +import { TableCellCustom } from '../../../../../../core/src/shared/components/list/list.types'; + +@Component({ + selector: 'app-table-cell-autoscaler-event-change', + templateUrl: './table-cell-autoscaler-event-change.component.html', + styleUrls: ['./table-cell-autoscaler-event-change.component.scss'] +}) +export class TableCellAutoscalerEventChangeComponent extends TableCellCustom { } diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status-icon.pipe.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status-icon.pipe.ts new file mode 100644 index 0000000000..cd6217d3fd --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status-icon.pipe.ts @@ -0,0 +1,38 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'tableCellAutoscalerEventStatusIcon' +}) +export class TableCellAutoscalerEventStatusIconPipe implements PipeTransform { + + private result(outputType: string, value: number, cssClass: string, icon: string, label: string) { + switch (outputType) { + case 'class': + return cssClass; + case 'icon': + return icon; + case 'label': + return label; + default: + return ''; + } + } + + transform(value: number, args?: string): string { + if (!args || !args.length) { + return ''; + } + switch (value) { + case 0: + return this.result(args, value, 'text-success', 'lens', 'succeeded'); + case 1: + return this.result(args, value, 'text-danger', 'warning', 'failed'); + case 2: + return this.result(args, value, 'text-tentative', 'broken_image', 'ignored'); + default: + return ''; + } + + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.html b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.html new file mode 100644 index 0000000000..d295c4f14c --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.html @@ -0,0 +1,5 @@ +
+ + {{row.entity.status | tableCellAutoscalerEventStatusIcon:'icon'}} + {{row.entity.status | tableCellAutoscalerEventStatusIcon:'label'}} +
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.scss b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.scss new file mode 100644 index 0000000000..195f5260cb --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.scss @@ -0,0 +1,12 @@ +.app-autoscaler-event-status { + align-items: center; + display: flex; + flex-direction: row; + .mat-icon { + height: 14px; + width: 14px; + } + .material-icons { + font-size: 14px; + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.spec.ts new file mode 100644 index 0000000000..08e1647b35 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.spec.ts @@ -0,0 +1,33 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatIcon } from '@angular/material'; + +import { EntityInfo } from '../../../../../../store/src/types/api.types'; +import { TableCellAutoscalerEventStatusIconPipe } from './table-cell-autoscaler-event-status-icon.pipe'; +import { TableCellAutoscalerEventStatusComponent } from './table-cell-autoscaler-event-status.component'; + +describe('TableCellAutoscalerEventStatusComponent', () => { + let component: TableCellAutoscalerEventStatusComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TableCellAutoscalerEventStatusComponent, MatIcon, TableCellAutoscalerEventStatusIconPipe] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TableCellAutoscalerEventStatusComponent); + component = fixture.componentInstance; + component.row = { + entity: { + type: '' + } + } as EntityInfo; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.ts new file mode 100644 index 0000000000..ab8cf4d7e7 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-event/table-cell-autoscaler-event-status/table-cell-autoscaler-event-status.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +import { TableCellCustom } from '../../../../../../core/src/shared/components/list/list.types'; +import { EntityInfo } from '../../../../../../store/src/types/api.types'; + +@Component({ + selector: 'app-table-cell-autoscaler-event-status', + templateUrl: './table-cell-autoscaler-event-status.component.html', + styleUrls: ['./table-cell-autoscaler-event-status.component.scss'] +}) +export class TableCellAutoscalerEventStatusComponent extends TableCellCustom { } diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.html b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.html new file mode 100644 index 0000000000..8496cb9450 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.html @@ -0,0 +1,31 @@ + + + + {{metricType}}
+
+ {{paramsMetricsStart | date:'medium'}} ~ + {{paramsMetricsEnd | date:'medium'}} +
+
+
+ +
+ + +
+
+ + +
+
+
\ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.scss b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.scss new file mode 100644 index 0000000000..002bddcdbd --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.scss @@ -0,0 +1,26 @@ +mat-card-header { + .autoscaler-metric-subtitle { + color: rgba(0, 0, 0, .54); + font-size: .9em; + margin-top: .3em; + } +} + +.autoscaler-chart-card { + display: flex; + height: 250px; + width: 100%; + + .autoscaler-chart-gauge { + height: 200px; + margin: 40px 20px 0 0; + width: 20%; + ngx-charts-gauge { + width: 100%; + } + } + .autoscaler-chart-combo { + margin-top: 1em; + width: 80%; + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.spec.ts new file mode 100644 index 0000000000..280d49798e --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.spec.ts @@ -0,0 +1,71 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; + +import { + ApplicationEnvVarsHelper, +} from '../../../../../../core/src/features/applications/application/application-tabs-base/tabs/build-tab/application-env-vars.service'; +import { + ApplicationStateService, +} from '../../../../../../core/src/shared/components/application-state/application-state.service'; +import { ConfirmationDialogService } from '../../../../../../core/src/shared/components/confirmation-dialog.service'; +import { ServiceActionHelperService } from '../../../../../../core/src/shared/data-services/service-action-helper.service'; +import { EntityMonitorFactory } from '../../../../../../core/src/shared/monitors/entity-monitor.factory.service'; +import { PaginationMonitorFactory } from '../../../../../../core/src/shared/monitors/pagination-monitor.factory'; +import { generateTestApplicationServiceProvider } from '../../../../../../core/test-framework/application-service-helper'; +import { BaseTestModules } from '../../../../../../core/test-framework/cloud-foundry-endpoint-service.helper'; +import { AppAutoscalerMetricChartCardComponent } from './app-autoscaler-metric-chart-card.component'; +import { AppAutoscalerComboChartComponent } from './combo-chart/combo-chart.component'; +import { AppAutoscalerComboSeriesVerticalComponent } from './combo-chart/combo-series-vertical.component'; + +describe('AppAutoscalerMetricChartCardComponent', () => { + let component: AppAutoscalerMetricChartCardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppAutoscalerMetricChartCardComponent, + AppAutoscalerComboChartComponent, + AppAutoscalerComboSeriesVerticalComponent + ], + imports: [ + ...BaseTestModules, + NgxChartsModule + ], + providers: [ + EntityMonitorFactory, + generateTestApplicationServiceProvider('1', '1'), + ApplicationEnvVarsHelper, + ApplicationStateService, + PaginationMonitorFactory, + ConfirmationDialogService, + DatePipe, + ServiceActionHelperService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AppAutoscalerMetricChartCardComponent); + component = fixture.componentInstance; + component.row = { + entity: { + upper: [], + lower: [], + }, + metadata: { + guid: '', + created_at: '', + updated_at: '', + url: '' + } + }; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.ts new file mode 100644 index 0000000000..16c9a735e0 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component.ts @@ -0,0 +1,121 @@ +import { Component, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { filter } from 'rxjs/operators'; + +import { ApplicationService } from '../../../../../../core/src/features/applications/application.service'; +import { CardCell, IListRowCell } from '../../../../../../core/src/shared/components/list/list.types'; +import { PaginationMonitorFactory } from '../../../../../../core/src/shared/monitors/pagination-monitor.factory'; +import { AppState } from '../../../../../../store/src/app-state'; +import { getPaginationObservables } from '../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; +import { APIResource } from '../../../../../../store/src/types/api.types'; +import { AutoscalerConstants, buildLegendData } from '../../../../core/autoscaler-helpers/autoscaler-util'; +import { AutoscalerPaginationParams, GetAppAutoscalerAppMetricAction } from '../../../../store/app-autoscaler.actions'; +import { + AppAutoscalerMetricData, + AppAutoscalerMetricDataPoint, + AppScalingTrigger, +} from '../../../../store/app-autoscaler.types'; +import { appAutoscalerAppMetricEntityType, autoscalerEntityFactory } from '../../../../store/autoscaler-entity-factory'; + + +@Component({ + selector: 'app-app-autoscaler-metric-chart-card', + templateUrl: './app-autoscaler-metric-chart-card.component.html', + styleUrls: ['./app-autoscaler-metric-chart-card.component.scss'] +}) + +export class AppAutoscalerMetricChartCardComponent extends CardCell> implements IListRowCell { + static columns = 1; + listData: { + label: string; + data$: Observable; + customStyle?: string; + }[]; + + envVarUrl: string; + + comboBarScheme = { + name: 'singleLightBlue', + selectable: true, + group: 'Ordinal', + domain: ['#01579b'] + }; + lineChartScheme = { + name: 'coolthree', + selectable: true, + group: 'Ordinal', + domain: ['#01579b'] + }; + + public paramsMetricsEnd: number = (new Date()).getTime(); + public paramsMetricsStart: number = this.paramsMetricsEnd - 30 * 60 * 1000; + public paramsMetrics: AutoscalerPaginationParams = { + 'start-time': this.paramsMetricsStart + '000000', + 'end-time': this.paramsMetricsEnd + '000000', + page: '1', + 'results-per-page': '10000000', + 'order-direction': 'asc' + }; + + public metricType: string; + + @Input('row') + set row(row: APIResource) { + if (row) { + if (row.entity.query && row.entity.query.params) { + this.paramsMetricsStart = row.entity.query.params.start * 1000; + this.paramsMetricsEnd = row.entity.query.params.end * 1000; + this.paramsMetrics['start-time'] = this.paramsMetricsStart + '000000'; + this.paramsMetrics['end-time'] = this.paramsMetricsEnd + '000000'; + + this.appAutoscalerAppMetricLegend = this.getLegend2(row.entity); + this.metricType = AutoscalerConstants.getMetricFromMetricId(row.metadata.guid); + this.metricData$ = this.getAppMetric(this.metricType, row.entity, this.paramsMetrics); + } + } + } + + constructor( + private appService: ApplicationService, + private store: Store, + private paginationMonitorFactory: PaginationMonitorFactory, + ) { + super(); + } + + public metricData$: Observable; + public appAutoscalerAppMetricLegend; + + getLegend2(trigger: AppScalingTrigger) { + const legendColor = buildLegendData(trigger); + const legendValue: AppAutoscalerMetricDataPoint[] = []; + legendColor.map((item) => { + legendValue.push({ + name: item.name, + value: 1 + }); + }); + return { + legendValue, + legendColor + }; + } + + getAppMetric(metricName: string, trigger: AppScalingTrigger, params: AutoscalerPaginationParams): Observable { + const action = new GetAppAutoscalerAppMetricAction(this.appService.appGuid, + this.appService.cfGuid, metricName, false, trigger, params); + this.store.dispatch(action); + return getPaginationObservables({ + store: this.store, + action, + paginationMonitor: this.paginationMonitorFactory.create( + action.paginationKey, + autoscalerEntityFactory(appAutoscalerAppMetricEntityType) + ) + }, false).entities$.pipe( + filter(entities => !!entities) + ); + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.html b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.html new file mode 100644 index 0000000000..e2605ee693 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.scss b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.scss new file mode 100644 index 0000000000..345a3802c0 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.scss @@ -0,0 +1,85 @@ +.ngx-charts { + float: left; + overflow: visible; + + .circle, + .bar, + .arc { + cursor: pointer; + } + + .bar, + .cell, + .arc, + .card { + &.active, + &:hover { + opacity: .8; + transition: opacity 100ms ease-in-out; + } + + &:focus { + outline: none; + } + } + + g { + &:focus { + outline: none; + } + } + + .line-series, + .line-series-range, + .area-series { + &.inactive { + opacity: .2; + transition: opacity 100ms ease-in-out; + } + } + + .line-highlight { + display: none; + + &.active { + display: block; + } + } + + .area { + opacity: .6; + } + + .circle { + &:hover { + cursor: pointer; + } + } + + .label { + font-size: 12px; + font-weight: normal; + } + + .tooltip-anchor { + fill: rgb(0, 0, 0); + } + + .gridline-path { + fill: none; + stroke: #ddd; + stroke-width: 1; + } + + .grid-panel { + rect { + fill: none; + } + + &.odd { + rect { + fill: rgba(0, 0, 0, .05); + } + } + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.ts new file mode 100644 index 0000000000..99c5c596c9 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-chart.component.ts @@ -0,0 +1,421 @@ +import { + Component, + ContentChild, + EventEmitter, + HostListener, + Input, + Output, + TemplateRef, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import { + BaseChartComponent, + calculateViewDimensions, + ColorHelper, + LineSeriesComponent, + ViewDimensions, +} from '@swimlane/ngx-charts'; +import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale'; +import { curveLinear } from 'd3-shape'; + +@Component({ + selector: 'app-autoscaler-combo-chart-component', + templateUrl: './combo-chart.component.html', + styleUrls: ['./combo-chart.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class AppAutoscalerComboChartComponent extends BaseChartComponent { + + @ViewChild(LineSeriesComponent) lineSeriesComponent: LineSeriesComponent; + + @Input() curve: any = curveLinear; + @Input() legend = false; + @Input() legendTitle = 'Legend'; + @Input() legendPosition = 'right'; + @Input() xAxis; + @Input() yAxis; + @Input() showXAxisLabel; + @Input() showYAxisLabel; + @Input() xAxisLabel; + @Input() yAxisLabel; + @Input() tooltipDisabled = false; + @Input() gradient: boolean; + @Input() showGridLines = true; + @Input() activeEntries: any[] = []; + @Input() schemeType: string; + @Input() xAxisTickFormatting: any; + @Input() yAxisTickFormatting: any; + @Input() roundDomains = false; + @Input() colorSchemeLine: any[]; + @Input() autoScale; + @Input() lineChart: any; + @Input() yLeftAxisScaleFactor: any; + @Input() rangeFillOpacity: number; + @Input() animations = true; + @Input() yScaleMax: number; + @Input() metricName: string; + @Input() legendData: any[]; + + @Output() activate: EventEmitter = new EventEmitter(); + @Output() deactivate: EventEmitter = new EventEmitter(); + + @ContentChild('tooltipTemplate') tooltipTemplate: TemplateRef; + @ContentChild('seriesTooltipTemplate') seriesTooltipTemplate: TemplateRef; + + dims: ViewDimensions; + xScale: any; + yScale: any; + xDomain: any; + yDomain: any; + transform: string; + colors: ColorHelper; + colorsLine: ColorHelper; + colorsExtra: ColorHelper; + margin: any[] = [10, 20, 10, 20]; + xAxisHeight = 0; + yAxisWidth = 0; + legendOptions: any; + legendOptionsExtra: any; + scaleType = 'linear'; + xScaleLine; + yScaleLine; + xDomainLine; + yDomainLine; + seriesDomain; + scaledAxis; + combinedSeries; + xSet; + filteredDomain; + hoveredVertical; + yOrientLeft = 'left'; + yOrientRight = 'right'; + legendSpacing = 0; + bandwidth; + barPadding = 2; + + trackBy(index, item): string { + return item.name; + } + + update(): void { + super.update(); + if (!this.yAxis) { + this.legendSpacing = 0; + } else { + this.legendSpacing = 50; + } + + this.dims = calculateViewDimensions({ + width: this.legend ? this.width - this.legendSpacing : this.width, + height: this.height, + margins: this.margin, + showXAxis: this.xAxis, + showYAxis: this.yAxis, + xAxisHeight: this.xAxisHeight, + yAxisWidth: this.yAxisWidth, + showXLabel: this.showXAxisLabel, + showYLabel: this.showYAxisLabel, + showLegend: this.legend, + legendType: this.schemeType, + }); + + this.xScale = this.getXScale(); + this.yScale = this.getYScale(); + + // line chart + this.xDomainLine = this.getXDomainLine(); + if (this.filteredDomain) { + this.xDomainLine = this.filteredDomain; + } + + this.yDomainLine = this.getYDomain(); + this.seriesDomain = this.getSeriesDomain(); + + this.xScaleLine = this.getXScaleLine(this.xDomainLine, this.dims.width); + this.yScaleLine = this.getYScaleLine(this.yDomainLine, this.dims.height); + + this.setColors(); + this.legendOptions = this.getLegendOptions(); + this.legendOptionsExtra = this.getLegendOptionsExtra(); + + this.transform = `translate(${this.dims.xOffset} , ${this.margin[0]})`; + } + + deactivateAll() { + this.activeEntries = [...this.activeEntries]; + for (const entry of this.activeEntries) { + this.deactivate.emit({ value: entry, entries: [] }); + } + this.activeEntries = []; + } + + @HostListener('mouseleave') + hideCircles(): void { + this.hoveredVertical = null; + this.deactivateAll(); + } + + updateHoveredVertical(item): void { + this.hoveredVertical = item.value; + this.deactivateAll(); + } + + updateDomain(domain): void { + this.filteredDomain = domain; + this.xDomainLine = this.filteredDomain; + this.xScaleLine = this.getXScaleLine(this.xDomainLine, this.dims.width); + } + + getSeriesDomain(): any[] { + this.combinedSeries = []; + this.combinedSeries.push({ + name: this.metricName, + series: this.results + }); + return this.combinedSeries.map(d => d.name); + } + + isDate(value): boolean { + if (value instanceof Date) { + return true; + } + + return false; + } + + getScaleType(values): string { + let date = true; + let num = true; + + for (const value of values) { + if (!this.isDate(value)) { + date = false; + } + + if (typeof value !== 'number') { + num = false; + } + } + + if (date) { + return 'time'; + } + if (num) { + return 'linear'; + } + return 'ordinal'; + } + + getXDomainLine(): any[] { + let values = []; + + for (const results of this.lineChart) { + for (const d of results.series) { + if (!values.includes(d.name)) { + values.push(d.name); + } + } + } + + this.scaleType = this.getScaleType(values); + let domain = []; + + if (this.scaleType === 'time') { + const min = Math.min(...values); + const max = Math.max(...values); + domain = [min, max]; + } else if (this.scaleType === 'linear') { + values = values.map(v => Number(v)); + const min = Math.min(...values); + const max = Math.max(...values); + domain = [min, max]; + } else { + domain = values; + } + + this.xSet = values; + return domain; + } + + getYDomainLine(): any[] { + const domain = []; + + for (const results of this.lineChart) { + for (const d of results.series) { + if (domain.indexOf(d.value) < 0) { + domain.push(d.value); + } + if (d.min !== undefined && domain.indexOf(d.min) < 0) { + domain.push(d.min); + } + if (d.max !== undefined && domain.indexOf(d.max) < 0) { + domain.push(d.max); + } + } + } + + const min = Math.min(...domain); + const max = this.yScaleMax + ? this.yScaleMax + : Math.max(...domain); + return [min, max]; + } + + getXScaleLine(domain, width): any { + let scale; + if (this.bandwidth === undefined) { + this.bandwidth = (this.dims.width - this.barPadding); + } + + if (this.scaleType === 'time') { + scale = scaleTime() + .range([0, width]) + .domain(domain); + } else if (this.scaleType === 'linear') { + scale = scaleLinear() + .range([0, width]) + .domain(domain); + + if (this.roundDomains) { + scale = scale.nice(); + } + } else if (this.scaleType === 'ordinal') { + scale = scalePoint() + .range([this.bandwidth / 2, width - this.bandwidth / 2]) + .domain(domain); + } + + return scale; + } + + getYScaleLine(domain, height): any { + const scale = scaleLinear() + .range([height, 0]) + .domain(domain); + + return this.roundDomains ? scale.nice() : scale; + } + + getXScale(): any { + this.xDomain = this.getXDomain(); + const spacing = this.xDomain.length / (this.dims.width / this.barPadding + 1); + return scaleBand() + .range([0, this.dims.width]) + .paddingInner(spacing) + .domain(this.xDomain); + } + + getYScale(): any { + this.yDomain = this.getYDomain(); + const scale = scaleLinear() + .range([this.dims.height, 0]) + .domain(this.yDomain); + return this.roundDomains ? scale.nice() : scale; + } + + getXDomain(): any[] { + return this.results.map(d => d.name); + } + + getYDomain() { + const values = this.results.map(d => d.value); + const min = Math.min(0, ...values); + const max = this.yScaleMax + ? Math.max(this.yScaleMax, ...values) + : Math.max(0, ...values); + if (this.yLeftAxisScaleFactor) { + const minMax = this.yLeftAxisScaleFactor(min, max); + return [Math.min(0, minMax.min), minMax.max]; + } else { + return [min, max]; + } + } + + onClick(data) { + this.select.emit(data); + } + + setColors(): void { + let domain; + if (this.schemeType === 'ordinal') { + domain = this.xDomain; + } else { + domain = this.yDomain; + } + this.colors = new ColorHelper(this.scheme, this.schemeType, domain, this.customColors); + this.colorsLine = new ColorHelper(this.colorSchemeLine, this.schemeType, domain, this.customColors); + this.colorsExtra = new ColorHelper(this.scheme, this.schemeType, domain, this.customColors); + } + + getLegendOptions() { + const opts = { + scaleType: this.schemeType, + colors: undefined, + domain: this.seriesDomain, + title: undefined, + position: this.legendPosition + }; + if (opts.scaleType === 'ordinal') { + opts.colors = this.colorsLine; + opts.title = this.legendTitle; + } else { + opts.colors = this.colors.scale; + } + return opts; + } + + getLegendOptionsExtra() { + const opts = { + scaleType: this.schemeType, + colors: this.colorsExtra, + domain: [], + title: this.legendTitle, + position: this.legendPosition + }; + opts.colors.colorDomain = []; + opts.colors.customColors = this.legendData; + this.legendData.map((item) => { + opts.colors.colorDomain.push(item.value); + opts.colors.domain.push(item.name); + opts.domain.push(item.name); + }); + return opts; + } + + updateLineWidth(width): void { + this.bandwidth = width; + } + updateYAxisWidth({ width }): void { + this.yAxisWidth = width + 20; + this.update(); + } + + updateXAxisHeight({ height }): void { + this.xAxisHeight = height; + this.update(); + } + + onActivate(item) { + const idx = this.activeEntries.findIndex(d => { + return d.name === item.name && d.value === item.value && d.series === item.series; + }); + if (idx > -1) { + return; + } + + this.activeEntries = [item, ...this.activeEntries]; + this.activate.emit({ value: item, entries: this.activeEntries }); + } + + onDeactivate(item) { + const idx = this.activeEntries.findIndex(d => { + return d.name === item.name && d.value === item.value && d.series === item.series; + }); + + this.activeEntries.splice(idx, 1); + this.activeEntries = [...this.activeEntries]; + + this.deactivate.emit({ value: item, entries: this.activeEntries }); + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.spec.ts new file mode 100644 index 0000000000..528fada25f --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.spec.ts @@ -0,0 +1,50 @@ +import { DatePipe } from '@angular/common'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; + +import { CoreModule } from '../../../../../../../core/src/core/core.module'; +import { ApplicationService } from '../../../../../../../core/src/features/applications/application.service'; +import { SharedModule } from '../../../../../../../core/src/shared/shared.module'; +import { TabNavService } from '../../../../../../../core/tab-nav.service'; +import { ApplicationServiceMock } from '../../../../../../../core/test-framework/application-service-helper'; +import { createBasicStoreModule } from '../../../../../../../core/test-framework/store-test-helper'; +import { CfAutoscalerTestingModule } from '../../../../../cf-autoscaler-testing.module'; +import { AppAutoscalerComboSeriesVerticalComponent } from './combo-series-vertical.component'; + +describe('AppAutoscalerComboSeriesVerticalComponent', () => { + let component: AppAutoscalerComboSeriesVerticalComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AppAutoscalerComboSeriesVerticalComponent], + imports: [ + BrowserAnimationsModule, + createBasicStoreModule(), + CoreModule, + SharedModule, + RouterTestingModule, + CfAutoscalerTestingModule, + NgxChartsModule, + ], + providers: [ + DatePipe, + { provide: ApplicationService, useClass: ApplicationServiceMock }, + TabNavService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AppAutoscalerComboSeriesVerticalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.ts new file mode 100644 index 0000000000..5c049fe3f1 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.ts @@ -0,0 +1,210 @@ +import { animate, style, transition, trigger } from '@angular/animations'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; + +import { AppAutoscalerMetricDataLine, AppAutoscalerMetricDataPoint } from '../../../../../store/app-autoscaler.types'; + +function formatLabel(label: any): string { + if (label instanceof Date) { + label = label.toLocaleDateString(); + } else { + label = label.toLocaleString(); + } + + return label; +} + +/* tslint:disable:component-selector */ +@Component({ + selector: 'g[ngx-combo-charts-series-vertical]', + template: ` + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + animations: [ + trigger('animationState', [ + transition('* => void', [ + style({ + opacity: 1, + transform: '*', + }), + animate(500, style({ opacity: 0, transform: 'scale(0)' })) + ]) + ]) + ] +}) +export class AppAutoscalerComboSeriesVerticalComponent implements OnChanges { + + @Input() dims; + @Input() type = 'standard'; + @Input() series; + @Input() seriesLine; + @Input() xScale; + @Input() yScale; + @Input() colors; + @Input() tooltipDisabled = false; + @Input() gradient: boolean; + @Input() activeEntries: AppAutoscalerMetricDataLine[]; + @Input() seriesName: string; + @Input() animations = true; + + @Output() select = new EventEmitter(); + @Output() activate = new EventEmitter(); + @Output() deactivate = new EventEmitter(); + @Output() bandwidth = new EventEmitter(); + + bars: any; + x: any; + y: any; + + ngOnChanges(changes): void { + this.update(); + } + + update(): void { + let width; + if (this.series.length) { + width = this.xScale.bandwidth(); + this.bandwidth.emit(width); + } + + let d0 = 0; + let total; + if (this.type === 'normalized') { + total = this.series.map(d => d.value).reduce((sum, d) => sum + d, 0); + } + + this.bars = this.series.map((d, index) => { + + let value = d.value; + const label = d.name; + const formattedLabel = formatLabel(label); + const roundEdges = this.type === 'standard'; + + const bar: any = { + value, + label, + roundEdges, + data: d, + width, + formattedLabel, + height: 0, + x: 0, + y: 0 + }; + + if (this.type === 'standard') { + bar.height = Math.abs(this.yScale(value) - this.yScale(0)); + bar.x = this.xScale(label); + + if (value < 0) { + bar.y = this.yScale(0); + } else { + bar.y = this.yScale(value); + } + } else if (this.type === 'stacked') { + const offset0 = d0; + const offset1 = offset0 + value; + d0 += value; + + bar.height = this.yScale(offset0) - this.yScale(offset1); + bar.x = 0; + bar.y = this.yScale(offset1); + bar.offset0 = offset0; + bar.offset1 = offset1; + } else if (this.type === 'normalized') { + let offset0 = d0; + let offset1 = offset0 + value; + d0 += value; + + if (total > 0) { + offset0 = (offset0 * 100) / total; + offset1 = (offset1 * 100) / total; + } else { + offset0 = 0; + offset1 = 0; + } + + bar.height = this.yScale(offset0) - this.yScale(offset1); + bar.x = 0; + bar.y = this.yScale(offset1); + bar.offset0 = offset0; + bar.offset1 = offset1; + value = (offset1 - offset0).toFixed(2); + } + + if (this.colors.scaleType === 'ordinal') { + bar.color = this.colors.getColor(label); + } else { + if (this.type === 'standard') { + bar.color = this.colors.getColor(value); + bar.gradientStops = this.colors.getLinearGradientStops(value); + } else { + bar.color = this.colors.getColor(bar.offset1); + bar.gradientStops = this.colors.getLinearGradientStops(bar.offset1, bar.offset0); + } + } + + let tooltipLabel = formattedLabel; + if (this.seriesName) { + tooltipLabel = `${this.seriesName} • ${formattedLabel}`; + } + + this.getSeriesTooltips(this.seriesLine, index); + const lineValue = this.seriesLine[0].series[index].value; + const lineName = this.seriesLine[0].series[index].name; + bar.tooltipText = ` + ${tooltipLabel} + Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}% + `; + + return bar; + }); + } + + getSeriesTooltips(seriesLine: AppAutoscalerMetricDataLine[], index): AppAutoscalerMetricDataPoint[] { + return seriesLine.map(d => { + return d.series[index]; + }); + } + + isActive(entry: AppAutoscalerMetricDataLine): boolean { + if (!this.activeEntries) { + return false; + } + const item = this.activeEntries.find(d => { + return entry.name === d.name && entry.series === d.series; + }); + return item !== undefined; + } + + onClick(data): void { + this.select.emit(data); + } + + trackBy(index, bar): string { + return bar.label; + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-data-source.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-data-source.ts new file mode 100644 index 0000000000..f797bb6dbd --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-data-source.ts @@ -0,0 +1,42 @@ +import { Store } from '@ngrx/store'; + +import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state'; +import { getRowMetadata } from '../../../../../core/src/features/cloud-foundry/cf.helpers'; +import { ListDataSource } from '../../../../../core/src/shared/components/list/data-sources-controllers/list-data-source'; +import { IListConfig } from '../../../../../core/src/shared/components/list/list.component.types'; +import { MetricsRangeSelectorService } from '../../../../../core/src/shared/services/metrics-range-selector.service'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { GetAppAutoscalerPolicyTriggerAction } from '../../../store/app-autoscaler.actions'; +import { AppScalingTrigger } from '../../../store/app-autoscaler.types'; +import { autoscalerEntityFactory } from '../../../store/autoscaler-entity-factory'; + + +export class AppAutoscalerMetricChartDataSource extends ListDataSource> { + action: any; + constructor( + store: Store, + cfGuid: string, + appGuid: string, + listConfig: IListConfig>, + metricsRangeService: MetricsRangeSelectorService + ) { + const action = new GetAppAutoscalerPolicyTriggerAction(null, appGuid, cfGuid); + super( + { + store, + action, + schema: autoscalerEntityFactory(action.entityType), + getRowUniqueId: getRowMetadata, + paginationKey: action.paginationKey, + isLocal: true, + listConfig, + refresh: () => { + if (this.metricsAction.windowValue) { + this.metricsAction = metricsRangeService.getNewTimeWindowAction(this.metricsAction, this.metricsAction.windowValue); + } + this.store.dispatch(this.metricsAction); + } + } + ); + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service.spec.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service.spec.ts new file mode 100644 index 0000000000..a3de2640d4 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service.spec.ts @@ -0,0 +1,28 @@ +import { DatePipe } from '@angular/common'; +import { inject, TestBed } from '@angular/core/testing'; + +import { + ApplicationEnvVarsHelper, +} from '../../../../../core/src/features/applications/application/application-tabs-base/tabs/build-tab/application-env-vars.service'; +import { generateTestApplicationServiceProvider } from '../../../../../core/test-framework/application-service-helper'; +import { BaseTestModules } from '../../../../../core/test-framework/cloud-foundry-endpoint-service.helper'; +import { CfAutoscalerTestingModule } from '../../../cf-autoscaler-testing.module'; +import { AppAutoscalerMetricChartListConfigService } from './app-autoscaler-metric-chart-list-config.service'; + +describe('AppAutoscalerMetricChartListConfigService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + AppAutoscalerMetricChartListConfigService, + generateTestApplicationServiceProvider('1', '1'), + ApplicationEnvVarsHelper, + DatePipe + ], + imports: [...BaseTestModules, CfAutoscalerTestingModule] + }); + }); + + it('should be created', inject([AppAutoscalerMetricChartListConfigService], (service: AppAutoscalerMetricChartListConfigService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service.ts b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service.ts new file mode 100644 index 0000000000..8894f78ec7 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-list-config.service.ts @@ -0,0 +1,101 @@ +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import * as moment from 'moment'; + +import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state'; +import { ApplicationService } from '../../../../../core/src/features/applications/application.service'; +import { ITableColumn } from '../../../../../core/src/shared/components/list/list-table/table.types'; +import { BaseCfListConfig } from '../../../../../core/src/shared/components/list/list-types/base-cf/base-cf-list-config'; +import { ListViewTypes } from '../../../../../core/src/shared/components/list/list.component.types'; +import { MetricsRangeSelectorService } from '../../../../../core/src/shared/services/metrics-range-selector.service'; +import { ITimeRange, MetricQueryType } from '../../../../../core/src/shared/services/metrics-range-selector.types'; +import { ListView } from '../../../../../store/src/actions/list.actions'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { AutoscalerConstants } from '../../../core/autoscaler-helpers/autoscaler-util'; +import { AppScalingTrigger } from '../../../store/app-autoscaler.types'; +import { + AppAutoscalerMetricChartCardComponent, +} from './app-autoscaler-metric-chart-card/app-autoscaler-metric-chart-card.component'; +import { AppAutoscalerMetricChartDataSource } from './app-autoscaler-metric-chart-data-source'; + + +@Injectable() +export class AppAutoscalerMetricChartListConfigService extends BaseCfListConfig> { + autoscalerMetricSource: AppAutoscalerMetricChartDataSource; + cardComponent = AppAutoscalerMetricChartCardComponent; + viewType = ListViewTypes.CARD_ONLY; + defaultView = 'cards' as ListView; + columns: Array>> = [ + { + columnId: 'name', + headerCell: () => 'Metric type', + cellDefinition: { + getValue: (row) => AutoscalerConstants.getMetricFromMetricId(row.metadata.guid) + }, + cellFlex: '2' + } + ]; + text = { + title: null, + noEntries: 'There are no metrics defined in the policy' + }; + + showCustomTime = true; + customTimePollingInterval = 60000; + customTimeInitialValue = '30:minute'; + customTimeWindows: ITimeRange[] = [ + { + value: '30:minute', + label: 'The past 30 minutes', + queryType: MetricQueryType.QUERY + }, + { + value: '1:hour', + label: 'The past 1 hour', + queryType: MetricQueryType.QUERY + }, + { + value: '2:hour', + label: 'The past 2 hours', + queryType: MetricQueryType.QUERY + }, + { + label: 'Custom time window', + queryType: MetricQueryType.RANGE_QUERY + } + ]; + + private twoHours = 1000 * 60 * 60 * 2; + customTimeValidation = (start: moment.Moment, end: moment.Moment) => { + if (!end || !start) { + return ' '; + } + if (!start.isBefore(end)) { + return 'Start date must be before end date.'; + } + if (end.diff(start) > this.twoHours) { + return 'Time window must be two hours or less'; + } + } + + constructor( + private store: Store, + private appService: ApplicationService, + metricsRangeService: MetricsRangeSelectorService) { + super(); + this.autoscalerMetricSource = new AppAutoscalerMetricChartDataSource( + this.store, + this.appService.cfGuid, + this.appService.appGuid, + this, + metricsRangeService + ); + } + + getGlobalActions = () => null; + getMultiActions = () => null; + getSingleActions = () => null; + getDataSource = () => this.autoscalerMetricSource; + getMultiFiltersConfigs = () => []; + getColumns = () => this.columns; +} diff --git a/src/frontend/packages/cf-autoscaler/src/store/app-autoscaler.actions.ts b/src/frontend/packages/cf-autoscaler/src/store/app-autoscaler.actions.ts new file mode 100644 index 0000000000..068406d25f --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/store/app-autoscaler.actions.ts @@ -0,0 +1,213 @@ +import { RequestOptions } from '@angular/http'; + +import { applicationEntityType } from '../../../cloud-foundry/src/cf-entity-factory'; +import { createEntityRelationPaginationKey } from '../../../store/src/helpers/entity-relations/entity-relations.types'; +import { ApiRequestTypes } from '../../../store/src/reducers/api-request-reducer/request-helpers'; +import { PaginatedAction } from '../../../store/src/types/pagination.types'; +import { IRequestAction } from '../../../store/src/types/request.types'; +import { AppAutoscalerPolicyLocal, AppScalingTrigger } from './app-autoscaler.types'; +import { + appAutoscalerAppMetricEntityType, + appAutoscalerHealthEntityType, + appAutoscalerPolicyEntityType, + appAutoscalerPolicyTriggerEntityType, + appAutoscalerScalingHistoryEntityType, + AUTOSCALER_ENDPOINT_TYPE, + autoscalerEntityFactory, +} from './autoscaler-entity-factory'; + +export const AppAutoscalerPolicyEvents = { + GET_APP_AUTOSCALER_POLICY: '[App Autoscaler] Get autoscaler policy', + GET_APP_AUTOSCALER_POLICY_SUCCESS: '[App Autoscaler] Get autoscaler policy success', + GET_APP_AUTOSCALER_POLICY_FAILED: '[App Autoscaler] Get autoscaler policy failed' +}; + +export const AppAutoscalerPolicyTriggerEvents = { + GET_APP_AUTOSCALER_POLICY: '[App Autoscaler] Get autoscaler policy trigger', + GET_APP_AUTOSCALER_POLICY_SUCCESS: '[App Autoscaler] Get autoscaler policy trigger success', + GET_APP_AUTOSCALER_POLICY_FAILED: '[App Autoscaler] Get autoscaler policy trigger failed' +}; + +export const AppAutoscalerScalingHistoryEvents = { + GET_APP_AUTOSCALER_SCALING_HISTORY: '[App Autoscaler] Get autoscaler scaling history', + GET_APP_AUTOSCALER_SCALING_HISTORY_SUCCESS: '[App Autoscaler] Get autoscaler scaling history success', + GET_APP_AUTOSCALER_SCALING_HISTORY_FAILED: '[App Autoscaler] Get autoscaler scaling history failed' +}; + +export const AppAutoscalerMetricEvents = { + GET_APP_AUTOSCALER_METRIC: '[App Autoscaler] Get autoscaler metric', + GET_APP_AUTOSCALER_METRIC_SUCCESS: '[App Autoscaler] Get autoscaler metric success', + GET_APP_AUTOSCALER_METRIC_FAILED: '[App Autoscaler] Get autoscaler metric failed' +}; + +export const APP_AUTOSCALER_POLICY = '[New App Autoscaler] Fetch policy'; +export const APP_AUTOSCALER_POLICY_TRIGGER = '[New App Autoscaler] Fetch policy trigger'; +export const UPDATE_APP_AUTOSCALER_POLICY = '[New App Autoscaler] Update policy'; +export const DETACH_APP_AUTOSCALER_POLICY = '[New App Autoscaler] Detach policy'; +export const APP_AUTOSCALER_HEALTH = '[New App Autoscaler] Fetch Health'; +export const APP_AUTOSCALER_SCALING_HISTORY = '[New App Autoscaler] Fetch Scaling History'; +export const FETCH_APP_AUTOSCALER_METRIC = '[New App Autoscaler] Fetch Metric'; + +export const UPDATE_APP_AUTOSCALER_POLICY_STEP = '[Edit Autoscaler Policy] Step'; + +export class GetAppAutoscalerHealthAction implements IRequestAction { + constructor( + public guid: string, + public endpointGuid: string, + ) { + } + type = APP_AUTOSCALER_HEALTH; + entity = autoscalerEntityFactory(appAutoscalerHealthEntityType); + entityType = appAutoscalerHealthEntityType; + endpointType = AUTOSCALER_ENDPOINT_TYPE; +} + +export class GetAppAutoscalerPolicyAction implements IRequestAction { + constructor( + public guid: string, + public endpointGuid: string, + ) { } + type = APP_AUTOSCALER_POLICY; + entity = autoscalerEntityFactory(appAutoscalerPolicyEntityType); + entityType = appAutoscalerPolicyEntityType; + endpointType = AUTOSCALER_ENDPOINT_TYPE; +} + +export class UpdateAppAutoscalerPolicyAction implements IRequestAction { + static updateKey = 'Updating-Existing-Application-Policy'; + constructor( + public guid: string, + public endpointGuid: string, + public policy: AppAutoscalerPolicyLocal, + ) { } + type = UPDATE_APP_AUTOSCALER_POLICY; + entityType = appAutoscalerPolicyEntityType; + endpointType = AUTOSCALER_ENDPOINT_TYPE; +} + +export class DetachAppAutoscalerPolicyAction implements IRequestAction { + static updateKey = 'Detaching-Existing-Application-Policy'; + constructor( + public guid: string, + public endpointGuid: string, + ) { } + type = DETACH_APP_AUTOSCALER_POLICY; + entityType = appAutoscalerPolicyEntityType; + requestType: ApiRequestTypes = 'delete'; + endpointType = AUTOSCALER_ENDPOINT_TYPE; +} + +export class GetAppAutoscalerPolicyTriggerAction implements PaginatedAction { + constructor( + public paginationKey: string, + public guid: string, + public endpointGuid: string, + public normalFormat?: boolean + ) { + this.paginationKey = this.paginationKey || createEntityRelationPaginationKey(applicationEntityType, guid); + } + actions = [ + AppAutoscalerPolicyTriggerEvents.GET_APP_AUTOSCALER_POLICY, + AppAutoscalerPolicyTriggerEvents.GET_APP_AUTOSCALER_POLICY_SUCCESS, + AppAutoscalerPolicyTriggerEvents.GET_APP_AUTOSCALER_POLICY_FAILED + ]; + type = APP_AUTOSCALER_POLICY_TRIGGER; + entity = [autoscalerEntityFactory(appAutoscalerPolicyTriggerEntityType)]; + entityType = appAutoscalerPolicyTriggerEntityType; + endpointType = AUTOSCALER_ENDPOINT_TYPE; + options: RequestOptions; + query: AutoscalerQuery = { + metric: 'policy' + }; + windowValue: string; +} + +export interface AutoscalerPaginationParams { + 'order-direction-field'?: string; + 'order-direction': 'asc' | 'desc'; + 'results-per-page': string; + 'start-time': string; + 'end-time': string; + 'page'?: string; +} + +export interface AutoscalerQuery { + metric: string; + params?: { + start: number; + end: number + }; +} + +export class GetAppAutoscalerScalingHistoryAction implements PaginatedAction { + private static sortField = 'timestamp'; + constructor( + public paginationKey: string, + public guid: string, + public endpointGuid: string, + public normalFormat?: boolean, + public params?: AutoscalerPaginationParams, + ) { + this.paginationKey = this.paginationKey || createEntityRelationPaginationKey(applicationEntityType, guid); + } + actions = [ + AppAutoscalerScalingHistoryEvents.GET_APP_AUTOSCALER_SCALING_HISTORY, + AppAutoscalerScalingHistoryEvents.GET_APP_AUTOSCALER_SCALING_HISTORY_SUCCESS, + AppAutoscalerScalingHistoryEvents.GET_APP_AUTOSCALER_SCALING_HISTORY_FAILED + ]; + type = APP_AUTOSCALER_SCALING_HISTORY; + entity = [autoscalerEntityFactory(appAutoscalerScalingHistoryEntityType)]; + entityType = appAutoscalerScalingHistoryEntityType; + endpointType = AUTOSCALER_ENDPOINT_TYPE; + options: RequestOptions; + initialParams: AutoscalerPaginationParams = { + 'order-direction-field': GetAppAutoscalerScalingHistoryAction.sortField, + 'order-direction': 'desc', + 'results-per-page': '5', + 'start-time': '0', + 'end-time': '0', + }; + query: AutoscalerQuery = { + metric: 'history' + }; + windowValue: string; +} + +export abstract class GetAppAutoscalerMetricAction implements PaginatedAction { + constructor( + public guid: string, + public endpointGuid: string, + public metricName: string, + public skipFormat: boolean, + public trigger: AppScalingTrigger, + public params: AutoscalerPaginationParams, + ) { + this.paginationKey = this.paginationKey || createEntityRelationPaginationKey(applicationEntityType, guid, metricName); + } + actions = [ + AppAutoscalerMetricEvents.GET_APP_AUTOSCALER_METRIC, + AppAutoscalerMetricEvents.GET_APP_AUTOSCALER_METRIC_SUCCESS, + AppAutoscalerMetricEvents.GET_APP_AUTOSCALER_METRIC_FAILED + ]; + url: string; + type = FETCH_APP_AUTOSCALER_METRIC; + entityType: string; + endpointType = AUTOSCALER_ENDPOINT_TYPE; + paginationKey: string; + initialParams = this.params; +} + +export class GetAppAutoscalerAppMetricAction extends GetAppAutoscalerMetricAction implements PaginatedAction { + constructor( + public guid: string, + public endpointGuid: string, + public metricName: string, + public skipFormat: boolean, + public trigger: AppScalingTrigger, + public params: AutoscalerPaginationParams, + ) { + super(guid, endpointGuid, metricName, skipFormat, trigger, params); + this.url = `apps/${guid}/metric/${metricName}`; + } + entityType = appAutoscalerAppMetricEntityType; +} diff --git a/src/frontend/packages/cf-autoscaler/src/store/app-autoscaler.types.ts b/src/frontend/packages/cf-autoscaler/src/store/app-autoscaler.types.ts new file mode 100644 index 0000000000..d7cb19c63d --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/store/app-autoscaler.types.ts @@ -0,0 +1,155 @@ +import { AutoscalerQuery } from './app-autoscaler.actions'; + +export interface AppAutoscalerPolicy { + instance_min_count: number; + instance_max_count: number; + scaling_rules?: AppScalingRule[]; + schedules?: { + timezone: string, + recurring_schedule?: AppRecurringSchedule[], + specific_date?: AppSpecificDate[] + }; +} + +export interface AppSpecificDate { + end_date_time: string; + initial_min_instance_count?: number; + instance_max_count: number; + instance_min_count: number; + start_date_time: string; +} + +export interface AppScalingRule { + adjustment: string; + breach_duration_secs?: number; + color?: string; + cool_down_secs?: number; + metric_type: string; + operator: string; + threshold: number; +} + +export interface AppScalingTrigger { + upper: AppScalingRule[]; + lower: AppScalingRule[]; + query?: AutoscalerQuery; +} + +export interface AppRecurringSchedule { + initial_min_instance_count?: number; + instance_min_count: number; + instance_max_count: number; + start_time: string; + end_time: string; + days_of_month?: number[]; + days_of_week?: number[]; + start_date?: string; + end_date?: string; +} + +export interface AppAutoscalerPolicyLocal extends AppAutoscalerPolicy { + enabled: boolean; + scaling_rules_form: AppScalingRule[]; + scaling_rules_map: { + [metricName: string]: AppScalingTrigger + }; +} + +export interface AppAutoscalerHealth { + entity: { + uptime: number; + }; +} + +export interface AppAutoscalerScalingHistory { + next_url: string; + prev_url: string; + page: number; + resources: AppAutoscalerEvent[]; + total_pages: number; + total_results: number; +} + +export interface AppAutoscalerEvent { + app_id: string; + error: string; + message: string; + new_instances: number; + old_instances: number; + reason: string; + scaling_type: number; + status: number; + timestamp: number; +} + +export interface AppAutoscalerMetricData { + app_id: string; + name: string; + timestamp: number; + unit: string; + value: string; + chartMaxValue?: string; +} + +export interface AppAutoscalerMetricDataLocal { + latest: { + target: AppAutoscalerMetricDataPoint[], + colorTarget: AppAutoscalerMetricDataPoint[] + }; + formated: { + target: AppAutoscalerMetricDataPoint[], + colorTarget: AppAutoscalerMetricDataPoint[] + }; + markline: AppAutoscalerMetricDataLine[]; + unit: string; + chartMaxValue: number; +} + +export interface AppAutoscalerMetricDataPoint { + name: string; + value: number | string; + time?: number; +} + +export interface AppAutoscalerMetricLegend { + name: string; + value: string; +} + +export interface AppAutoscalerMetricDataLine { + name: string; + series: AppAutoscalerMetricDataPoint[]; +} + +export interface AppAutoscalerMetricMapInfo { + unit_internal: string; + interval: number; +} + +export interface AppAutoscalerMetricBasicInfo { + interval: number; + unit: string; + chartMaxValue: number; +} + +export interface AppAutoscalerFetchPolicyFailedResponse { + status: number; + noPolicy: boolean; +} + +export interface AppAutoscalerInvalidPolicyError { + alertInvalidPolicyTriggerThreshold100?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyTriggerThresholdRange?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyTriggerStepRange?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyScheduleDateBeforeNow?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyScheduleEndDateBeforeStartDate?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyScheduleStartDateTimeBeforeNow?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyScheduleEndDateTimeBeforeStartDateTime?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyScheduleSpecificConflict?: AppAutoscalerInvalidPolicyErrorEntity; + alertInvalidPolicyScheduleEndDateTimeBeforeNow?: AppAutoscalerInvalidPolicyErrorEntity; +} + +export interface AppAutoscalerInvalidPolicyErrorEntity { + value?: string | number; +} + diff --git a/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-factory.ts b/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-factory.ts new file mode 100644 index 0000000000..30a256adb7 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-factory.ts @@ -0,0 +1,69 @@ +import { Schema, schema } from 'normalizr'; + +import { getAPIResourceGuid } from '../../../cloud-foundry/src/selectors/api.selectors'; +import { EntitySchema } from '../../../store/src/helpers/entity-schema'; + +export const appAutoscalerHealthEntityType = 'autoscalerHealth'; +export const appAutoscalerPolicyEntityType = 'autoscalerPolicy'; +export const appAutoscalerPolicyTriggerEntityType = 'autoscalerPolicyTrigger'; +export const appAutoscalerScalingHistoryEntityType = 'autoscalerScalingHistory'; +export const appAutoscalerAppMetricEntityType = 'autoscalerAppMetric'; + +export const AUTOSCALER_ENDPOINT_TYPE = 'autoscaler'; + +const entityCache: { + [key: string]: EntitySchema +} = {}; + +export class AutoscalerEntitySchema extends EntitySchema { + /** + * @param entityKey As per schema.Entity ctor + * @param [definition] As per schema.Entity ctor + * @param [options] As per schema.Entity ctor + * @param [relationKey] Allows multiple children of the same type within a single parent entity. For instance user with developer + * spaces, manager spaces, auditor space, etc + */ + constructor( + entityKey: string, + definition?: Schema, + options?: schema.EntityOptions, + relationKey?: string + ) { + super(entityKey, AUTOSCALER_ENDPOINT_TYPE, definition, options, relationKey); + } +} + +entityCache[appAutoscalerPolicyEntityType] = new AutoscalerEntitySchema( + appAutoscalerPolicyEntityType, + {}, + { idAttribute: getAPIResourceGuid } +); + +entityCache[appAutoscalerPolicyTriggerEntityType] = new AutoscalerEntitySchema( + appAutoscalerPolicyTriggerEntityType, + {}, + { idAttribute: getAPIResourceGuid } +); +entityCache[appAutoscalerHealthEntityType] = new AutoscalerEntitySchema( + appAutoscalerHealthEntityType, + {}, + { idAttribute: getAPIResourceGuid } +); +entityCache[appAutoscalerScalingHistoryEntityType] = new AutoscalerEntitySchema( + appAutoscalerScalingHistoryEntityType, + {}, + { idAttribute: getAPIResourceGuid } +); +entityCache[appAutoscalerAppMetricEntityType] = new AutoscalerEntitySchema( + appAutoscalerAppMetricEntityType, + {}, + { idAttribute: getAPIResourceGuid } +); + +export function autoscalerEntityFactory(key: string): EntitySchema { + const entity = entityCache[key]; + if (!entity) { + throw new Error(`Unknown entity schema type: ${key}`); + } + return entity; +} diff --git a/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts b/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts new file mode 100644 index 0000000000..f029ff4a19 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts @@ -0,0 +1,92 @@ +import { + StratosBaseCatalogueEntity, + StratosCatalogueEntity, +} from '../../../core/src/core/entity-catalogue/entity-catalogue-entity'; +import { entityCatalogue } from '../../../core/src/core/entity-catalogue/entity-catalogue.service'; +import { IStratosEndpointDefinition } from '../../../core/src/core/entity-catalogue/entity-catalogue.types'; +import { APIResource } from '../../../store/src/types/api.types'; +import { IFavoriteMetadata } from '../../../store/src/types/user-favorites.types'; +import { + AppAutoscalerHealth, + AppAutoscalerPolicy, + AppAutoscalerScalingHistory, + AppScalingTrigger, +} from './app-autoscaler.types'; +import { + appAutoscalerAppMetricEntityType, + appAutoscalerHealthEntityType, + appAutoscalerPolicyEntityType, + appAutoscalerPolicyTriggerEntityType, + appAutoscalerScalingHistoryEntityType, + AUTOSCALER_ENDPOINT_TYPE, + autoscalerEntityFactory, +} from './autoscaler-entity-factory'; + +export function registerAutoscalerEntities() { + generateCFEntities().forEach(entity => entityCatalogue.register(entity)); +} + +export function generateCFEntities(): StratosBaseCatalogueEntity[] { + // TODO: Q Should autoscaler have an endpoint type? Should it match cf? + const endpointDefinition = { + type: AUTOSCALER_ENDPOINT_TYPE, + label: 'Cloud Foundry', + labelPlural: 'Cloud Foundry', + icon: 'cloud_foundry', + iconFont: 'stratos-icons', + logoUrl: '/core/assets/endpoint-icons/cloudfoundry.png', + authTypes: [], + } as IStratosEndpointDefinition; + return [ + generatePolicyEntity(endpointDefinition), + generatePolicyTriggerEntity(endpointDefinition), + generateHealthEntity(endpointDefinition), + generateScalingEntity(endpointDefinition), + generateMetricEntity(endpointDefinition), + ]; +} + +function generatePolicyEntity(endpointDefinition: IStratosEndpointDefinition) { + const definition = { + type: appAutoscalerPolicyEntityType, + schema: autoscalerEntityFactory(appAutoscalerPolicyEntityType), + endpoint: endpointDefinition + }; + return new StratosCatalogueEntity>(definition); +} + +function generatePolicyTriggerEntity(endpointDefinition: IStratosEndpointDefinition) { + const definition = { + type: appAutoscalerPolicyTriggerEntityType, + schema: autoscalerEntityFactory(appAutoscalerPolicyTriggerEntityType), + endpoint: endpointDefinition + }; + return new StratosCatalogueEntity>(definition); +} + +function generateHealthEntity(endpointDefinition: IStratosEndpointDefinition) { + const definition = { + type: appAutoscalerHealthEntityType, + schema: autoscalerEntityFactory(appAutoscalerHealthEntityType), + endpoint: endpointDefinition + }; + return new StratosCatalogueEntity>(definition); +} + +function generateScalingEntity(endpointDefinition: IStratosEndpointDefinition) { + const definition = { + type: appAutoscalerScalingHistoryEntityType, + schema: autoscalerEntityFactory(appAutoscalerScalingHistoryEntityType), + endpoint: endpointDefinition + }; + return new StratosCatalogueEntity>(definition); +} + +function generateMetricEntity(endpointDefinition: IStratosEndpointDefinition) { + const definition = { + type: appAutoscalerAppMetricEntityType, + schema: autoscalerEntityFactory(appAutoscalerAppMetricEntityType), + endpoint: endpointDefinition + }; + return new StratosCatalogueEntity>(definition); // TODO: RC any +} diff --git a/src/frontend/packages/cf-autoscaler/src/store/autoscaler.effects.ts b/src/frontend/packages/cf-autoscaler/src/store/autoscaler.effects.ts new file mode 100644 index 0000000000..5a67ee8392 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/store/autoscaler.effects.ts @@ -0,0 +1,440 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http, Request, RequestOptions, URLSearchParams } from '@angular/http'; +import { Actions, Effect, ofType } from '@ngrx/effects'; +import { Action, Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { catchError, mergeMap, withLatestFrom } from 'rxjs/operators'; + +import { entityCatalogue } from '../../../core/src/core/entity-catalogue/entity-catalogue.service'; +import { environment } from '../../../core/src/environments/environment'; +import { AppState } from '../../../store/src/app-state'; +import { + resultPerPageParam, + resultPerPageParamDefault, +} from '../../../store/src/reducers/pagination-reducer/pagination-reducer.types'; +import { selectPaginationState } from '../../../store/src/selectors/pagination.selectors'; +import { APIResource, NormalizedResponse, PaginationResponse } from '../../../store/src/types/api.types'; +import { PaginatedAction, PaginationEntityState, PaginationParam } from '../../../store/src/types/pagination.types'; +import { + StartRequestAction, + WrapperRequestActionFailed, + WrapperRequestActionSuccess, +} from '../../../store/src/types/request.types'; +import { buildMetricData } from '../core/autoscaler-helpers/autoscaler-transform-metric'; +import { + autoscalerTransformArrayToMap, + autoscalerTransformMapToArray, +} from '../core/autoscaler-helpers/autoscaler-transform-policy'; +import { AutoscalerConstants } from '../core/autoscaler-helpers/autoscaler-util'; +import { + APP_AUTOSCALER_HEALTH, + APP_AUTOSCALER_POLICY, + APP_AUTOSCALER_POLICY_TRIGGER, + APP_AUTOSCALER_SCALING_HISTORY, + AutoscalerPaginationParams, + AutoscalerQuery, + DETACH_APP_AUTOSCALER_POLICY, + DetachAppAutoscalerPolicyAction, + FETCH_APP_AUTOSCALER_METRIC, + GetAppAutoscalerHealthAction, + GetAppAutoscalerMetricAction, + GetAppAutoscalerPolicyAction, + GetAppAutoscalerPolicyTriggerAction, + GetAppAutoscalerScalingHistoryAction, + UPDATE_APP_AUTOSCALER_POLICY, + UpdateAppAutoscalerPolicyAction, +} from './app-autoscaler.actions'; +import { + AppAutoscalerEvent, + AppAutoscalerFetchPolicyFailedResponse, + AppAutoscalerMetricData, + AppAutoscalerMetricDataLocal, + AppAutoscalerPolicyLocal, + AppScalingTrigger, +} from './app-autoscaler.types'; + +const { proxyAPIVersion } = environment; +const commonPrefix = `/pp/${proxyAPIVersion}/autoscaler`; + +function createAutoscalerRequestMessage(requestType: string, error: { status: string, _body: string }) { + return `Unable to ${requestType}: ${error.status} ${error._body}`; +} + +@Injectable() +export class AutoscalerEffects { + constructor( + private http: Http, + private actions$: Actions, + private store: Store, + ) { } + + @Effect() + fetchAppAutoscalerHealth$ = this.actions$.pipe( + ofType(APP_AUTOSCALER_HEALTH), + mergeMap(action => { + const actionType = 'fetch'; + this.store.dispatch(new StartRequestAction(action, actionType)); + const options = new RequestOptions(); + options.url = `${commonPrefix}/health`; + options.method = 'get'; + options.headers = this.addHeaders(action.endpointGuid); + return this.http + .request(new Request(options)).pipe( + mergeMap(response => { + const entity = entityCatalogue.getEntity(action); + const healthInfo = response.json(); + const mappedData = { + entities: { [entity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + this.transformData(entity.entityKey, mappedData, action.guid, healthInfo); + return [ + new WrapperRequestActionSuccess(mappedData, action, actionType) + ]; + }), + catchError(err => [ + new WrapperRequestActionFailed(createAutoscalerRequestMessage('fetch health info', err), action, actionType) + ])); + })); + + @Effect() + updateAppAutoscalerPolicy$ = this.actions$.pipe( + ofType(UPDATE_APP_AUTOSCALER_POLICY), + mergeMap(action => { + const actionType = 'update'; + this.store.dispatch(new StartRequestAction(action, actionType)); + const options = new RequestOptions(); + options.url = `${commonPrefix}/apps/${action.guid}/policy`; + options.method = 'put'; + options.headers = this.addHeaders(action.endpointGuid); + options.body = autoscalerTransformMapToArray(action.policy); + return this.http + .request(new Request(options)).pipe( + mergeMap(response => { + const policyInfo = autoscalerTransformArrayToMap(response.json()); + const entity = entityCatalogue.getEntity(action); + const mappedData = { + entities: { [entity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + this.transformData(entity.entityKey, mappedData, action.guid, policyInfo); + return [ + new WrapperRequestActionSuccess(mappedData, action, actionType) + ]; + }), + catchError(err => [ + new WrapperRequestActionFailed(createAutoscalerRequestMessage('update policy', err), action, actionType) + ])); + })); + + @Effect() + getAppAutoscalerPolicy$ = this.actions$.pipe( + ofType(APP_AUTOSCALER_POLICY), + mergeMap(action => this.fetchPolicy(action)) + ); + + @Effect() + detachAppAutoscalerPolicy$ = this.actions$.pipe( + ofType(DETACH_APP_AUTOSCALER_POLICY), + mergeMap(action => { + const actionType = 'delete'; + this.store.dispatch(new StartRequestAction(action, actionType)); + const options = new RequestOptions(); + options.url = `${commonPrefix}/apps/${action.guid}/policy`; + options.method = 'delete'; + options.headers = this.addHeaders(action.endpointGuid); + return this.http + .request(new Request(options)).pipe( + mergeMap(response => { + const entity = entityCatalogue.getEntity(action); + const mappedData = { + entities: { [entity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + this.transformData(entity.entityKey, mappedData, action.guid, { enabled: false }); + return [ + new WrapperRequestActionSuccess(mappedData, action, actionType) + ]; + }), + catchError(err => [ + new WrapperRequestActionFailed(createAutoscalerRequestMessage('detach policy', err), action, actionType) + ])); + })); + + @Effect() + fetchAppAutoscalerPolicyTrigger$ = this.actions$.pipe( + ofType(APP_AUTOSCALER_POLICY_TRIGGER), + mergeMap(action => this.fetchPolicy(new GetAppAutoscalerPolicyAction(action.guid, action.endpointGuid), action)) + ); + + @Effect() + fetchAppAutoscalerScalingHistory$ = this.actions$.pipe( + ofType(APP_AUTOSCALER_SCALING_HISTORY), + withLatestFrom(this.store), + mergeMap(([action, state]) => { + const actionType = 'fetch'; + const paginatedAction = action as PaginatedAction; + this.store.dispatch(new StartRequestAction(action, actionType)); + const options = new RequestOptions(); + options.url = `${commonPrefix}/apps/${action.guid}/event`; + options.method = 'get'; + options.headers = this.addHeaders(action.endpointGuid); + const entity = entityCatalogue.getEntity(action); + // Set params from store + const paginationState = selectPaginationState( + entity.entityKey, + paginatedAction.paginationKey, + )(state); + const paginationParams = this.getPaginationParams(paginationState); + paginatedAction.pageNumber = paginationState + ? paginationState.currentPage + : 1; + const { metricConfig, ...trimmedPaginationParams } = paginationParams; + options.params = this.buildParams(action.initialParams, trimmedPaginationParams, action.params); + if (!options.params.has(resultPerPageParam)) { + options.params.set( + resultPerPageParam, + resultPerPageParamDefault.toString(), + ); + } + if (options.params.has('order-direction-field')) { + options.params.delete('order-direction-field'); + } + if (options.params.has('order-direction')) { + options.params.set('order', options.params.get('order-direction')); + options.params.delete('order-direction'); + } + if (metricConfig && metricConfig.params) { + options.params.set('start-time', metricConfig.params.start + '000000000'); + options.params.set('end-time', metricConfig.params.end + '000000000'); + } else if (action.query && action.query.params) { + options.params.set('start-time', action.query.params.start + '000000000'); + options.params.set('end-time', action.query.params.end + '000000000'); + } + return this.http + .request(new Request(options)).pipe( + mergeMap(response => { + const histories = response.json(); + const mappedData = { + entities: { [entity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + if (action.normalFormat) { + this.transformData(entity.entityKey, mappedData, action.guid, histories); + } else { + this.transformEventData(entity.entityKey, mappedData, action.guid, histories); + } + return [ + new WrapperRequestActionSuccess(mappedData, action, actionType, histories.total_results, histories.total_pages) + ]; + }), + catchError(err => [ + new WrapperRequestActionFailed(createAutoscalerRequestMessage('fetch scaling history', err), action, actionType) + ])); + })); + + @Effect() + fetchAppAutoscalerAppMetric$ = this.actions$.pipe( + ofType(FETCH_APP_AUTOSCALER_METRIC), + mergeMap(action => { + const actionType = 'fetch'; + this.store.dispatch(new StartRequestAction(action, actionType)); + const options = new RequestOptions(); + options.url = `${commonPrefix}/${action.url}`; + options.method = 'get'; + options.headers = this.addHeaders(action.endpointGuid); + options.params = this.buildParams(action.initialParams, action.params); + if (options.params.has('order-direction')) { + options.params.set('order', options.params.get('order-direction')); + options.params.delete('order-direction'); + } + const entity = entityCatalogue.getEntity(action); + return this.http + .request(new Request(options)).pipe( + mergeMap(response => { + const data: PaginationResponse = response.json(); + const mappedData = { + entities: { [entity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + this.addMetric( + entity.entityKey, mappedData, action.guid, action.metricName, data, parseInt(action.initialParams['start-time'], 10), + parseInt(action.initialParams['end-time'], 10), action.skipFormat, action.trigger); + return [ + new WrapperRequestActionSuccess(mappedData, action, actionType) + ]; + }), + catchError(err => [ + new WrapperRequestActionFailed(createAutoscalerRequestMessage('fetch metrics', err), action, actionType) + ])); + })); + + private fetchPolicy( + getPolicyAction: GetAppAutoscalerPolicyAction, + getPolicyTriggerAction?: GetAppAutoscalerPolicyTriggerAction): Observable { + const actionType = 'fetch'; + this.store.dispatch(new StartRequestAction(getPolicyAction, actionType)); + const options = new RequestOptions(); + options.url = `${commonPrefix}/apps/${getPolicyAction.guid}/policy`; + options.method = 'get'; + options.headers = this.addHeaders(getPolicyAction.endpointGuid); + return this.http + .request(new Request(options)).pipe( + mergeMap(response => { + const actionEntity = entityCatalogue.getEntity(getPolicyAction); + const policyInfo = autoscalerTransformArrayToMap(response.json()); + const mappedData = { + entities: { [actionEntity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + this.transformData(actionEntity.entityKey, mappedData, getPolicyAction.guid, policyInfo); + + const res = [ + new WrapperRequestActionSuccess(mappedData, getPolicyAction, actionType) + ]; + + if (getPolicyTriggerAction) { + const triggerEntity = entityCatalogue.getEntity(getPolicyTriggerAction); + const mappedPolicyData = { + entities: { [triggerEntity.entityKey]: {} }, + result: [] + } as NormalizedResponse; + this.transformTriggerData( + triggerEntity.entityKey, + mappedPolicyData, + policyInfo, + getPolicyTriggerAction.query, + getPolicyAction.guid + ); + res.push( + new WrapperRequestActionSuccess( + mappedPolicyData, + getPolicyTriggerAction, + actionType, + Object.keys(policyInfo.scaling_rules_map).length, + 1) + ); + } + return res; + }), + catchError(err => { + const noPolicy = err.status === 404 && err._body === '{}'; + if (noPolicy) { + err._body = 'No policy is defined for this application.'; + } + const response: AppAutoscalerFetchPolicyFailedResponse = { status: err.status, noPolicy }; + + return [ + new WrapperRequestActionFailed(createAutoscalerRequestMessage('fetch policy', err), getPolicyAction, actionType, null, response) + ]; + })); + } + + addMetric( + schemaKey: string, + mappedData: NormalizedResponse>, + appId: string, + metricName: string, + data: PaginationResponse, + startTime: number, + endTime: number, + skipFormat: boolean, + trigger: AppScalingTrigger + ) { + const id = AutoscalerConstants.createMetricId(appId, metricName); + + mappedData.entities[schemaKey][id] = { + entity: buildMetricData(metricName, data, startTime, endTime, skipFormat, trigger), + metadata: { + guid: id, + created_at: null, + updated_at: null, + url: null + } + }; + mappedData.result.push(id); + } + + transformData(key: string, mappedData: NormalizedResponse, appGuid: string, data: any) { + mappedData.entities[key][appGuid] = { + entity: data, + metadata: { + guid: appGuid + } + }; + mappedData.result.push(appGuid); + } + + transformEventData(key: string, mappedData: NormalizedResponse, appGuid: string, data: PaginationResponse) { + mappedData.entities[key] = []; + data.resources.forEach((item) => { + const id = AutoscalerConstants.createMetricId(appGuid, item.timestamp + ''); + mappedData.entities[key][id] = { + entity: item, + metadata: { + created_at: item.timestamp, + guid: id, + updated_at: item.timestamp + } + }; + }); + mappedData.result = Object.keys(mappedData.entities[key]); + } + + transformTriggerData( + key: string, mappedData: NormalizedResponse, data: AppAutoscalerPolicyLocal, query: AutoscalerQuery, appGuid: string) { + mappedData.entities[key] = Object.keys(data.scaling_rules_map).reduce((entity, metricType) => { + const id = AutoscalerConstants.createMetricId(appGuid, metricType); + data.scaling_rules_map[metricType].query = query; + entity[id] = { + entity: data.scaling_rules_map[metricType], + metadata: { + guid: id + } + }; + return entity; + }, []); + mappedData.result = Object.keys(mappedData.entities[key]); + } + + addHeaders(cfGuid: string) { + const headers = new Headers(); + headers.set('x-cap-api-host', 'autoscaler'); + headers.set('x-cap-passthrough', 'true'); + headers.set('x-cap-cnsi-list', cfGuid); + return headers; + } + + buildParams(initialParams: AutoscalerPaginationParams, params?: PaginationParam, paginationParams?: AutoscalerPaginationParams) { + const searchParams = new URLSearchParams(); + if (initialParams) { + Object.keys(initialParams).forEach((key) => { + searchParams.set(key, initialParams[key]); + }); + } + if (params) { + Object.keys(params).forEach((key) => { + searchParams.set(key, params[key]); + }); + } + if (paginationParams) { + Object.keys(paginationParams).forEach((key) => { + searchParams.set(key, paginationParams[key]); + }); + } + return searchParams; + } + + getPaginationParams(paginationState: PaginationEntityState): PaginationParam { + return paginationState + ? { + ...paginationState.params, + q: [ + ...(paginationState.params.q || []) + ], + page: paginationState.currentPage.toString(), + } + : {}; + } + +} diff --git a/src/frontend/packages/cf-autoscaler/src/store/autoscaler.store.module.ts b/src/frontend/packages/cf-autoscaler/src/store/autoscaler.store.module.ts new file mode 100644 index 0000000000..ce294027fa --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/store/autoscaler.store.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; + +import { CoreModule } from '../../../core/src/core/core.module'; + +@NgModule({ + imports: [ + CoreModule + ] +}) +export class AutoscalerStoreModule { } diff --git a/src/frontend/packages/cf-autoscaler/src/styles.scss b/src/frontend/packages/cf-autoscaler/src/styles.scss new file mode 100644 index 0000000000..8edf850b82 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/styles.scss @@ -0,0 +1,7 @@ +.autoscaler-policy-edit-specific-left { + .form-field-left { + .mat-form-field-infix { + width: unset; + } + } +} diff --git a/src/frontend/packages/cf-autoscaler/src/test.ts b/src/frontend/packages/cf-autoscaler/src/test.ts new file mode 100644 index 0000000000..18653a67a2 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/src/test.ts @@ -0,0 +1,36 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files +import 'core-js/es7/reflect'; +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; + +import { APP_BASE_HREF } from '@angular/common'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + + +declare const require: any; + +// First, initialize the Angular testing environment. +const testBed = getTestBed(); +testBed.initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); + +beforeEach(() => { + testBed.configureTestingModule({ + providers: [{ provide: APP_BASE_HREF, useValue: '/' }] + }); +}); + +/** + * Bump up the Jasmine timeout from 5 seconds + */ +beforeAll(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; +}); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/src/frontend/packages/cf-autoscaler/tsconfig.lib.json b/src/frontend/packages/cf-autoscaler/tsconfig.lib.json new file mode 100644 index 0000000000..29e640092d --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.lib.json", + "compilerOptions": { + "outDir": "../../../../out-tsc/lib", + }, + "exclude": [ + "./src/test.ts", + "**/*.spec.ts", + ] +} diff --git a/src/frontend/packages/cf-autoscaler/tsconfig.spec.json b/src/frontend/packages/cf-autoscaler/tsconfig.spec.json new file mode 100644 index 0000000000..cb4a7be918 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../tsconfig.spec.json", + "compilerOptions": { + "outDir": "../../../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/src/frontend/packages/cf-autoscaler/tslint.json b/src/frontend/packages/cf-autoscaler/tslint.json new file mode 100644 index 0000000000..a097b9ebb4 --- /dev/null +++ b/src/frontend/packages/cf-autoscaler/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + ["app", "lib"], + "camelCase" + ], + "component-selector": [ + true, + "element", + ["app", "lib"], + "kebab-case" + ] + } +} diff --git a/src/frontend/packages/cloud-foundry/src/actions/domains.actions.ts b/src/frontend/packages/cloud-foundry/src/actions/domains.actions.ts index 8d05498c9e..6ab92b89eb 100644 --- a/src/frontend/packages/cloud-foundry/src/actions/domains.actions.ts +++ b/src/frontend/packages/cloud-foundry/src/actions/domains.actions.ts @@ -19,7 +19,7 @@ export class FetchDomain extends CFStartAction implements ICFAction { constructor(public guid: string, public endpointGuid: string) { super(); this.options = new RequestOptions(); - this.options.url = `shared_domains/${guid}`; + this.options.url = `domains/${guid}`; this.options.method = 'get'; this.options.params = new URLSearchParams(); } @@ -32,7 +32,7 @@ export class FetchAllDomains extends CFStartAction implements PaginatedAction { constructor(public endpointGuid: string, public paginationKey: string = null, public flattenPagination = true) { super(); this.options = new RequestOptions(); - this.options.url = 'shared_domains'; + this.options.url = 'domains'; this.options.method = 'get'; this.options.params = new URLSearchParams(); this.paginationKey = this.paginationKey || createEntityRelationPaginationKey(endpointSchemaKey, endpointGuid); diff --git a/src/frontend/packages/cloud-foundry/src/actions/organization.actions.ts b/src/frontend/packages/cloud-foundry/src/actions/organization.actions.ts index fae4145602..331135bbcb 100644 --- a/src/frontend/packages/cloud-foundry/src/actions/organization.actions.ts +++ b/src/frontend/packages/cloud-foundry/src/actions/organization.actions.ts @@ -2,15 +2,24 @@ import { RequestMethod, RequestOptions, URLSearchParams } from '@angular/http'; import { IUpdateOrganization } from '../../../core/src/core/cf-api.types'; import { getActions } from '../../../store/src/actions/action.helper'; +import { entityFactory } from '../../../store/src/helpers/entity-factory'; import { + createEntityRelationPaginationKey, EntityInlineChildAction, EntityInlineParentAction, } from '../../../store/src/helpers/entity-relations/entity-relations.types'; import { PaginatedAction } from '../../../store/src/types/pagination.types'; import { ICFAction } from '../../../store/src/types/request.types'; import { CFEntityConfig } from '../../cf-types'; +import { + cfEntityFactory, + cfUserEntityType, + domainEntityType, + organizationEntityType, + spaceEntityType, + spaceWithOrgEntityType, +} from '../cf-entity-factory'; import { CFStartAction } from './cf-action.types'; -import { cfEntityFactory, cfUserEntityType, organizationEntityType, spaceEntityType, spaceWithOrgEntityType } from '../cf-entity-factory'; import { createDefaultUserRelations } from './user.actions.helpers'; export const GET_ORGANIZATION = '[Organization] Get one'; @@ -25,6 +34,10 @@ export const GET_ORGANIZATION_SPACES = '[Space] Get all org spaces'; export const GET_ORGANIZATION_SPACES_SUCCESS = '[Space] Get all org spaces success'; export const GET_ORGANIZATION_SPACES_FAILED = '[Space] Get all org spaces failed'; +export const GET_ORGANIZATION_DOMAINS = '[Organization] Get all org domains'; +export const GET_ORGANIZATION_DOMAINS_SUCCESS = '[Organization] Get all org domains success'; +export const GET_ORGANIZATION_DOMAINS_FAILED = '[Organization] Get all org domains failed'; + export const DELETE_ORGANIZATION = '[Organization] Delete organization'; export const DELETE_ORGANIZATION_SUCCESS = '[Organization] Delete organization success'; export const DELETE_ORGANIZATION_FAILED = '[Organization] Delete organization failed'; @@ -90,6 +103,37 @@ export class GetAllOrganizationSpacesWithOrgs extends GetAllOrganizationSpaces { schemaKey = spaceWithOrgEntityType; } +export class GetAllOrganizationDomains extends CFStartAction implements PaginatedAction, EntityInlineParentAction, EntityInlineChildAction { + constructor( + public orgGuid: string, + public endpointGuid: string, + public paginationKey: string = null, + public includeRelations = [], + public populateMissing = true + ) { + super(); + if (!this.paginationKey) { + this.paginationKey = createEntityRelationPaginationKey(organizationEntityType, orgGuid); + } + this.options = new RequestOptions(); + this.options.url = `organizations/${orgGuid}/domains`; + this.options.method = 'get'; + this.parentGuid = orgGuid; + } + actions = [GET_ORGANIZATION_DOMAINS, GET_ORGANIZATION_DOMAINS_SUCCESS, GET_ORGANIZATION_DOMAINS_FAILED]; + entity = entityFactory(domainEntityType); + entityType = domainEntityType; + options: RequestOptions; + flattenPagination = true; + initialParams = { + 'results-per-page': 100, + 'order-direction': 'desc', + 'order-direction-field': 'name' + }; + parentGuid: string; + parentEntityConfig = new CFEntityConfig(organizationEntityType); +} + export class GetAllOrganizations extends CFStartAction implements PaginatedAction, EntityInlineParentAction { constructor( public paginationKey: string, diff --git a/src/frontend/packages/cloud-foundry/src/actions/quota-definitions.actions.ts b/src/frontend/packages/cloud-foundry/src/actions/quota-definitions.actions.ts index 683ec61524..507856be51 100644 --- a/src/frontend/packages/cloud-foundry/src/actions/quota-definitions.actions.ts +++ b/src/frontend/packages/cloud-foundry/src/actions/quota-definitions.actions.ts @@ -1,6 +1,9 @@ import { RequestMethod, RequestOptions, URLSearchParams } from '@angular/http'; -import { EntityInlineChildAction } from '../../../store/src/helpers/entity-relations/entity-relations.types'; +import { + EntityInlineChildAction, + EntityInlineParentAction, +} from '../../../store/src/helpers/entity-relations/entity-relations.types'; import { PaginatedAction } from '../../../store/src/types/pagination.types'; import { ICFAction } from '../../../store/src/types/request.types'; import { CFEntityConfig } from '../../cf-types'; @@ -12,6 +15,15 @@ import { } from '../cf-entity-factory'; import { CFStartAction } from './cf-action.types'; + +export const GET_QUOTA_DEFINITION = '[QuotaDefinition] Get one'; +export const GET_QUOTA_DEFINITION_SUCCESS = '[QuotaDefinition] Get one success'; +export const GET_QUOTA_DEFINITION_FAILED = '[QuotaDefinition] Get one failed'; + +export const GET_SPACE_QUOTA_DEFINITION = '[SpaceQuotaDefinition] Get one'; +export const GET_SPACE_QUOTA_DEFINITION_SUCCESS = '[QuotaDefinition] Get one success'; +export const GET_SPACE_QUOTA_DEFINITION_FAILED = '[QuotaDefinition] Get one failed'; + export const GET_QUOTA_DEFINITIONS = '[QuotaDefinitions] Get all'; export const GET_QUOTA_DEFINITIONS_SUCCESS = '[QuotaDefinitions] Get all success'; export const GET_QUOTA_DEFINITIONS_FAILED = '[QuotaDefinitions] Get all failed'; @@ -28,6 +40,9 @@ export const DISASSOCIATE_SPACE_QUOTA_DEFINITION = '[QuotaDefinitions] Disassoci export const DISASSOCIATE_SPACE_QUOTA_DEFINITION_SUCCESS = '[QuotaDefinitions] Disassociate space quota definition success'; export const DISASSOCIATE_SPACE_QUOTA_DEFINITION_FAILED = '[QuotaDefinitions] Disassociate space quota definition failed'; +// const quotaDefinitionEntitySchema = entityFactory(quotaDefinitionSchemaKey); +// const spaceQuotaEntitySchema = entityFactory(spaceQuotaSchemaKey); + export class GetQuotaDefinitions extends CFStartAction implements PaginatedAction { constructor( public paginationKey: string, @@ -56,6 +71,40 @@ export class GetQuotaDefinitions extends CFStartAction implements PaginatedActio flattenPagination = true; } +export class GetQuotaDefinition extends CFStartAction implements ICFAction, EntityInlineParentAction { + constructor(public guid: string, public endpointGuid: string, public includeRelations = [], public populateMissing = true) { + super(); + this.options = new RequestOptions(); + this.options.url = `quota_definitions/${guid}`; + this.options.method = RequestMethod.Get; + } + actions = [ + GET_QUOTA_DEFINITION, + GET_QUOTA_DEFINITION_SUCCESS, + GET_QUOTA_DEFINITION_FAILED + ]; + entity = [cfEntityFactory(quotaDefinitionEntityType)]; + entityType = quotaDefinitionEntityType; + options: RequestOptions; +} + +export class GetSpaceQuotaDefinition extends CFStartAction implements ICFAction, EntityInlineParentAction { + constructor(public guid: string, public endpointGuid: string, public includeRelations = [], public populateMissing = true) { + super(); + this.options = new RequestOptions(); + this.options.url = `space_quota_definitions/${guid}`; + this.options.method = RequestMethod.Get; + } + actions = [ + GET_SPACE_QUOTA_DEFINITION, + GET_SPACE_QUOTA_DEFINITION_SUCCESS, + GET_SPACE_QUOTA_DEFINITION_FAILED + ]; + entity = [cfEntityFactory(spaceQuotaEntityType)]; + entityType = spaceQuotaEntityType; + options: RequestOptions; +} + export class GetOrganizationSpaceQuotaDefinitions extends CFStartAction implements PaginatedAction, EntityInlineChildAction { parentGuid: string; diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-factory.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-factory.ts index 944b9fb424..5f1b1441ef 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-factory.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-factory.ts @@ -42,7 +42,7 @@ export const serviceInstancesWithNoBindingsEntityType = 'serviceInstanceWithNoBi export const serviceBindingNoBindingsEntityType = 'serviceBindingNoBindings'; -export const entityCache: { +const entityCache: { [key: string]: EntitySchema } = {}; @@ -381,7 +381,3 @@ export function cfEntityFactory(key: string): EntitySchema { } return entity; } - -export function addEntityToCache(entitySchema: EntitySchema, key?: string) { - entityCache[key || entitySchema.key] = entitySchema; -} diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 98dcfb1d08..175b2f12c7 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -120,7 +120,7 @@ export function registerCFEntities() { generateCFEntities().forEach(entity => entityCatalogue.register(entity)); } -export function generateCFEntities(): StratosBaseCatalogueEntity[] { +function generateCFEntities(): StratosBaseCatalogueEntity[] { const endpointDefinition = { type: CF_ENDPOINT_TYPE, label: 'Cloud Foundry', diff --git a/src/frontend/packages/core/sass/_all-theme.scss b/src/frontend/packages/core/sass/_all-theme.scss index b66faa691e..e5d24e322e 100644 --- a/src/frontend/packages/core/sass/_all-theme.scss +++ b/src/frontend/packages/core/sass/_all-theme.scss @@ -26,6 +26,7 @@ @import '../src/shared/components/log-viewer/log-viewer.component.theme'; @import '../src/shared/components/chips/chips.component.theme'; @import '../src/shared/components/cards/card-number-metric/card-number-metric.component.theme'; +@import '../src/shared/components/cards/card-boolean-metric/card-boolean-metric.component.theme'; @import '../src/shared/components/simple-usage-chart/simple-usage-chart.component.theme'; @import '../src/core/dot-content/dot-content.component.theme'; @import '../src/shared/components/stratos-title/stratos-title.component.theme'; @@ -119,6 +120,7 @@ $side-nav-light-active: #484848; @include app-deploy-app-theme($theme, $app-theme); @include app-cloud-foundry-firehose-theme($theme, $app-theme); @include app-card-number-metric-theme($theme, $app-theme); + @include app-card-boolean-metric-theme($theme, $app-theme); @include app-dot-content($theme, $app-theme); @include stratos-title-component-theme($theme, $app-theme); @include app-page-header-theme($theme, $app-theme); diff --git a/src/frontend/packages/core/src/app.module.ts b/src/frontend/packages/core/src/app.module.ts index 790736aecf..4d39726425 100644 --- a/src/frontend/packages/core/src/app.module.ts +++ b/src/frontend/packages/core/src/app.module.ts @@ -6,6 +6,7 @@ import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router import { Store } from '@ngrx/store'; import { debounceTime, filter, withLatestFrom } from 'rxjs/operators'; +import { CfAutoscalerModule } from '../../cf-autoscaler/src/cf-autoscaler.module'; import { CloudFoundryPackageModule } from '../../cloud-foundry/src/cloud-foundry.module'; import { SetRecentlyVisitedEntityAction } from '../../store/src/actions/recently-visited.actions'; import { @@ -44,7 +45,6 @@ import { FavoritesConfigMapper } from './shared/components/favorites-meta-card/f import { GlobalEventData, GlobalEventService } from './shared/global-events.service'; import { SharedModule } from './shared/shared.module'; import { XSRFModule } from './xsrf.module'; -import { CloudFoundryStoreModule } from '../../cloud-foundry/src/store/cloud-foundry.store.module'; // Create action for router navigation. See // - https://github.com/ngrx/platform/issues/68 @@ -103,6 +103,7 @@ export class CustomRouterStateSerializer AboutModule, CustomImportModule, XSRFModule, + CfAutoscalerModule ], providers: [ TabNavService, @@ -124,14 +125,18 @@ export class AppModule { private favoritesConfigMapper: FavoritesConfigMapper, ) { EntityActionDispatcher.initialize(this.store); - eventService.addEventConfig( - { - eventTriggered: (state: GeneralEntityAppState) => new GlobalEventData(!state.dashboard.timeoutSession), - message: 'Timeout session is disabled - this is considered a security risk.', - key: 'timeoutSessionWarning', - link: '/user-profile' - } - ); + eventService.addEventConfig({ + eventTriggered: (state: GeneralEntityAppState) => new GlobalEventData(!state.dashboard.timeoutSession), + message: 'Timeout session is disabled - this is considered a security risk.', + key: 'timeoutSessionWarning', + link: '/user-profile' + }); + eventService.addEventConfig({ + eventTriggered: (state: GeneralEntityAppState) => new GlobalEventData(!state.dashboard.pollingEnabled), + message: 'Data polling is disabled - you may be seeing out-of-date data throughout the application.', + key: 'pollingEnabledWarning', + link: '/user-profile' + }); // This should be brought back in in the future // eventService.addEventConfig, EndpointModel>( // { diff --git a/src/frontend/packages/core/src/core/browser-helper.ts b/src/frontend/packages/core/src/core/browser-helper.ts new file mode 100644 index 0000000000..339393b049 --- /dev/null +++ b/src/frontend/packages/core/src/core/browser-helper.ts @@ -0,0 +1,8 @@ +export const getEventTarget = (event) => { + // Ensure we work on Firefox as well as Chrome etc + return event.target || event.srcElement; +}; + +export const getEventFiles = (event) => { + return getEventTarget(event).files; +}; diff --git a/src/frontend/packages/core/src/core/cf-api.types.ts b/src/frontend/packages/core/src/core/cf-api.types.ts index e6be65e498..4304e38234 100644 --- a/src/frontend/packages/core/src/core/cf-api.types.ts +++ b/src/frontend/packages/core/src/core/cf-api.types.ts @@ -187,8 +187,11 @@ export interface IOrganization { export interface IDomain { name: string; - router_group_guid?: any; - router_group_type?: any; + router_group_guid?: string; + router_group_type?: string; + owning_organization_guid?: string; + owning_organization_url?: string; + shared_organizations_url?: string; } export interface ICfV2Info { diff --git a/src/frontend/packages/core/src/core/entity-service.ts b/src/frontend/packages/core/src/core/entity-service.ts index 0beaf8d58c..6b8b3f495f 100644 --- a/src/frontend/packages/core/src/core/entity-service.ts +++ b/src/frontend/packages/core/src/core/entity-service.ts @@ -127,7 +127,8 @@ export class EntityService { }), map(([entityRequestInfo, entity]) => ({ entityRequestInfo, - entity + // If the entity is deleted ensure that we don't pass through a stale state + entity: entityRequestInfo.deleting && entityRequestInfo.deleting.deleted ? null : entity })) ); } diff --git a/src/frontend/packages/core/src/core/extension/dynamic-extension-routes.ts b/src/frontend/packages/core/src/core/extension/dynamic-extension-routes.ts index 2dc1d1d106..e27d1daa19 100644 --- a/src/frontend/packages/core/src/core/extension/dynamic-extension-routes.ts +++ b/src/frontend/packages/core/src/core/extension/dynamic-extension-routes.ts @@ -6,14 +6,14 @@ import { getRoutesFromExtensions, StratosRouteType } from './extension-service'; /** * This is used to dynamically add an extension's routes - since we can't do this - * if the extentsion's module is lazy-loaded. + * if the extension's module is lazy-loaded. * * This CanActive plugin typically is added to the route config to catch all unknown routes '**' - * When activated, it removes itself from the routing congif, so it only evern activates once. + * When activated, it removes itself from the routing config, so it only ever activates once. * * It checks if there are any new routes from extensions that need to be added and add them. * - * Lastly, it navigates to the saem route that it intercepted - if a new extension route + * Lastly, it navigates to the same route that it intercepted - if a new extension route * was added that now matches, it gets the route, otherwise the route goes up the chain * as it would have before. */ diff --git a/src/frontend/packages/core/src/core/extension/extension-service.ts b/src/frontend/packages/core/src/core/extension/extension-service.ts index ff6249102b..5a395b8cda 100644 --- a/src/frontend/packages/core/src/core/extension/extension-service.ts +++ b/src/frontend/packages/core/src/core/extension/extension-service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@angular/core'; -import { Route, Router } from '@angular/router'; +import { ActivatedRoute, Route, Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { GeneralEntityAppState } from '../../../../store/src/app-state'; +import { AppState, GeneralEntityAppState } from '../../../../store/src/app-state'; +import { IPageSideNavTab } from '../../features/dashboard/page-side-nav/page-side-nav.component'; +import { EntityServiceFactory } from '../entity-service-factory.service'; export const extensionsActionRouteKey = 'extensionsActionsKey'; @@ -17,9 +19,15 @@ export enum StratosTabType { } export interface StratosTabMetadata { - type: StratosTabType; label: string; link: string; + icon: string; + iconFont?: string; + hidden?: (store: Store, esf: EntityServiceFactory, activatedRoute: ActivatedRoute) => Observable; +} + +export interface StratosTabMetadataConfig extends StratosTabMetadata { + type: StratosTabType; } // The different types of Action @@ -44,18 +52,23 @@ export interface StratosActionMetadata { export type StratosRouteType = StratosTabType | StratosActionType; +export interface StratosExtensionRoutes { + path: string; + component: any; +} + // Stores the extension metadata as defined by the decorators const extensionMetadata = { loginComponent: null, - extensionRoutes: {}, - tabs: {}, - actions: {} + extensionRoutes: {} as { [key: string]: StratosExtensionRoutes[] }, + tabs: {} as { [key: string]: IPageSideNavTab[] }, + actions: {} as { [key: string]: StratosActionMetadata[] }, }; /** * Decorator for a Tab extension */ -export function StratosTab(props: StratosTabMetadata) { +export function StratosTab(props: StratosTabMetadataConfig) { return target => addExtensionTab(props.type, target, props); } @@ -70,7 +83,7 @@ export function StratosLoginComponent() { return target => extensionMetadata.loginComponent = target; } -function addExtensionTab(tab: StratosTabType, target: any, props: any) { +function addExtensionTab(tab: StratosTabType, target: any, props: StratosTabMetadataConfig) { if (!extensionMetadata.tabs[tab]) { extensionMetadata.tabs[tab] = []; } @@ -82,10 +95,12 @@ function addExtensionTab(tab: StratosTabType, target: any, props: any) { path: props.link, component: target }); - extensionMetadata.tabs[tab].push(props); + extensionMetadata.tabs[tab].push({ + ...props + }); } -function addExtensionAction(action: StratosActionType, target: any, props: any) { +function addExtensionAction(action: StratosActionType, target: any, props: StratosActionMetadata) { if (!extensionMetadata.actions[action]) { extensionMetadata.actions[action] = []; extensionMetadata.extensionRoutes[action] = []; @@ -158,11 +173,11 @@ export class ExtensionService { // Helpers to access Extension metadata (without using the injectable Extension Service) -export function getRoutesFromExtensions(routeType: StratosRouteType) { +export function getRoutesFromExtensions(routeType: StratosRouteType): StratosExtensionRoutes[] { return extensionMetadata.extensionRoutes[routeType] || []; } -export function getTabsFromExtensions(tabType: StratosTabType) { +export function getTabsFromExtensions(tabType: StratosTabType): IPageSideNavTab[] { return extensionMetadata.tabs[tabType] || []; } diff --git a/src/frontend/packages/core/src/features/applications/application.service.ts b/src/frontend/packages/core/src/features/applications/application.service.ts index e63b214487..093a333b55 100644 --- a/src/frontend/packages/core/src/features/applications/application.service.ts +++ b/src/frontend/packages/core/src/features/applications/application.service.ts @@ -14,6 +14,7 @@ import { UpdateApplication, UpdateExistingApplication, } from '../../../../cloud-foundry/src/actions/application.actions'; +import { GetAllOrganizationDomains } from '../../../../cloud-foundry/src/actions/organization.actions'; import { GetSpace } from '../../../../cloud-foundry/src/actions/space.actions'; import { CFAppState } from '../../../../cloud-foundry/src/cf-app-state'; import { @@ -40,7 +41,7 @@ import { endpointEntitiesSelector } from '../../../../store/src/selectors/endpoi import { APIResource, EntityInfo } from '../../../../store/src/types/api.types'; import { AppStat } from '../../../../store/src/types/app-metadata.types'; import { PaginationEntityState } from '../../../../store/src/types/pagination.types'; -import { IApp, IAppSummary, IOrganization, ISpace } from '../../core/cf-api.types'; +import { IApp, IAppSummary, IDomain, IOrganization, ISpace } from '../../core/cf-api.types'; import { EntityService } from '../../core/entity-service'; import { EntityServiceFactory } from '../../core/entity-service-factory.service'; import { @@ -58,7 +59,6 @@ import { import { getRoute, isTCPRoute } from './routes/routes.helper'; - export function createGetApplicationAction(guid: string, endpointGuid: string) { return new GetApplication( guid, @@ -137,6 +137,7 @@ export class ApplicationService { applicationState$: Observable; applicationUrl$: Observable; applicationRunning$: Observable; + orgDomains$: Observable[]>; /** * Fetch the current state of the app (given it's instances) as an object ready @@ -259,6 +260,25 @@ export class ApplicationService { map(app => app ? app.app.entity.state === 'STARTED' : false) ); + // In an ideal world we'd get domains inline with the application, however the inline path from app to org domains exceeds max cf depth + // of 2 (app --> space --> org --> domains). + this.orgDomains$ = this.appOrg$.pipe( + switchMap(org => { + const domainsAction = new GetAllOrganizationDomains(org.metadata.guid, this.cfGuid); + return getPaginationObservables>( + { + store: this.store, + action: domainsAction, + paginationMonitor: this.paginationMonitorFactory.create( + domainsAction.paginationKey, + action + ) + }, + true + ).entities$; + }), + ); + } private constructStatusObservables() { diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.html b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.html new file mode 100644 index 0000000000..76a97eaf7e --- /dev/null +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.html @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.scss b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.spec.ts b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.spec.ts new file mode 100644 index 0000000000..629dddc546 --- /dev/null +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.spec.ts @@ -0,0 +1,46 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GetApplication } from '../../../../../../../store/src/actions/application.actions'; +import { applicationSchemaKey, entityFactory } from '../../../../../../../store/src/helpers/entity-factory'; +import { generateTestApplicationServiceProvider } from '../../../../../../test-framework/application-service-helper'; +import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { generateTestEntityServiceProvider } from '../../../../../../test-framework/entity-service.helper'; +import { ApplicationPollingService } from '../application-polling.service'; +import { ApplicationEnvVarsHelper } from '../tabs/build-tab/application-env-vars.service'; +import { ApplicationPollComponent } from './application-poll.component'; + +describe('ApplicationPollComponent', () => { + let component: ApplicationPollComponent; + let fixture: ComponentFixture; + + const appId = '1'; + const cfId = '2'; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ApplicationPollComponent], + providers: [ + ApplicationPollingService, + generateTestEntityServiceProvider( + appId, + entityFactory(applicationSchemaKey), + new GetApplication(appId, cfId) + ), + generateTestApplicationServiceProvider(cfId, appId), + ApplicationEnvVarsHelper + ], + imports: [...BaseTestModules] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ApplicationPollComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.ts b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.ts new file mode 100644 index 0000000000..8b2b02db6d --- /dev/null +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-poll/application-poll.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +import { ApplicationPollingService } from '../application-polling.service'; + +@Component({ + selector: 'app-application-poll', + templateUrl: './application-poll.component.html', + styleUrls: ['./application-poll.component.scss'] +}) +export class ApplicationPollComponent { + constructor(public appPollingService: ApplicationPollingService) { } +} diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-polling.service.ts b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-polling.service.ts new file mode 100644 index 0000000000..b3578519f1 --- /dev/null +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-polling.service.ts @@ -0,0 +1,86 @@ +import { Inject, Injectable, NgZone } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Observable, Subscription } from 'rxjs'; +import { first, map, tap } from 'rxjs/operators'; + +import { GetAppStatsAction, GetAppSummaryAction } from '../../../../../../cloud-foundry/src/actions/app-metadata.actions'; +import { AppState } from '../../../../../../store/src/app-state'; +import { selectDashboardState } from '../../../../../../store/src/selectors/dashboard.selectors'; +import { APIResource } from '../../../../../../store/src/types/api.types'; +import { IApp } from '../../../../core/cf-api.types'; +import { EntityService } from '../../../../core/entity-service'; +import { safeUnsubscribe } from '../../../../core/utils.service'; +import { ENTITY_SERVICE } from '../../../../shared/entity.tokens'; +import { ApplicationService } from '../../application.service'; + +@Injectable() +export class ApplicationPollingService { + + private pollingSub: Subscription; + private autoRefreshString = 'auto-refresh'; + + public isPolling$ = this.entityService.updatingSection$.pipe(map( + update => update[this.autoRefreshString] && update[this.autoRefreshString].busy + )); + + public isEnabled$: Observable; + + constructor( + public applicationService: ApplicationService, + @Inject(ENTITY_SERVICE) private entityService: EntityService>, + private store: Store, + private ngZone: NgZone, + ) { + this.isEnabled$ = this.store.select(selectDashboardState).pipe( + map(dashboardState => dashboardState.pollingEnabled) + ); + + // Update initial started/stopped state + this.isEnabled$.pipe(first()).subscribe(enabled => this.updateEnabled(enabled)); + } + + public updateEnabled(enable: boolean) { + if (enable) { + this.start(); + } else { + this.stop(); + } + } + + public start() { + if (this.pollingSub && !this.pollingSub.closed) { + return; + } + + // Auto refresh + this.ngZone.runOutsideAngular(() => { + this.pollingSub = this.entityService + .poll(10000, this.autoRefreshString).pipe( + tap(() => this.ngZone.run(() => this.poll(false)))) + .subscribe(); + }); + } + + public stop() { + safeUnsubscribe(this.pollingSub); + } + + public poll(withApp = false) { + const { cfGuid, appGuid } = this.applicationService; + if (withApp) { + const updatingApp = { + ...this.entityService.action, + updatingKey: this.autoRefreshString + }; + this.store.dispatch(updatingApp); + } + this.entityService.entityObs$.pipe( + first(), + ).subscribe(resource => { + this.store.dispatch(new GetAppSummaryAction(appGuid, cfGuid)); + if (resource && resource.entity && resource.entity.entity && resource.entity.entity.state === 'STARTED') { + this.store.dispatch(new GetAppStatsAction(appGuid, cfGuid)); + } + }); + } +} diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts index 9fc5f9c0cc..cdb2bc3acb 100644 --- a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts @@ -1,10 +1,9 @@ import { Component, Inject, NgZone, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; -import { filter, first, map, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators'; +import { filter, first, map, startWith, switchMap, withLatestFrom } from 'rxjs/operators'; import { CF_ENDPOINT_TYPE } from '../../../../../../cloud-foundry/cf-types'; -import { GetAppStatsAction, GetAppSummaryAction } from '../../../../../../cloud-foundry/src/actions/app-metadata.actions'; import { CFAppState } from '../../../../../../cloud-foundry/src/cf-app-state'; import { applicationEntityType } from '../../../../../../cloud-foundry/src/cf-entity-factory'; import { IAppFavMetadata } from '../../../../../../cloud-foundry/src/cf-metadata-types'; @@ -36,11 +35,13 @@ import { ENTITY_SERVICE } from '../../../../shared/entity.tokens'; import { IPageSideNavTab } from '../../../dashboard/page-side-nav/page-side-nav.component'; import { ApplicationService } from '../../application.service'; import { EndpointsService } from './../../../../core/endpoints.service'; +import { ApplicationPollingService } from './application-polling.service'; @Component({ selector: 'app-application-tabs-base', templateUrl: './application-tabs-base.component.html', - styleUrls: ['./application-tabs-base.component.scss'] + styleUrls: ['./application-tabs-base.component.scss'], + providers: [ApplicationPollingService] }) export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { public appState$: Observable; @@ -64,7 +65,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { private currentUserPermissionsService: CurrentUserPermissionsService, scmService: GitSCMService, private favoritesConfigMapper: FavoritesConfigMapper, - + private appPollingService: ApplicationPollingService ) { const catalogueEntity = entityCatalogue.getEntity(CF_ENDPOINT_TYPE, applicationEntityType); this.schema = catalogueEntity.getSchema(); @@ -94,13 +95,13 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { ); this.tabLinks = [ - { link: 'summary', label: 'Summary', matIcon: 'description' }, - { link: 'instances', label: 'Instances', matIcon: 'library_books' }, - { link: 'routes', label: 'Routes', matIconFont: 'stratos-icons', matIcon: 'network_route' }, - { link: 'log-stream', label: 'Log Stream', matIcon: 'featured_play_list' }, - { link: 'services', label: 'Services', matIconFont: 'stratos-icons', matIcon: 'service' }, - { link: 'variables', label: 'Variables', matIcon: 'list', hidden: appDoesNotHaveEnvVars$ }, - { link: 'events', label: 'Events', matIcon: 'watch_later' } + { link: 'summary', label: 'Summary', icon: 'description' }, + { link: 'instances', label: 'Instances', icon: 'library_books' }, + { link: 'routes', label: 'Routes', iconFont: 'stratos-icons', icon: 'network_route' }, + { link: 'log-stream', label: 'Log Stream', icon: 'featured_play_list' }, + { link: 'services', label: 'Services', iconFont: 'stratos-icons', icon: 'service' }, + { link: 'variables', label: 'Variables', icon: 'list', hidden$: appDoesNotHaveEnvVars$ }, + { link: 'events', label: 'Events', icon: 'watch_later' } ]; this.endpointsService.hasMetrics(applicationService.cfGuid).subscribe(hasMetrics => { @@ -110,14 +111,17 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { { link: 'metrics', label: 'Metrics', - matIcon: 'equalizer' + icon: 'equalizer' } ]; } }); // Add any tabs from extensions - this.tabLinks = this.tabLinks.concat(getTabsFromExtensions(StratosTabType.Application)); + const tabs = getTabsFromExtensions(StratosTabType.Application); + tabs.map((extensionTab) => { + this.tabLinks.push(extensionTab); + }); // Ensure Git SCM tab gets updated if the app is redeployed from a different SCM Type this.stratosProjectSub = this.applicationService.applicationStratProject$ @@ -133,11 +137,11 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { // Add tab or update existing tab const tab = this.tabLinks.find(t => t.link === 'gitscm'); if (!tab) { - this.tabLinks.push({ link: 'gitscm', label: scm.getLabel(), matIconFont: iconInfo.fontName, matIcon: iconInfo.iconName }); + this.tabLinks.push({ link: 'gitscm', label: scm.getLabel(), iconFont: iconInfo.fontName, icon: iconInfo.iconName }); } else { tab.label = scm.getLabel(); - tab.matIconFont = iconInfo.fontName; - tab.matIcon = iconInfo.iconName; + tab.iconFont = iconInfo.fontName; + tab.icon = iconInfo.iconName; } this.tabLinks = [...this.tabLinks]; } @@ -149,13 +153,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { applicationActions$: Observable; summaryDataChanging$: Observable; appSub$: Subscription; - entityServiceAppRefresh$: Subscription; stratosProjectSub: Subscription; - autoRefreshString = 'auto-refresh'; - - autoRefreshing$ = this.entityService.updatingSection$.pipe(map( - update => update[this.autoRefreshString] || { busy: false } - )); tabLinks: IPageSideNavTab[]; @@ -245,22 +243,6 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { } ngOnInit() { - const { cfGuid, appGuid } = this.applicationService; - // Auto refresh - this.ngZone.runOutsideAngular(() => { - this.entityServiceAppRefresh$ = this.entityService - .poll(10000, this.autoRefreshString).pipe( - tap(({ resource }) => { - this.ngZone.run(() => { - this.store.dispatch(new GetAppSummaryAction(appGuid, cfGuid)); - if (resource && resource.entity && resource.entity.state === 'STARTED') { - this.store.dispatch(new GetAppStatsAction(appGuid, cfGuid)); - } - }); - })) - .subscribe(); - }); - this.appSub$ = this.entityService.entityMonitor.entityRequest$.subscribe(requestInfo => { if ( requestInfo.deleting.deleted || @@ -293,9 +275,9 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { this.summaryDataChanging$ = observableCombineLatest( initialFetch$, this.applicationService.isUpdatingApp$, - this.autoRefreshing$ - ).pipe(map(([isFetchingApp, isUpdating, autoRefresh]) => { - if (autoRefresh.busy) { + this.appPollingService.isPolling$ + ).pipe(map(([isFetchingApp, isUpdating, isPolling]) => { + if (isPolling) { return false; } return !!(isFetchingApp || isUpdating); @@ -303,6 +285,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { } ngOnDestroy() { - safeUnsubscribe(this.appSub$, this.entityServiceAppRefresh$, this.stratosProjectSub); + safeUnsubscribe(this.appSub$, this.stratosProjectSub); + this.appPollingService.stop(); } } diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.html b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.html index 5e9cb27896..ec38896423 100644 --- a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.html +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.html @@ -1,9 +1,12 @@
- + - - - -
- + launch +
@@ -57,17 +68,28 @@ @@ -80,17 +102,21 @@ - {{ appSvc.cf?.name}} + {{ appSvc.cf?.name}} - {{ appOrg.entity.name }} + {{ appOrg.entity.name }} - {{ appSpace.entity.name }} + {{ appSpace.entity.name }} - View + View @@ -107,9 +133,11 @@ - + + + + {{ appSvc.app.entity.stack?.entity.name || '-' }} - {{ appSvc.app.entity.stack?.entity.name || '-' }} @@ -121,11 +149,13 @@ -
+
{{(applicationService.applicationStratProject$| async)?.deploySource.url}}
- + {{ deploySource.commit | slice:0:8 }} @@ -137,7 +167,8 @@ Deployed from local file - + {{deploySource.dockerImage}} {{deploySource.dockerImage}} diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.scss b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.scss index 70865fcfee..fa05e0aa2f 100644 --- a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.scss +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.scss @@ -21,6 +21,13 @@ } &__sub-nav { display: flex; + width: 100%; + + &__poll { + display: flex; + flex: 1; + justify-content: flex-end; + } } } diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.spec.ts b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.spec.ts index 6119ff1d9f..c32aaffd10 100644 --- a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.spec.ts +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/build-tab/build-tab.component.spec.ts @@ -19,6 +19,8 @@ import { APP_GUID, CF_GUID, ENTITY_SERVICE } from '../../../../../../shared/enti import { SharedModule } from '../../../../../../shared/shared.module'; import { ApplicationService } from '../../../../application.service'; import { entityServiceFactory } from '../../../application-base.component'; +import { ApplicationPollComponent } from '../../application-poll/application-poll.component'; +import { ApplicationPollingService } from '../../application-polling.service'; import { ApplicationEnvVarsHelper } from './application-env-vars.service'; import { BuildTabComponent } from './build-tab.component'; import { ViewBuildpackComponent } from './view-buildpack/view-buildpack.component'; @@ -32,7 +34,8 @@ describe('BuildTabComponent', () => { TestBed.configureTestingModule({ declarations: [ BuildTabComponent, - ViewBuildpackComponent + ViewBuildpackComponent, + ApplicationPollComponent ], imports: [ CoreModule, @@ -62,6 +65,7 @@ describe('BuildTabComponent', () => { useFactory: entityServiceFactory, deps: [CF_GUID, APP_GUID, EntityServiceFactory] }, + ApplicationPollingService ] }) .compileComponents(); diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/events-tab/events-tab.component.scss b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/events-tab/events-tab.component.scss index b7248b408e..e69de29bb2 100644 --- a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/events-tab/events-tab.component.scss +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/events-tab/events-tab.component.scss @@ -1,25 +0,0 @@ -[app-table-content] { - mat-header-cell, - mat-cell { - flex-grow: 1; - } - - .cdk-column-actor_name { - align-items: center; - display: flex; - flex-basis: 100px; - flex-direction: row; - } - - .cdk-column-type { - flex-basis: 100px; - } - - .cdk-column-detail { - flex-grow: 3; - - td { - padding-right: 5px; - } - } -} diff --git a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/routes-tab/routes-tab/routes-tab.component.ts b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/routes-tab/routes-tab/routes-tab.component.ts index 21b31fb8b1..d32552c3b6 100644 --- a/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/routes-tab/routes-tab/routes-tab.component.ts +++ b/src/frontend/packages/core/src/features/applications/application/application-tabs-base/tabs/routes-tab/routes-tab/routes-tab.component.ts @@ -4,14 +4,7 @@ import { Store } from '@ngrx/store'; import { Subscription } from 'rxjs'; import { first } from 'rxjs/operators'; -import { CFEntityConfig } from '../../../../../../../../../cloud-foundry/cf-types'; -import { FetchAllDomains } from '../../../../../../../../../cloud-foundry/src/actions/domains.actions'; import { CFAppState } from '../../../../../../../../../cloud-foundry/src/cf-app-state'; -import { domainEntityType } from '../../../../../../../../../cloud-foundry/src/cf-entity-factory'; -import { - getPaginationObservables, -} from '../../../../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; -import { APIResource } from '../../../../../../../../../store/src/types/api.types'; import { CurrentUserPermissionsService } from '../../../../../../../core/current-user-permissions.service'; import { ConfirmationDialogService } from '../../../../../../../shared/components/confirmation-dialog.service'; import { @@ -19,7 +12,6 @@ import { } from '../../../../../../../shared/components/list/list-types/app-route/cf-app-routes-list-config.service'; import { ListConfig } from '../../../../../../../shared/components/list/list.component.types'; import { CfOrgSpaceDataService } from '../../../../../../../shared/data-services/cf-org-space-service.service'; -import { PaginationMonitorFactory } from '../../../../../../../shared/monitors/pagination-monitor.factory'; import { ApplicationService } from '../../../../../application.service'; @Component({ @@ -46,26 +38,10 @@ import { ApplicationService } from '../../../../../application.service'; export class RoutesTabComponent implements OnInit { paginationSubscription: Subscription; - constructor( - private store: Store, - private appService: ApplicationService, - private paginationMonitorFactory: PaginationMonitorFactory - ) { - } + constructor(private appService: ApplicationService) { } + ngOnInit() { - const { cfGuid } = this.appService; - const action = new FetchAllDomains(cfGuid); - getPaginationObservables( - { - store: this.store, - action, - paginationMonitor: this.paginationMonitorFactory.create( - action.paginationKey, - new CFEntityConfig(domainEntityType) - ) - }, - true - ).entities$.pipe( + this.appService.orgDomains$.pipe( first() ).subscribe(); } diff --git a/src/frontend/packages/core/src/features/applications/applications.module.ts b/src/frontend/packages/core/src/features/applications/applications.module.ts index 75db658c1f..32a099967c 100644 --- a/src/frontend/packages/core/src/features/applications/applications.module.ts +++ b/src/frontend/packages/core/src/features/applications/applications.module.ts @@ -2,11 +2,18 @@ import { DatePipe } from '@angular/common'; import { NgModule } from '@angular/core'; import { CoreModule } from '../../core/core.module'; +import { CustomImportModule } from '../../custom-import.module'; import { SharedModule } from '../../shared/shared.module'; +import { ApplicationDeleteComponent } from './application-delete/application-delete.component'; +import { + DeleteAppServiceInstancesComponent, +} from './application-delete/delete-app-instances/delete-app-instances.component'; +import { DeleteAppRoutesComponent } from './application-delete/delete-app-routes/delete-app-routes.component'; import { ApplicationMonitorService } from './application-monitor.service'; import { ApplicationWallComponent } from './application-wall/application-wall.component'; import { ApplicationService } from './application.service'; import { ApplicationBaseComponent } from './application/application-base.component'; +import { ApplicationPollComponent } from './application/application-tabs-base/application-poll/application-poll.component'; import { ApplicationTabsBaseComponent } from './application/application-tabs-base/application-tabs-base.component'; import { ApplicationEnvVarsHelper } from './application/application-tabs-base/tabs/build-tab/application-env-vars.service'; import { BuildTabComponent } from './application/application-tabs-base/tabs/build-tab/build-tab.component'; @@ -22,17 +29,13 @@ import { RoutesTabComponent } from './application/application-tabs-base/tabs/rou import { ServicesTabComponent } from './application/application-tabs-base/tabs/services-tab/services-tab.component'; import { VariablesTabComponent } from './application/application-tabs-base/tabs/variables-tab/variables-tab.component'; import { ApplicationsRoutingModule } from './applications.routing'; +import { CliInfoApplicationComponent } from './cli-info-application/cli-info-application.component'; import { EditApplicationComponent } from './edit-application/edit-application.component'; +import { NewApplicationBaseStepComponent } from './new-application-base-step/new-application-base-step.component'; import { AddRouteStepperComponent } from './routes/add-route-stepper/add-route-stepper.component'; import { AddRoutesComponent } from './routes/add-routes/add-routes.component'; import { MapRoutesComponent } from './routes/map-routes/map-routes.component'; import { SshApplicationComponent } from './ssh-application/ssh-application.component'; -import { CliInfoApplicationComponent } from './cli-info-application/cli-info-application.component'; -import { ApplicationDeleteComponent } from './application-delete/application-delete.component'; -import { DeleteAppRoutesComponent } from './application-delete/delete-app-routes/delete-app-routes.component'; -import { DeleteAppServiceInstancesComponent } from './application-delete/delete-app-instances/delete-app-instances.component'; -import { CustomImportModule } from '../../custom-import.module'; -import { NewApplicationBaseStepComponent } from './new-application-base-step/new-application-base-step.component'; @NgModule({ imports: [ @@ -65,6 +68,7 @@ import { NewApplicationBaseStepComponent } from './new-application-base-step/new DeleteAppRoutesComponent, DeleteAppServiceInstancesComponent, NewApplicationBaseStepComponent, + ApplicationPollComponent ], providers: [ ApplicationService, diff --git a/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-options-step/deploy-application-options-step.component.ts b/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-options-step/deploy-application-options-step.component.ts index 6878d65d88..348d42708b 100644 --- a/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-options-step/deploy-application-options-step.component.ts +++ b/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-options-step/deploy-application-options-step.component.ts @@ -7,7 +7,7 @@ import { combineLatest, Observable, of as observableOf, Subscription } from 'rxj import { filter, first, map, share, startWith, switchMap } from 'rxjs/operators'; import { SaveAppOverrides } from '../../../../../../cloud-foundry/src/actions/deploy-applications.actions'; -import { FetchAllDomains } from '../../../../../../cloud-foundry/src/actions/domains.actions'; +import { GetAllOrganizationDomains } from '../../../../../../cloud-foundry/src/actions/organization.actions'; import { GetAllStacks } from '../../../../../../cloud-foundry/src/actions/stack.action'; import { CFAppState } from '../../../../../../cloud-foundry/src/cf-app-state'; import { getPaginationObservables } from '../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; @@ -102,7 +102,7 @@ export class DeployApplicationOptionsStepComponent implements OnInit, OnDestroy // Create the domains list for the domains drop down this.domains$ = cfDetails$.pipe( switchMap(cfDetails => { - const action = new FetchAllDomains(cfDetails.cloudFoundry); + const action = new GetAllOrganizationDomains(cfDetails.org, cfDetails.cloudFoundry); return getPaginationObservables>( { store: this.store, diff --git a/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs-utils.ts b/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs-utils.ts index dcacea01b6..2b2324ae24 100644 --- a/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs-utils.ts +++ b/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs-utils.ts @@ -29,16 +29,14 @@ export class DeployApplicationFsUtils { for (const item of items) { const filePath = item.webkitRelativePath.split('/'); // First part is the root folder name - if (filePath.length === 2 && !rootFolderName) { + if (filePath.length > 1 && !rootFolderName) { rootFolderName = filePath[0]; } - if (filePath.length > 2) { - // Don't traverse below the 1st level of files (could take a while) - break; - } + if (!cfIgnoreFile && filePath.length === 2 && filePath[1] === CF_IGNORE_FILE) { cfIgnoreFile = item; } + if (filePath.length === 2 && filePath[1] === CF_MANIFEST_FILE) { manifestFile = item; } diff --git a/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs.component.ts b/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs.component.ts index de5be77135..ed88d6b1d0 100644 --- a/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs.component.ts +++ b/src/frontend/packages/core/src/features/applications/deploy-application/deploy-application-step2/deploy-application-fs/deploy-application-fs.component.ts @@ -3,6 +3,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject } from 'rxjs'; import { filter, first } from 'rxjs/operators'; +import { getEventFiles } from '../../../../../core/browser-helper'; import { FileScannerInfo } from './deploy-application-fs-scanner'; import { DeployApplicationFsUtils } from './deploy-application-fs-utils'; @@ -31,7 +32,7 @@ export class DeployApplicationFsComponent implements ControlValueAccessor { // Handle result of a file input form field selection onFileChange(event) { - const files = event.srcElement.files; + const files = getEventFiles(event); const utils = new DeployApplicationFsUtils(); utils.handleFileInputSelection(files).pipe( filter(res => !!res), diff --git a/src/frontend/packages/core/src/features/applications/routes/add-routes/add-routes.component.ts b/src/frontend/packages/core/src/features/applications/routes/add-routes/add-routes.component.ts index 8a9776aed1..094e64778e 100644 --- a/src/frontend/packages/core/src/features/applications/routes/add-routes/add-routes.component.ts +++ b/src/frontend/packages/core/src/features/applications/routes/add-routes/add-routes.component.ts @@ -5,12 +5,10 @@ import { Store } from '@ngrx/store'; import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; import { filter, map, mergeMap, pairwise, switchMap, take, tap } from 'rxjs/operators'; -import { CFEntityConfig } from '../../../../../../cloud-foundry/cf-types'; import { AssignRouteToApplication, GetAppRoutes, } from '../../../../../../cloud-foundry/src/actions/application-service-routes.actions'; -import { FetchAllDomains } from '../../../../../../cloud-foundry/src/actions/domains.actions'; import { CreateRoute } from '../../../../../../cloud-foundry/src/actions/route.actions'; import { GetSpace } from '../../../../../../cloud-foundry/src/actions/space.actions'; import { CFAppState } from '../../../../../../cloud-foundry/src/cf-app-state'; @@ -24,7 +22,6 @@ import { selectCfRequestInfo } from '../../../../../../cloud-foundry/src/selecto import { RouterNav } from '../../../../../../store/src/actions/router.actions'; import { createEntityRelationKey } from '../../../../../../store/src/helpers/entity-relations/entity-relations.types'; import { RequestInfoState } from '../../../../../../store/src/reducers/api-request-reducer/types'; -import { getPaginationObservables } from '../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; import { APIResource } from '../../../../../../store/src/types/api.types'; import { Route, RouteMode } from '../../../../../../store/src/types/route.types'; import { IDomain, ISpace } from '../../../../core/cf-api.types'; @@ -114,22 +111,9 @@ export class AddRoutesComponent implements OnInit, OnDestroy { } })); - const fetchAllDomainsAction = new FetchAllDomains(this.cfGuid); - const sharedDomains$ = getPaginationObservables( - { - store: this.store, - action: fetchAllDomainsAction, - paginationMonitor: this.paginationMonitorFactory.create( - fetchAllDomainsAction.paginationKey, - new CFEntityConfig(domainEntityType) - ) - }, - true - ).entities$; - - const space$ = sharedDomains$.pipe( - // We don't need the shared domains, but we need them fetched first so we get the router_group_type - switchMap(sharedDomains => this.appService.waitForAppEntity$ + const space$ = this.applicationService.orgDomains$.pipe( + // We don't need the domains, but we need them fetched first so we get the router_group_type + switchMap(() => this.appService.waitForAppEntity$ .pipe( switchMap(app => { this.spaceGuid = app.entity.entity.space_guid; @@ -140,8 +124,8 @@ export class AddRoutesComponent implements OnInit, OnDestroy { ); return spaceService.waitForEntity$; }), - filter(({ entity, entityRequestInfo }) => !!entity.entity.domains), - tap(({ entity, entityRequestInfo }) => { + filter(({ entity }) => !!entity.entity.domains), + tap(({ entity }) => { this.domains = []; const domains = entity.entity.domains; domains.forEach(domain => { diff --git a/src/frontend/packages/core/src/features/applications/routes/map-routes/map-routes.component.ts b/src/frontend/packages/core/src/features/applications/routes/map-routes/map-routes.component.ts index 9ed608ff07..119edaa33e 100644 --- a/src/frontend/packages/core/src/features/applications/routes/map-routes/map-routes.component.ts +++ b/src/frontend/packages/core/src/features/applications/routes/map-routes/map-routes.component.ts @@ -1,20 +1,13 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; import { BehaviorSubject, Subscription } from 'rxjs'; import { tap } from 'rxjs/operators'; -import { CFEntityConfig } from '../../../../../../cloud-foundry/cf-types'; -import { FetchAllDomains } from '../../../../../../cloud-foundry/src/actions/domains.actions'; -import { CFAppState } from '../../../../../../cloud-foundry/src/cf-app-state'; -import { domainEntityType } from '../../../../../../cloud-foundry/src/cf-entity-factory'; -import { getPaginationObservables } from '../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; import { APIResource } from '../../../../../../store/src/types/api.types'; import { CfAppMapRoutesListConfigService, } from '../../../../shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service'; import { CfAppRoutesDataSource } from '../../../../shared/components/list/list-types/app-route/cf-app-routes-data-source'; import { ListConfig } from '../../../../shared/components/list/list.component.types'; -import { PaginationMonitorFactory } from '../../../../shared/monitors/pagination-monitor.factory'; import { ApplicationService } from '../../application.service'; @Component({ @@ -34,10 +27,8 @@ export class MapRoutesComponent implements OnInit, OnDestroy { @Input() selectedRoute$: BehaviorSubject; constructor( - private store: Store, private appService: ApplicationService, - listConfig: ListConfig, - private paginationMonitorFactory: PaginationMonitorFactory + listConfig: ListConfig ) { this.routesDataSource = listConfig.getDataSource() as CfAppRoutesDataSource; } @@ -54,18 +45,7 @@ export class MapRoutesComponent implements OnInit, OnDestroy { ) .subscribe(); - const action = new FetchAllDomains(this.appService.cfGuid); - this.paginationSubscription = getPaginationObservables( - { - store: this.store, - action, - paginationMonitor: this.paginationMonitorFactory.create( - action.paginationKey, - new CFEntityConfig(domainEntityType) - ) - }, - true - ).entities$.subscribe(); + this.paginationSubscription = this.appService.orgDomains$.subscribe(); } ngOnDestroy(): void { diff --git a/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts index bdb78a13e5..946ecac33a 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts @@ -70,31 +70,31 @@ export class CloudFoundryTabsBaseComponent implements OnInit { // Default tabs + add any tabs from extensions this.tabLinks = [ - { link: 'summary', label: 'Summary', matIcon: 'description' }, - { link: 'organizations', label: 'Organizations', matIcon: 'organization', matIconFont: 'stratos-icons' }, + { link: 'summary', label: 'Summary', icon: 'description' }, + { link: 'organizations', label: 'Organizations', icon: 'organization', iconFont: 'stratos-icons' }, { link: CloudFoundryTabsBaseComponent.cells, label: 'Cells', - matIcon: 'select_all', - hidden: cellsHidden$ + icon: 'select_all', + hidden$: cellsHidden$ }, - { link: 'routes', label: 'Routes', matIcon: 'network_route', matIconFont: 'stratos-icons', }, + { link: 'routes', label: 'Routes', icon: 'network_route', iconFont: 'stratos-icons', }, { link: CloudFoundryTabsBaseComponent.users, label: 'Users', - hidden: usersHidden$, - matIcon: 'people' + hidden$: usersHidden$, + icon: 'people' }, { link: CloudFoundryTabsBaseComponent.firehose, label: 'Firehose', - hidden: firehoseHidden$, - matIcon: 'featured_play_list' + hidden$: firehoseHidden$, + icon: 'featured_play_list' }, - { link: 'feature-flags', label: 'Feature Flags', matIcon: 'flag' }, - { link: 'build-packs', label: 'Build Packs', matIcon: 'build' }, - { link: 'stacks', label: 'Stacks', matIcon: 'code' }, - { link: 'security-groups', label: 'Security Groups', matIcon: 'security' }, + { link: 'feature-flags', label: 'Feature Flags', icon: 'flag' }, + { link: 'build-packs', label: 'Build Packs', icon: 'build' }, + { link: 'stacks', label: 'Stacks', icon: 'code' }, + { link: 'security-groups', label: 'Security Groups', icon: 'security' }, ...getTabsFromExtensions(StratosTabType.CloudFoundry) ]; } diff --git a/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.module.ts index 70c9e7c75e..3d4ad47444 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.module.ts @@ -28,8 +28,10 @@ import { EditOrganizationStepComponent } from './edit-organization/edit-organiza import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; import { EditSpaceStepComponent } from './edit-space/edit-space-step/edit-space-step.component'; import { EditSpaceComponent } from './edit-space/edit-space.component'; +import { QuotaDefinitionComponent } from './quota-definition/quota-definition.component'; import { CloudFoundryEndpointService } from './services/cloud-foundry-endpoint.service'; import { CloudFoundryOrganizationService } from './services/cloud-foundry-organization.service'; +import { SpaceQuotaDefinitionComponent } from './space-quota-definition/space-quota-definition.component'; import { CfAdminAddUserWarningComponent } from './tabs/cf-admin-add-user-warning/cf-admin-add-user-warning.component'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; import { @@ -100,7 +102,6 @@ import { import { UserInviteService } from './user-invites/user-invite.service'; import { InviteUsersCreateComponent } from './users/invite-users/invite-users-create/invite-users-create.component'; import { InviteUsersComponent } from './users/invite-users/invite-users.component'; -import { RemoveUserComponent } from './users/remove-user/remove-user.component'; import { CfRolesService } from './users/manage-users/cf-roles.service'; import { UsersRolesConfirmComponent } from './users/manage-users/manage-users-confirm/manage-users-confirm.component'; import { UsersRolesModifyComponent } from './users/manage-users/manage-users-modify/manage-users-modify.component'; @@ -109,6 +110,7 @@ import { } from './users/manage-users/manage-users-modify/space-roles-list-wrapper/space-roles-list-wrapper.component'; import { UsersRolesSelectComponent } from './users/manage-users/manage-users-select/manage-users-select.component'; import { UsersRolesComponent } from './users/manage-users/manage-users.component'; +import { RemoveUserComponent } from './users/remove-user/remove-user.component'; @NgModule({ imports: [ @@ -168,6 +170,8 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component CloudFoundryInviteUserLinkComponent, CfAdminAddUserWarningComponent, RemoveUserComponent, + QuotaDefinitionComponent, + SpaceQuotaDefinitionComponent, ], providers: [ EndpointListHelper, diff --git a/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.routing.ts b/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.routing.ts index 18fc5a5898..472dd16d90 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.routing.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/cloud-foundry.routing.ts @@ -13,6 +13,8 @@ import { CloudFoundryTabsBaseComponent } from './cloud-foundry-tabs-base/cloud-f import { CloudFoundryComponent } from './cloud-foundry/cloud-foundry.component'; import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; import { EditSpaceComponent } from './edit-space/edit-space.component'; +import { QuotaDefinitionComponent } from './quota-definition/quota-definition.component'; +import { SpaceQuotaDefinitionComponent } from './space-quota-definition/space-quota-definition.component'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; import { CloudFoundryCellAppsComponent, @@ -73,8 +75,8 @@ import { CloudFoundryStacksComponent } from './tabs/cloud-foundry-stacks/cloud-f import { CloudFoundrySummaryTabComponent } from './tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component'; import { CloudFoundryUsersComponent } from './tabs/cloud-foundry-users/cloud-foundry-users.component'; import { InviteUsersComponent } from './users/invite-users/invite-users.component'; -import { RemoveUserComponent } from './users/remove-user/remove-user.component'; import { UsersRolesComponent } from './users/manage-users/manage-users.component'; +import { RemoveUserComponent } from './users/remove-user/remove-user.component'; /* tslint:enable:max-line-length */ @@ -142,6 +144,14 @@ const cloudFoundry: Routes = [{ // Root for attaching CF wide actions (i.e assignments, tabs) component: CloudFoundryBaseComponent, children: [ + { + path: 'quota-definitions/:quotaId', + component: QuotaDefinitionComponent + }, + { + path: 'organizations/:orgId/space-quota-definitions/:quotaId', + component: SpaceQuotaDefinitionComponent + }, { path: 'organizations/:orgId/edit-org', component: EditOrganizationComponent @@ -277,6 +287,10 @@ const cloudFoundry: Routes = [{ path: 'users', component: CloudFoundryOrganizationUsersComponent }, + { + path: 'quota', + component: QuotaDefinitionComponent + }, { path: '**', component: PageNotFoundComponentComponent, @@ -323,6 +337,14 @@ const cloudFoundry: Routes = [{ path: 'users', component: CloudFoundrySpaceUsersComponent }, + { + path: 'quota', + component: QuotaDefinitionComponent + }, + { + path: 'space-quota', + component: SpaceQuotaDefinitionComponent + }, { path: '**', component: PageNotFoundComponentComponent, diff --git a/src/frontend/packages/core/src/features/cloud-foundry/quota-definition-base/quota-definition-base.component.scss b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition-base/quota-definition-base.component.scss new file mode 100644 index 0000000000..777f33b959 --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition-base/quota-definition-base.component.scss @@ -0,0 +1,24 @@ +.quota-definition-base { + &__name-sub-text { + font-size: 14px; + opacity: .6; + } + &__title { + margin: 10px 0; + } + &__name { + margin: 0; + } + &__section-header { + margin: 10px 0; + opacity: .6; + } + &__basic-services { + display: flex; + margin: 8px 0; + } + &__basic-services-label { + margin-right: 10px; + opacity: .6; + } +} diff --git a/src/frontend/packages/core/src/features/cloud-foundry/quota-definition-base/quota-definition-base.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition-base/quota-definition-base.component.ts new file mode 100644 index 0000000000..1b4d2b0513 --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition-base/quota-definition-base.component.ts @@ -0,0 +1,89 @@ +import { ActivatedRoute } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { combineLatest, Observable, of, Subscription } from 'rxjs'; +import { first, map } from 'rxjs/operators'; + +import { GetOrganization } from '../../../../../cloud-foundry/src/actions/organization.actions'; +import { GetSpace } from '../../../../../cloud-foundry/src/actions/space.actions'; +import { AppState } from '../../../../../store/src/app-state'; +import { endpointEntitiesSelector } from '../../../../../store/src/selectors/endpoint.selectors'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { EndpointModel } from '../../../../../store/src/types/endpoint.types'; +import { IOrganization, IOrgQuotaDefinition, ISpace, ISpaceQuotaDefinition } from '../../../core/cf-api.types'; +import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; +import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; +import { ActiveRouteCfOrgSpace } from '../cf-page.types'; + +export class QuotaDefinitionBaseComponent { + breadcrumbs$: Observable; + quotaDefinition$: Observable>; + org$: Observable>; + space$: Observable>; + cfGuid: string; + orgGuid: string; + spaceGuid: string; + quotaGuid: string; + detailsLoading$: Observable; + orgSubscriber: Subscription; + + constructor( + protected entityServiceFactory: EntityServiceFactory, + protected store: Store, + protected activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, + protected activatedRoute: ActivatedRoute, + ) { + this.cfGuid = activeRouteCfOrgSpace.cfGuid || activatedRoute.snapshot.queryParams.cfGuid; + this.orgGuid = activeRouteCfOrgSpace.orgGuid || activatedRoute.snapshot.queryParams.orgGuid; + this.spaceGuid = activeRouteCfOrgSpace.spaceGuid || activatedRoute.snapshot.queryParams.spaceGuid; + this.quotaGuid = activatedRoute.snapshot.params.quotaId || activatedRoute.snapshot.queryParams.quotaGuid; + this.setupOrgObservable(); + this.setupSpaceObservable(); + this.setupBreadcrumbs(); + } + + setupOrgObservable() { + if (this.orgGuid) { + this.org$ = this.entityServiceFactory.create>( + this.orgGuid, + new GetOrganization(this.orgGuid, this.cfGuid) + ).waitForEntity$.pipe( + map(data => data.entity), + ); + } + } + + setupSpaceObservable() { + if (this.spaceGuid) { + this.space$ = this.entityServiceFactory.create>( + this.spaceGuid, + new GetSpace(this.spaceGuid, this.cfGuid), + true + ).waitForEntity$.pipe( + map(data => data.entity), + ); + } + } + + private setupBreadcrumbs() { + const endpoints$ = this.store.select(endpointEntitiesSelector); + const org$ = this.org$ ? this.org$ : of(null); + const space$ = this.space$ ? this.space$ : of(null); + this.breadcrumbs$ = combineLatest(endpoints$, org$, space$).pipe( + map(([endpoints, org, space]) => this.getBreadcrumbs(endpoints[this.cfGuid], org, space)), + first() + ); + } + + protected setupQuotaDefinitionObservable() { + throw new Error('Method not implemented.'); + } + + protected getBreadcrumbs( + endpoint: EndpointModel, + org: APIResource, + space: APIResource + ) { + throw new Error('Method not implemented.'); + return null; + } +} diff --git a/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.html b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.html new file mode 100644 index 0000000000..eca680fa15 --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.html @@ -0,0 +1,82 @@ + +

{{ (quotaDefinition$ | async)?.entity?.name }}

+
+ +
+ + +
Name
+

{{ quotaDefinition.entity.name }}

+
+
Non Basic Services Allowed:
+ + +
+ +

Quota Limits

+

Memory

+ + + + + + + + + + + + +

Application

+ + + + + + + + + + + + +

Service

+ + + + + + + + + + + + +

Routes & Domains

+ + + + + + + + + + + + + + +
+
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.scss b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.spec.ts b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.spec.ts new file mode 100644 index 0000000000..9d1960f8c7 --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.spec.ts @@ -0,0 +1,46 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; + +import { TabNavService } from '../../../../tab-nav.service'; +import { + BaseTestModules, + generateTestCfEndpointServiceProvider, +} from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { testSCFGuid } from '../../../../test-framework/store-test-helper'; +import { QuotaDefinitionComponent } from './quota-definition.component'; + +describe('QuotaDefinitionComponent', () => { + let component: QuotaDefinitionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [QuotaDefinitionComponent], + imports: [...BaseTestModules], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + queryParams: { cfGuid: testSCFGuid }, + params: { quotaId: 'guid' } + } + } + }, + generateTestCfEndpointServiceProvider(), + TabNavService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(QuotaDefinitionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.ts new file mode 100644 index 0000000000..f97b058702 --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/quota-definition/quota-definition.component.ts @@ -0,0 +1,108 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { Observable, of, Subscription } from 'rxjs'; +import { filter, first, map, switchMap } from 'rxjs/operators'; + +import { GetQuotaDefinition } from '../../../../../cloud-foundry/src/actions/quota-definitions.actions'; +import { AppState } from '../../../../../store/src/app-state'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { EndpointModel } from '../../../../../store/src/types/endpoint.types'; +import { IOrganization, IOrgQuotaDefinition, ISpace } from '../../../core/cf-api.types'; +import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; +import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; +import { ActiveRouteCfOrgSpace } from '../cf-page.types'; +import { getActiveRouteCfOrgSpaceProvider } from '../cf.helpers'; +import { QuotaDefinitionBaseComponent } from '../quota-definition-base/quota-definition-base.component'; + +@Component({ + selector: 'app-quota-definition', + templateUrl: './quota-definition.component.html', + styleUrls: ['../quota-definition-base/quota-definition-base.component.scss', './quota-definition.component.scss'], + providers: [ + getActiveRouteCfOrgSpaceProvider + ] +}) +export class QuotaDefinitionComponent extends QuotaDefinitionBaseComponent { + breadcrumbs$: Observable; + quotaDefinition$: Observable>; + org$: Observable>; + space$: Observable>; + cfGuid: string; + orgGuid: string; + spaceGuid: string; + quotaGuid: string; + detailsLoading$: Observable; + orgSubscriber: Subscription; + + constructor( + protected entityServiceFactory: EntityServiceFactory, + protected store: Store, + activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, + activatedRoute: ActivatedRoute, + ) { + super(entityServiceFactory, store, activeRouteCfOrgSpace, activatedRoute); + this.setupQuotaDefinitionObservable(); + } + + setupQuotaDefinitionObservable() { + const quotaGuid$ = this.quotaGuid ? of(this.quotaGuid) : this.org$.pipe(map(org => org.entity.quota_definition_guid)); + const entityInfo$ = quotaGuid$.pipe( + first(), + switchMap(quotaGuid => this.entityServiceFactory.create>( + quotaGuid, + new GetQuotaDefinition(quotaGuid, this.cfGuid), + ).entityObs$) + ); + + this.quotaDefinition$ = entityInfo$.pipe( + filter(definition => !!definition && !!definition.entity), + map(definition => definition.entity) + ); + this.detailsLoading$ = entityInfo$.pipe( + filter(definition => !!definition), + map(definition => definition.entityRequestInfo.fetching) + ); + } + + protected getBreadcrumbs( + endpoint: EndpointModel, + org: APIResource, + space: APIResource + ) { + const baseCFUrl = `/cloud-foundry/${this.cfGuid}`; + + const breadcrumbs: IHeaderBreadcrumb[] = [{ + breadcrumbs: [ + { value: endpoint.name, routerLink: `${baseCFUrl}/quota-definitions` }, + ] + }]; + + if (org) { + const baseOrgUrl = `${baseCFUrl}/organizations/${org.metadata.guid}`; + + breadcrumbs.push({ + key: 'org', + breadcrumbs: [ + { value: endpoint.name, routerLink: `${baseCFUrl}/organizations` }, + { value: org.entity.name, routerLink: `${baseOrgUrl}/summary` }, + ] + }); + + if (space) { + const baseSpaceUrl = `${baseCFUrl}/organizations/${org.metadata.guid}/spaces/${space.metadata.guid}`; + + breadcrumbs.push({ + key: 'space', + breadcrumbs: [ + { value: endpoint.name, routerLink: `${baseCFUrl}/organizations` }, + { value: org.entity.name, routerLink: `${baseOrgUrl}/spaces` }, + { value: space.entity.name, routerLink: `${baseSpaceUrl}/summary` }, + ] + }); + } + } + + return breadcrumbs; + } +} diff --git a/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-organization.service.ts b/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-organization.service.ts index f34226e78f..2ba5d1738e 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-organization.service.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-organization.service.ts @@ -68,6 +68,7 @@ export const createSpaceQuotaDefinition = (orgGuid: string): ISpaceQuotaDefiniti export class CloudFoundryOrganizationService { orgGuid: string; cfGuid: string; + quotaLink$: Observable; userOrgRole$: Observable; quotaDefinition$: Observable; totalMem$: Observable; @@ -152,7 +153,6 @@ export class CloudFoundryOrganizationService { this.serviceInstancesCount$ = fetchServiceInstancesCount(this.cfGuid, this.orgGuid, null, this.store, this.paginationMonitorFactory); this.userProvidedServiceInstancesCount$ = this.cfUserProvidedServicesService.fetchUserProvidedServiceInstancesCount(this.cfGuid, this.orgGuid); - } private initialiseSpaceObservables() { @@ -198,6 +198,17 @@ export class CloudFoundryOrganizationService { this.spaces$ = this.org$.pipe(map(o => o.entity.entity.spaces), filter(o => !!o)); this.privateDomains$ = this.org$.pipe(map(o => o.entity.entity.private_domains)); this.quotaDefinition$ = this.org$.pipe(map(o => o.entity.entity.quota_definition && o.entity.entity.quota_definition.entity)); + this.quotaLink$ = this.org$.pipe(map(o => { + const quotaDefinition = o.entity.entity.quota_definition; + + return quotaDefinition && [ + '/cloud-foundry', + this.cfGuid, + 'organizations', + this.orgGuid, + 'quota' + ]; + })); } private getFlattenedList(property: string): (source: Observable[]>) => Observable { diff --git a/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-space.service.ts b/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-space.service.ts index f7e2a34d22..c00868cbb0 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-space.service.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/services/cloud-foundry-space.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Observable, of } from 'rxjs'; +import { combineLatest, Observable, of } from 'rxjs'; import { filter, map, publishReplay, refCount, switchMap } from 'rxjs/operators'; import { GetSpace } from '../../../../../cloud-foundry/src/actions/space.actions'; @@ -57,6 +57,7 @@ export class CloudFoundrySpaceService { loadingApps$: Observable; space$: Observable>>; usersCount$: Observable; + quotaLink$: Observable; constructor( public activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, @@ -152,6 +153,30 @@ export class CloudFoundrySpaceService { createOrgQuotaDefinition() ) ); + this.quotaLink$ = combineLatest(this.quotaDefinition$, this.spaceQuotaDefinition$).pipe( + map(([quota, spaceQuota]) => { + if (!spaceQuota) { + return [ + '/cloud-foundry', + this.cfGuid, + 'organizations', + this.orgGuid, + 'quota', + ]; + } + + return quota && [ + '/cloud-foundry', + this.cfGuid, + 'organizations', + this.orgGuid, + 'space', + this.spaceGuid, + 'space-quota' + ]; + } + ) + ); } private initialiseAppObservables() { diff --git a/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.html b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.html new file mode 100644 index 0000000000..ebe3e942ea --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.html @@ -0,0 +1,73 @@ + +

{{ (quotaDefinition$ | async)?.entity?.name }}

+
+ +
+ +
Name
+

{{ quotaDefinition.entity.name }}

+
+
Non Basic Services Allowed:
+ + +
+ +

Quota Limits

+

Memory

+ + + + + + + + + + + +

Application

+ + + + + + + + + + + +

Service

+ + + + + + + + + + + +

Routes

+ + + + + + + + + + +
+
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.scss b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.spec.ts b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.spec.ts new file mode 100644 index 0000000000..57ac1ce2b6 --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.spec.ts @@ -0,0 +1,57 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; + +import { organizationSchemaKey, spaceSchemaKey } from '../../../../../store/src/helpers/entity-factory'; +import { EntityRelationSpecHelper } from '../../../../../store/src/helpers/entity-relations/entity-relations.spec'; +import { TabNavService } from '../../../../tab-nav.service'; +import { + BaseTestModules, + generateTestCfEndpointServiceProvider, +} from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { createBasicStoreModule, getInitialTestStoreState, testSCFGuid } from '../../../../test-framework/store-test-helper'; +import { SpaceQuotaDefinitionComponent } from './space-quota-definition.component'; + +describe('SpaceQuotaDefinitionComponent', () => { + let component: SpaceQuotaDefinitionComponent; + let fixture: ComponentFixture; + const cfGuid = testSCFGuid; + const orgGuid = '123'; + const spaceGuid = '123'; + + const helper = new EntityRelationSpecHelper(); + + beforeEach(async(() => { + const store = getInitialTestStoreState(); + store.requestData[organizationSchemaKey][orgGuid] = helper.createEmptyOrg(orgGuid, 'org-name'); + store.requestData[spaceSchemaKey][spaceGuid] = helper.createEmptySpace(spaceGuid, 'space-name', orgGuid); + TestBed.configureTestingModule({ + declarations: [SpaceQuotaDefinitionComponent], + imports: [ + ...BaseTestModules, + createBasicStoreModule(store), + ], + providers: [{ + provide: ActivatedRoute, + useValue: { + snapshot: { + queryParams: { cfGuid, orgGuid, spaceGuid }, + params: { quotaId: 'guid' } + } + } + }, + generateTestCfEndpointServiceProvider(), TabNavService] + + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SpaceQuotaDefinitionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.ts new file mode 100644 index 0000000000..7289645e2c --- /dev/null +++ b/src/frontend/packages/core/src/features/cloud-foundry/space-quota-definition/space-quota-definition.component.ts @@ -0,0 +1,88 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { Observable, of, Subscription } from 'rxjs'; +import { filter, first, map, switchMap } from 'rxjs/operators'; + +import { GetSpaceQuotaDefinition } from '../../../../../cloud-foundry/src/actions/quota-definitions.actions'; +import { AppState } from '../../../../../store/src/app-state'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { EndpointModel } from '../../../../../store/src/types/endpoint.types'; +import { IOrganization, ISpace, ISpaceQuotaDefinition } from '../../../core/cf-api.types'; +import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; +import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; +import { ActiveRouteCfOrgSpace } from '../cf-page.types'; +import { getActiveRouteCfOrgSpaceProvider } from '../cf.helpers'; +import { QuotaDefinitionBaseComponent } from '../quota-definition-base/quota-definition-base.component'; + +@Component({ + selector: 'app-space-quota-definition', + styleUrls: ['../quota-definition-base/quota-definition-base.component.scss', './space-quota-definition.component.scss'], + templateUrl: './space-quota-definition.component.html', + providers: [ + getActiveRouteCfOrgSpaceProvider + ] +}) +export class SpaceQuotaDefinitionComponent extends QuotaDefinitionBaseComponent { + breadcrumbs$: Observable; + spaceQuotaDefinition$: Observable>; + cfGuid: string; + orgGuid: string; + spaceGuid: string; + quotaGuid: string; + detailsLoading$: Observable; + spaceSubscriber: Subscription; + + constructor( + protected entityServiceFactory: EntityServiceFactory, + protected store: Store, + activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, + activatedRoute: ActivatedRoute, + ) { + super(entityServiceFactory, store, activeRouteCfOrgSpace, activatedRoute); + this.setupQuotaDefinitionObservable(); + } + + setupQuotaDefinitionObservable() { + const quotaGuid$ = this.quotaGuid ? of(this.quotaGuid) : this.space$.pipe(map(space => space.entity.space_quota_definition_guid)); + const entityInfo$ = quotaGuid$.pipe( + first(), + switchMap(quotaGuid => this.entityServiceFactory.create>( + quotaGuid, + new GetSpaceQuotaDefinition(quotaGuid, this.cfGuid) + ).entityObs$ + ) + ); + + this.quotaDefinition$ = entityInfo$.pipe( + filter(definition => !!definition && !!definition.entity), + map(definition => definition.entity) + ); + this.detailsLoading$ = entityInfo$.pipe( + filter(definition => !!definition), + map(definition => definition.entityRequestInfo.fetching) + ); + } + + protected getBreadcrumbs( + endpoint: EndpointModel, + org: APIResource, + space: APIResource + ) { + const baseCFUrl = `/cloud-foundry/${this.cfGuid}`; + const baseOrgUrl = `${baseCFUrl}/organizations/${org.metadata.guid}`; + const baseSpaceUrl = `${baseOrgUrl}/spaces/${space.metadata.guid}`; + + const breadcrumbs: IHeaderBreadcrumb[] = [ + { + breadcrumbs: [ + { value: endpoint.name, routerLink: `${baseCFUrl}/organizations` }, + { value: org.entity.name, routerLink: `${baseOrgUrl}/spaces` }, + { value: space.entity.name, routerLink: `${baseSpaceUrl}/summary` }, + ], + }, + ]; + + return breadcrumbs; + } +} diff --git a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts index f84d138c43..9bfb30da63 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -26,18 +26,18 @@ export class CloudFoundryCellBaseComponent { { link: 'summary', label: 'Summary', - matIcon: 'description' + icon: 'description' }, { link: 'charts', label: 'Metrics', - matIcon: 'equalizer' + icon: 'equalizer' }, { link: CloudFoundryCellBaseComponent.AppsLinks, label: 'App Instances', - matIcon: 'application_instance', - matIconFont: 'stratos-icons' + icon: 'application_instance', + iconFont: 'stratos-icons' }, ]; @@ -72,7 +72,7 @@ export class CloudFoundryCellBaseComponent { first() ); - this.tabLinks.find(link => link.link === CloudFoundryCellBaseComponent.AppsLinks).hidden = + this.tabLinks.find(link => link.link === CloudFoundryCellBaseComponent.AppsLinks).hidden$ = cfEndpointService.currentUser$.pipe( map(user => !user.admin) ); diff --git a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-base/cloud-foundry-organization-base.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-base/cloud-foundry-organization-base.component.ts index 76302baa11..2b47da3082 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-base/cloud-foundry-organization-base.component.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-base/cloud-foundry-organization-base.component.ts @@ -40,17 +40,22 @@ export class CloudFoundryOrganizationBaseComponent { { link: 'summary', label: 'Summary', - matIcon: 'description' + icon: 'description' }, { link: 'spaces', label: 'Spaces', - matIcon: 'language' + icon: 'language' }, { link: 'users', label: 'Users', - matIcon: 'people' + icon: 'people' + }, + { + link: 'quota', + label: 'Quota', + icon: 'data_usage' } ]; public breadcrumbs$: Observable; diff --git a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts index d3b07a783d..727a517ac1 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts +++ b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy } from '@angular/core'; import { Store } from '@ngrx/store'; -import { combineLatest, Observable, Subscription } from 'rxjs'; +import { combineLatest, Observable, of, Subscription } from 'rxjs'; import { first, map, tap } from 'rxjs/operators'; import { CFAppState } from '../../../../../../../../cloud-foundry/src/cf-app-state'; @@ -45,35 +45,35 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy { { link: 'summary', label: 'Summary', - matIcon: 'description' + icon: 'description' }, { link: 'apps', label: 'Applications', - matIcon: 'apps' + icon: 'apps' }, { link: 'service-instances', label: 'Services', - matIconFont: 'stratos-icons', - matIcon: 'service' + iconFont: 'stratos-icons', + icon: 'service' }, { link: 'user-service-instances', label: 'User Services', - matIconFont: 'stratos-icons', - matIcon: 'service_square' + iconFont: 'stratos-icons', + icon: 'service_square' }, { link: 'routes', label: 'Routes', - matIconFont: 'stratos-icons', - matIcon: 'network_route' + iconFont: 'stratos-icons', + icon: 'network_route' }, { link: 'users', label: 'Users', - matIcon: 'people' + icon: 'people' } ]; @@ -90,6 +90,8 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy { private deleteRedirectSub: Subscription; + private quotaLinkSub: Subscription; + public extensionActions: StratosActionMetadata[] = getActionsFromExtensions(StratosActionType.CloudFoundryOrg); public favorite$: Observable>; @@ -129,7 +131,22 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy { ).subscribe(); // Add any tabs from extensions - this.tabLinks = this.tabLinks.concat(getTabsFromExtensions(StratosTabType.CloudFoundrySpace)); + this.setupLinks(); + } + + private setupLinks() { + this.quotaLinkSub = this.cfSpaceService.space$.pipe( + tap((space) => { + this.tabLinks.push({ + link: 'space-quota', + label: 'Quota', + icon: 'data_usage', + hidden$: of(!space.entity.entity.space_quota_definition) + }); + this.tabLinks = this.tabLinks.concat(getTabsFromExtensions(StratosTabType.CloudFoundrySpace)); + }), + first() + ).subscribe(); } private setUpBreadcrumbs( @@ -167,6 +184,7 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy { ngOnDestroy() { this.deleteRedirectSub.unsubscribe(); + this.quotaLinkSub.unsubscribe(); } deleteSpaceWarn = () => { diff --git a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.scss b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.scss index 6db3213d8a..e69de29bb2 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.scss +++ b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.scss @@ -1,7 +0,0 @@ -app-loading-page { - position: relative; - - app-tile-group { - margin-bottom: 24px; - } -} diff --git a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-summary/cloud-foundry-organization-summary.component.scss b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-summary/cloud-foundry-organization-summary.component.scss index 6db3213d8a..e69de29bb2 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-summary/cloud-foundry-organization-summary.component.scss +++ b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-organizations/cloud-foundry-organization-summary/cloud-foundry-organization-summary.component.scss @@ -1,7 +0,0 @@ -app-loading-page { - position: relative; - - app-tile-group { - margin-bottom: 24px; - } -} diff --git a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component.scss b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component.scss index 00f04cff83..dc360c8f01 100644 --- a/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component.scss +++ b/src/frontend/packages/core/src/features/cloud-foundry/tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component.scss @@ -2,13 +2,4 @@ display: flex; min-height: 100%; width: 100%; - - app-loading-page { - position: relative; - - app-tile-group { - margin-bottom: 24px; - } - } - } diff --git a/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.html b/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.html index b16004c4e1..09f01fda05 100644 --- a/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.html +++ b/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.html @@ -9,11 +9,11 @@
  • - - {{ tab.matIcon }} + {{ tab.icon }} - + {{ tab.label }}
  • diff --git a/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.spec.ts b/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.spec.ts index 70ff27cf1e..8ada83ed4d 100644 --- a/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.spec.ts +++ b/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.spec.ts @@ -2,6 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TabNavService } from '../../../../tab-nav.service'; import { BaseTestModulesNoShared } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { EntityMonitorFactory } from '../../../shared/monitors/entity-monitor.factory.service'; import { PageSideNavComponent } from './page-side-nav.component'; describe('PageSideNavComponent', () => { @@ -12,7 +13,7 @@ describe('PageSideNavComponent', () => { TestBed.configureTestingModule({ imports: [BaseTestModulesNoShared], declarations: [PageSideNavComponent], - providers: [TabNavService] + providers: [TabNavService, EntityMonitorFactory] }) .compileComponents(); })); diff --git a/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.ts b/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.ts index 1cb1ecab8b..d4088e65d8 100644 --- a/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.ts +++ b/src/frontend/packages/core/src/features/dashboard/page-side-nav/page-side-nav.component.ts @@ -1,18 +1,17 @@ -import { Component, OnInit, Input } from '@angular/core'; -import { Observable } from 'rxjs'; -import { IBreadcrumb } from '../../../shared/components/breadcrumbs/breadcrumbs.types'; -import { TabNavService } from '../../../../tab-nav.service'; +import { Component, Input, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; -import { DashboardOnlyAppState } from '../../../../../store/src/app-state'; +import { Observable, of } from 'rxjs'; + +import { AppState } from '../../../../../store/src/app-state'; import { selectIsMobile } from '../../../../../store/src/selectors/dashboard.selectors'; +import { TabNavService } from '../../../../tab-nav.service'; +import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; +import { StratosTabMetadata } from '../../../core/extension/extension-service'; +import { IBreadcrumb } from '../../../shared/components/breadcrumbs/breadcrumbs.types'; -export interface IPageSideNavTab { - key?: string; - label: string; - matIcon?: string; - matIconFont?: string; - link: string; - hidden?: Observable; +export interface IPageSideNavTab extends StratosTabMetadata { + hidden$?: Observable; } @Component({ @@ -22,19 +21,33 @@ export interface IPageSideNavTab { }) export class PageSideNavComponent implements OnInit { - @Input() - public tabs: IPageSideNavTab[]; + pTabs: IPageSideNavTab[]; + @Input() set tabs(tabs: IPageSideNavTab[]) { + if (!tabs || (this.pTabs && tabs.length === this.pTabs.length)) { + return; + } + this.pTabs = tabs.map(tab => ({ + ...tab, + hidden$: tab.hidden$ || (tab.hidden ? tab.hidden(this.store, this.esf, this.activatedRoute) : of(false)) + })); + } + get tabs(): IPageSideNavTab[] { + return this.pTabs; + } @Input() public header: string; public activeTab$: Observable; public breadcrumbs$: Observable; - public isMobile$ = this.store.select(selectIsMobile); - + public isMobile$: Observable; constructor( public tabNavService: TabNavService, - private store: Store - ) { } + private store: Store, + private esf: EntityServiceFactory, + private activatedRoute: ActivatedRoute, + ) { + this.isMobile$ = this.store.select(selectIsMobile); + } ngOnInit() { this.activeTab$ = this.tabNavService.getCurrentTabHeaderObservable(); diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.html b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.html index 60cab3c3bb..0ca0904907 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.html +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.html @@ -6,9 +6,10 @@ Connect to {{ connectService?.config.name }} now (optional). -
    You may connect this endpoint at a later date from the - endpoints page.
    +
    You may connect this endpoint at a later date from the + endpoints page. +
diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.scss b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.scss index 7cbbe92dd3..f8b7fcc146 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.scss +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-connect/create-endpoint-connect.component.scss @@ -26,7 +26,7 @@ } &__subtext { font-size: 13px; - margin-top: -5px; + margin: 2px 0 0 24px; opacity: .5; position: absolute; } diff --git a/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts b/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts index 33dae6ae80..d7eb048bf1 100644 --- a/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts +++ b/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts @@ -5,6 +5,7 @@ import { first, map } from 'rxjs/operators'; import { RouterNav } from '../../../../../store/src/actions/router.actions'; import { EndpointOnlyAppState } from '../../../../../store/src/app-state'; +import { selectDashboardState } from '../../../../../store/src/selectors/dashboard.selectors'; import { CurrentUserPermissions } from '../../../core/current-user-permissions.config'; import { EndpointsService } from '../../../core/endpoints.service'; import { @@ -53,7 +54,6 @@ export class EndpointsPageComponent implements OnDestroy, OnInit { public extensionActions: StratosActionMetadata[] = getActionsFromExtensions(StratosActionType.Endpoints); private startEndpointHealthCheckPulse() { - this.endpointsService.checkAllEndpoints(); this.ngZone.runOutsideAngular(() => { this.healthCheckTimeout = window.setInterval(() => { this.ngZone.run(() => { @@ -68,7 +68,14 @@ export class EndpointsPageComponent implements OnDestroy, OnInit { } ngOnInit() { - this.startEndpointHealthCheckPulse(); + this.endpointsService.checkAllEndpoints(); + this.store.select(selectDashboardState).pipe( + first() + ).subscribe(dashboard => { + if (dashboard.pollingEnabled) { + this.startEndpointHealthCheckPulse(); + } + }); // Doesn't look like this is used (see connect-endpoint-dialog.component for actual handler) // const params = queryParamMap(); // if (params.cnsi_guid) { diff --git a/src/frontend/packages/core/src/features/service-catalog/service-tabs-base/service-tabs-base.component.ts b/src/frontend/packages/core/src/features/service-catalog/service-tabs-base/service-tabs-base.component.ts index cb97f11c29..daff063194 100644 --- a/src/frontend/packages/core/src/features/service-catalog/service-tabs-base/service-tabs-base.component.ts +++ b/src/frontend/packages/core/src/features/service-catalog/service-tabs-base/service-tabs-base.component.ts @@ -24,19 +24,19 @@ export class ServiceTabsBaseComponent { { link: 'summary', label: 'Summary', - matIcon: 'description' + icon: 'description' }, { link: 'instances', label: 'Instances', - matIcon: 'service_instance', - matIconFont: 'stratos-icons' + icon: 'service_instance', + iconFont: 'stratos-icons' }, { link: 'plans', label: 'Plans', - matIcon: 'service_plan', - matIconFont: 'stratos-icons' + icon: 'service_plan', + iconFont: 'stratos-icons' } ]; diff --git a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html index f29d4e7962..deb4e8a6ae 100644 --- a/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html +++ b/src/frontend/packages/core/src/features/user-profile/profile-info/profile-info.component.html @@ -39,23 +39,44 @@

User Profile

Settings -