diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eecd45a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+config/build-context/context.tar.gz
+node_modules
+dist
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..32ef673
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,2 @@
+registry=https://registry.npmjs.org/
+save-exact=true
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0bfb4ec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Peter Mikitsh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index a694eee..4052870 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,93 @@

+
-> :snowflake: **S**elf-hosted **now** deployments
+> :snowflake: **S**elf-hosted **now** deployments
-Enjoy effortless deployments with a clone of [now](https://github.com/zeit/now-cli) on your own hardware.
+Enjoy effortless deployments with a clone of [now] on a cloud of your choosing.
+
+### Features
+
+- ⚡️ Deploy docker images via `snow` (or `snow deploy`)
+- 🔒 Auto-configured SSL
+- 🔃 Auto-scaling
+
+### This is Magic 🔮
+
+No, it isn't. This CLI abstracts away the complexities of using [Kubernetes], [cert-manager], [Kaniko], and a private [Docker Registry] together, replicating the functionality provided by `now`.
### Getting started
-```
-curl -sSL https://get.snowjs.app/ | sh
-npm i -g snow-cli
-snow
+```sh
+> npm i -g @snowjs/cli
+
+# Install CLI tools
+> snow install
+
+# Create your kubernetes cluster (GCP)
+> snow create
+
+# Get your deployment's IP address
+# And create a DNS 'A' record (e.g., myapp.com A 1.2.3.4)
+> snow ip
+
+# Deploy
+> snow
```
### Supported commands
-Without arguments, deploys just like `now`.
-
-| Support | Command | Description |
-|---------|------------------------|-------------------|
-| :x: | \ | Deploy |
-| :x: | `alias` | Alias deployment |
-| :x: | `deploy` | Deploy |
-| :x: | `domains` | List domains |
-| :x: | `domains add [domain]` | Add domain |
-| :x: | `domains rm [domain]` | Remove domain |
-| :x: | `login` | Login |
-| :x: | `logout` | Logout |
-| :x: | `ls` | List deployments |
-| :x: | `rm [name]` | Remove deployment |
-### Flags
-
-| Support | Command | Flag | Description |
-|---------|---------|-------------------|----------------------|
-| :x: | \ | `--token [token]` | Authentication token |
-| :x: | `login` | `--host [host]` | Snow server hostname |
\ No newline at end of file
+**Detailed** descriptions of supported commands at [docs/commands.md](docs/commands.md).
+
+| Support | Command | Description |
+| ------------------ | ------------------------------------ | ----------------------------- |
+| :white_check_mark: | \ | Deploy |
+| :white_check_mark: | `alias [ls]` | List aliases |
+| :white_check_mark: | `alias set ` | Create alias |
+| :white_check_mark: | `alias rm ` | Remove alias |
+| :white_check_mark: | `certs [ls]` | List SSL Certificates |
+| :white_check_mark: | `certs issue []` | Issue certificate |
+| :white_check_mark: | `certs rm ` | Remove a certificate |
+| :new: | `create` | Create Kubernetes cluster |
+| :white_check_mark: | `deploy` | Deploy |
+| :white_check_mark: | `domains [ls]` | List domains |
+| :white_check_mark: | `domains add ` | Add domain |
+| :no_entry: | `domains buy ` | Buy domain |
+| :white_check_mark: | `domains rm ` | Remove domain |
+| :white_check_mark: | `login` | Login |
+| :white_check_mark: | `logout` | Logout |
+| :white_check_mark: | `ls` | List deployments |
+| :new: | `install` | Install CLI tools (via brew) |
+| :new: | `ip` | Get IP Address of deployments |
+| :white_check_mark: | `rm ` | Remove deployment |
+| :white_check_mark: | `scale []` | Scale deployment |
+| :white_check_mark: | `secrets [ls]` | List secrets |
+| :white_check_mark: | `secrets add ` | Create secret |
+| :white_check_mark: | `secrets rename ` | Rename secret |
+| :white_check_mark: | `secrets rm ` | Remove secret |
+
+### Tell me more
+
+The essential CLI commands to understand are `snow create` and `snow deploy`.
+
+### Dependencies
+
+The following CLI tools (installable via `snow install`) are necessary to orchestrate the entire end-to-end process, from Kubernetes cluster creation to managing your deployments:
+
+- `kubectl` (for managing deployments, secrets)
+- `helm` (for installing [tiller], [cert-manager], and [ingress-nginx] on your cluster)
+- CLI tool for your cloud provider (e.g., `gcloud`).
+
+If running Kubernetes locally on Minikube, you will additionally need these cli tools:
+
+- `docker` (for running local registry)
+- `minikube` (for running Kubernetes locally)
+- `virtualbox` (for creating docker images)
+
+[cert-manager]: https://github.com/jetstack/cert-manager
+[docker registry]: https://github.com/helm/charts/tree/master/stable/docker-registry
+[now]: https://github.com/zeit/now-cli
+[ingress-nginx]: https://github.com/kubernetes/ingress-nginx
+[kaniko]: https://github.com/GoogleContainerTools/kaniko
+[kubernetes]: https://kubernetes.io/
diff --git a/config/build-context/Dockerfile b/config/build-context/Dockerfile
new file mode 100644
index 0000000..ac52609
--- /dev/null
+++ b/config/build-context/Dockerfile
@@ -0,0 +1,3 @@
+FROM nginx:alpine
+COPY index.html /usr/share/nginx/html/index.html
+EXPOSE 80
\ No newline at end of file
diff --git a/config/build-context/README.md b/config/build-context/README.md
new file mode 100644
index 0000000..7e8b94c
--- /dev/null
+++ b/config/build-context/README.md
@@ -0,0 +1,3 @@
+# build-context
+
+This directory is a minimal example of everything you need to create a deployment.
diff --git a/config/build-context/index.html b/config/build-context/index.html
new file mode 100644
index 0000000..5800c67
--- /dev/null
+++ b/config/build-context/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Hello World - Nginx Docker
+
+
+
+ Hello World - Deployed with Snow
+
+
diff --git a/config/build-context/now.json b/config/build-context/now.json
new file mode 100644
index 0000000..92e343f
--- /dev/null
+++ b/config/build-context/now.json
@@ -0,0 +1,9 @@
+{
+ "name": "my-example",
+ "files": [
+ "index.html"
+ ],
+ "alias": [
+ "my-example.minikube"
+ ]
+}
diff --git a/config/deployment-gcp.yaml b/config/deployment-gcp.yaml
new file mode 100644
index 0000000..b31b328
--- /dev/null
+++ b/config/deployment-gcp.yaml
@@ -0,0 +1,50 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: helloweb
+ labels:
+ app: hello
+spec:
+ selector:
+ matchLabels:
+ app: hello
+ tier: web
+ template:
+ metadata:
+ labels:
+ app: hello
+ tier: web
+ spec:
+ containers:
+ - name: hello-app
+ image: gcr.io/google-samples/hello-app:1.0
+ ports:
+ - containerPort: 8080
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: helloweb
+ annotations:
+ kubernetes.io/ingress.global-static-ip-name: helloweb-ip
+ labels:
+ app: hello
+spec:
+ backend:
+ serviceName: helloweb-backend
+ servicePort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: helloweb-backend
+ labels:
+ app: hello
+spec:
+ type: NodePort
+ selector:
+ app: hello
+ tier: web
+ ports:
+ - port: 8080
+ targetPort: 8080
diff --git a/config/deployment-minikube.yaml b/config/deployment-minikube.yaml
new file mode 100644
index 0000000..a200cbf
--- /dev/null
+++ b/config/deployment-minikube.yaml
@@ -0,0 +1,63 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: whoami-deployment
+spec:
+ replicas: 5
+ selector:
+ matchLabels:
+ app: whoami
+ template:
+ metadata:
+ labels:
+ app: whoami
+ spec:
+ containers:
+ - name: whoami-container
+ image: containous/whoami
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: whoami-clusterip-service
+spec:
+ type: ClusterIP
+ ports:
+ - name: http
+ targetPort: 80
+ port: 80
+ selector:
+ app: whoami
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: whoami
+ # annotations:
+ # kubernetes.io/ingress.class: traefik
+spec:
+ # THE Below will make a 'global default service'
+ # backend:
+ # serviceName: whoami
+ # servicePort: 80
+ rules:
+ # whoami.minikube must be in your hosts file (pointing to `minikube ip`)
+ - host: whoami.minikube
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: whoami-clusterip-service
+ servicePort: 80
+ # whoami.com must be in your hosts file (pointing to `minikube ip`)
+ - host: whoami.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: whoami-clusterip-service
+ servicePort: 80
+ # tls:
+ # - hosts:
+ # - whoami.com
+ # - whoami.minikube
diff --git a/config/kaniko.yaml b/config/kaniko.yaml
new file mode 100644
index 0000000..16651cf
--- /dev/null
+++ b/config/kaniko.yaml
@@ -0,0 +1,71 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: kaniko
+spec:
+ initContainers:
+ - name: kaniko-init
+ image: alpine
+ args:
+ - "sh"
+ - "-c"
+ - "while true; do sleep 1; if [ -f /tmp/complete ]; then break; fi done"
+ volumeMounts:
+ - name: empty-folder
+ mountPath: /kaniko/build-context
+ - name: empty-folder
+ mountPath: /kaniko/.docker
+ containers:
+ - name: kaniko
+ image: gcr.io/kaniko-project/executor:latest
+ args:
+ - "--context=dir:///kaniko/build-context"
+ - "--destination=172.17.0.15:5000/foobar:latest"
+ - "--insecure"
+ volumeMounts:
+ - name: empty-folder
+ mountPath: /kaniko/build-context
+ - name: empty-folder
+ mountPath: /kaniko/.docker
+ volumes:
+ - name: empty-folder
+ emptyDir: {}
+
+# apiVersion: v1
+# kind: Pod
+# metadata:
+# name: kaniko
+# spec:
+# initContainers:
+# - name: kaniko-init
+# image: alpine
+# args:
+# - "sh"
+# - "-c"
+# - "while true; do sleep 1; if [ -f /tmp/complete ]; then break; fi done"
+# volumeMounts:
+# - name: build-context
+# mountPath: /kaniko/build-context
+# containers:
+# - name: kaniko
+# image: gcr.io/kaniko-project/executor:latest
+# args:
+# - "--dockerfile=Dockerfile"
+# - "--context=dir:///kaniko/build-context"
+# - "--destination=172.17.0.15:5000/foobar:latest"
+# - "--insecure"
+# # - "--skip-tls-verify"
+# # - "--tarPath=dir://kaniko/foo.tar"
+# # - "--no-push"
+# # - "--destination=mytag:latest"
+# volumeMounts:
+# - name: build-context
+# mountPath: /kaniko/build-context
+# - name: docker-config
+# mountPath: /kaniko/.docker
+# volumes:
+# - name: docker-config
+# configMap:
+# name: docker-config
+# - name: build-context
+# emptyDir: {}
diff --git a/demo.png b/demo.png
new file mode 100644
index 0000000..d6f186b
Binary files /dev/null and b/demo.png differ
diff --git a/docs/commands.md b/docs/commands.md
new file mode 100644
index 0000000..32ceac2
--- /dev/null
+++ b/docs/commands.md
@@ -0,0 +1,142 @@
+# CLI Commands
+
+Below is a description of each command. While you normally won't need exposure to the implementation details of each command, having a working knowledge of Kubernetes will be beneficial in understanding their behavior.
+
+## snow alias [ls]
+
+List all aliases (hostnames that map to deployments).
+
+## snow alias set \ \
+
+Configures your Kubernetes cluster so hostname `alias` points to a deployment named `deployment`.
+
+## snow alias rm \
+
+Remove host name `alias` from any deployment in may point to, and any associated SSL configuration for `alias`. If you continue to have a DNS A record for `alias` that points to your Kubernetes cluster, all responses for hostname `alias` will be served with the [default certificate].
+
+## snow certs [ls]
+
+Lists all certificates.
+
+## snow certs issue \ [\]
+
+Creates a certificate with the first common name (`cn`) listed. The first and all subsequent `cn` are listed in the certificate's Subject Alternative Names (SANs) section.
+
+This allows you to create a single certificate for multiple domain names.
+
+## snow certs rm \
+
+Delete a Kubernetes `certificate` object with id `cn`. Does _not_ delete the Kubernetes `secret` holding the certificate/private key pair.
+
+## snow create
+
+Creates a Kubernetes cluster to host your deployments.
+
+- You are authenticated to the cloud provider of your choosing.
+- A Kubernetes cluster is created.
+- [tiller][helm] is installed, which is used to install:
+ - [cert-manager]
+ - Will automatically watch your deployments and request new certificates for hostnames.
+ - Request new certificates when they're closing to expiring and seamlessly update in production.
+ - [ingress-nginx]
+ - Installing ingress-nginx will instantiate a load balancer, e.g., on GCP this instantiate a [TCP Proxy Load Balancer].
+ - The nginx controller is a Kubernetes object of type `service`.
+ - The type of service is `LoadBalancer`.
+ - Used for mapping hostnames to deployments.
+ - All HTTP traffic is permanently redirected (HTTP 308) to HTTPS.
+ - SSL termination occurs prior to requests reaching deployments.
+
+## snow [deploy]
+
+Also aliased as `snow`. Your current directory must have both `Dockerfile` and `now.json` files. Example `now.json`:
+
+```json
+{
+ "name": "myapp", // required
+ "alias": ["api.myapp.com", "myapp.com"],
+ "files": ["server.js"]
+}
+```
+
+The deployment process:
+
+- The files listed in `now.json` plus `Dockerfile` (collectively referred to as the "build context") are assembled into a tar archive.
+- [Kaniko] creates a Docker image from the build context, and pushes it to the private Docker registry in your Kubernetes cluster.
+- A Kubernetes `deployment` resource is created for your image.
+- A Kubernetes `service` resource exposes your deployment.
+- A Kubernetes `ingress` resource maps hostnames (listed as `alias` array in now.json) to the service.
+- [cert-manager] continually inspects ingresses, so if a deployment needs SSL certificates, they will be generated upon deployment.
+
+For your domain name to be resolvable by Kubernetes, you **must**:
+
+- create a DNS `A` record, which points to the IP Address of your load balancer (which can be found via `snow ip`).
+- Alias the domain name to a deployment.
+
+## snow domains [ls]
+
+List all configured domain names.
+
+## snow domains add \
+
+Verifies DNS records are configure properly for `domain`. Creates rule to redirect traffic from `domain` to the [default backend]. Requests an SSL certificate from Let's Encrypt, if one is not present in the cluster, and sets up SSL termination.
+
+## snow domains rm \
+
+Removes any traffic redirect rules from `domain`. Removes SSL termination with the Let's Encrypt SSL certificate for `domain` (the [default certificate] will be used instead).
+
+The Let's Encrypt SSL certificate will remain persisted (which is helpful if the domain is added later, and it avoids an unnecessary request for a new certificate: requests are limited by Let's Encrypt [rate limits]). Traffic from `domain` will redirect to the [default backend].
+
+## snow install
+
+Install dependencies using `brew`.
+
+## snow ip
+
+Prints the IP address of your cluster's load balancer. You'll need this for configuring DNS `A` records that point to your load balancer.
+
+## snow login
+
+Asks which cloud provider you are using, and configures your kube config file (typically at `~/.kube/config`). Since you will need credentials to your Kubernetes cluster to perform operations on it, you must login (or have a properly configured kube file) prior to running snow commands.
+
+## snow logout
+
+Remove all credentials from your kube config file (typically at `~/.kube/config`).
+
+## snow ls
+
+List all deployments.
+
+## snow rm \
+
+Deplete deployment `name`.
+
+## snow scale \ \ [\]
+
+Configure a deployment named `deployment` to scale to a `min` / `max` number of instances. If no `max` is specified, the `min` is used as the `max`.
+
+## snow secrets [ls]
+
+List all secrets. This will only show secrets created by the snow API, which are annotated in Kubernetes with label `snowsecret=true`.
+
+## snow secrets add \ \
+
+Create a secret named `key`.
+
+## snow secrets rename \ \
+
+Rename a secret from `old-key` to `new-key`. In Kubernetes, this means removing the old key, and creating a new key with identical info.
+
+## snow secrets rm \
+
+Delete a secret with name `key`.
+
+[cert-manager]: https://github.com/jetstack/cert-manager
+[default backend]: https://kubernetes.github.io/ingress-nginx/user-guide/default-backend/
+[default certificate]: https://kubernetes.github.io/ingress-nginx/user-guide/tls/#default-ssl-certificate
+[helm]: https://docs.helm.sh/
+[ingress]: https://kubernetes.io/docs/concepts/services-networking/ingress/
+[ingress-nginx]: https://github.com/kubernetes/ingress-nginx
+[kaniko]: https://github.com/GoogleContainerTools/kaniko
+[minikube]: https://kubernetes.io/docs/setup/minikube/
+[tcp proxy load balancer]: https://cloud.google.com/load-balancing/docs/choosing-load-balancer
+[rate limits]: https://letsencrypt.org/docs/rate-limits/
diff --git a/docs/comparison.md b/docs/comparison.md
new file mode 100644
index 0000000..3720bb7
--- /dev/null
+++ b/docs/comparison.md
@@ -0,0 +1,61 @@
+# Snow vs Now
+
+There are tradeoffs to using `snow` and `now`.
+
+## Features
+
+| Feature | ❄ snow | 𝚫 now | Details |
+| ------------------------------ | ------------------ | --------------------- | ------------------------------------------------ |
+| Doesn't require DNS TXT record | :white_check_mark: | :x: | Needed by now to validate domain ownership. |
+| Autoscaling | :white_check_mark: | :white_check_mark: \* | [Now plans] Pro & Advanced only. |
+| SSL Termination | :white_check_mark: | :white_check_mark: | Certificates self-renew; both use Let's Encrypt. |
+| Wildcard Certificates | :x: | :white_check_mark: \* | Supported on Now when using Zeit's name servers. |
+| Multi-region deployments | :x: | :white_check_mark: | Snow: Dependent upon [Multicluster SIG]. |
+| Internal deployments | :white_check_mark: | :x: | Deploy internally accessible Dockerfiles. |
+| HTTP(S)/WS(S) connections | :white_check_mark: | :white_check_mark: | Insecure connections are upgraded. |
+| TCP connections | :white_check_mark: | :x: | Useful for deploying things like Redis. |
+| Serverless lambdas | :x: | :white_check_mark: \* | Now v2 only. |
+
+### Multi-region deployments
+
+Now's Anycast DNS service and load balancers allows serving content as close as possible to users. Since the load balancer implementations used by Kubernetes provide similar functionality (e.g., [Google Cloud Load Balancing]), it may be possible to provide this functionality in the future when the [Multicluster SIG] API's reach general availability. A beta [multicluster ingress](`kubemci`) serves as a working proof-of-concept.
+
+### Internal deployments
+
+On snow, each deployment is associated with a kubernetes service. Each service is assigned an address from the service [address range] (e.g., `10.0.0.0/20`). You can use this IP Address to talk between deployments, without exposing the deployment on the public internet.
+
+On some cloud providers, by keeping traffic between deployments in the same zone/region, you might save on network ingress/egress costs as well.
+
+### Serverless lambdas
+
+Today, Serverless lambdas are out of scope for `snow`.
+
+## Operating Cost
+
+Costs will vary depend on which cloud you choose. These are minimal costs-- in a production setting, you might want a minimum of 2 compute instances, for example, to ensure high availability.
+
+Cost table for Google Cloud Platform in region `us-east4`.
+
+| Resource | Count | Cost |
+| ----------------------------------------- | ----- | ---------- |
+| `g1-small` compute instance (1vCPU/1.7GB) | 1 | \$14.77 |
+| Load balancer w/ 100GB Network Ingress | 1 | \$21.34 |
+| 100GB Network Egress - Americas | 100GB | \$11.88 |
+| Persistent Disk | 8GB | \$0.35 |
+| Total | | \$48.34/mo |
+
+Cost table for Digital Ocean in `nyc1`:
+
+| Resource | Count | Cost |
+| ------------------------------------------- | ----- | ---------- |
+| `s-1vcpu-2gb` standard droplet (1vCPU/2GB) | 1 | \$10.00 |
+| Load Balancer w/ ∞GB Network Ingress | 1 | \$10.00 |
+| 100GB Network Egress (first 1000GB/mo free) | 100GB | \$0.00 |
+| Block Storage (\$0.10/GB/mo) | 8GB | \$0.80 |
+| Total | | \$20.80/mo |
+
+[multicluster ingress]: https://github.com/GoogleCloudPlatform/k8s-multicluster-ingress
+[google cloud load balancing]: https://cloud.google.com/load-balancing/docs/choosing-load-balancer
+[address range]: https://www.mediawiki.org/wiki/Help:Range_blocks#Table_of_sample_ranges
+[now plans]: https://zeit.co/pricing/v1
+[multicluster sig]: https://github.com/kubernetes/community/tree/master/sig-multicluster
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..487fdca
--- /dev/null
+++ b/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "@snowjs/cli",
+ "version": "0.0.0",
+ "description": "Self-hosted now deployments",
+ "main": "dist/index.js",
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "tsc",
+ "watch": "tsc --watch --preserveWatchOutput",
+ "lint": "tslint -p . --format stylish"
+ },
+ "bin": {
+ "snow": "dist/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/snowjs/cli.git"
+ },
+ "author": "Peter Mikitsh ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/snowjs/cli/issues"
+ },
+ "homepage": "https://github.com/snowjs/cli#readme",
+ "dependencies": {
+ "chalk": "2.4.1",
+ "docker-file-parser": "1.0.4",
+ "mri": "1.1.1",
+ "source-map-support": "0.5.10"
+ },
+ "devDependencies": {
+ "@types/mri": "1.1.0",
+ "@types/node": "10.12.18",
+ "@types/source-map-support": "0.4.2",
+ "husky": "1.3.1",
+ "lint-staged": "8.1.0",
+ "prettier": "1.15.3",
+ "tslint": "5.12.0",
+ "tslint-config-prettier": "1.17.0",
+ "tslint-xo": "0.11.0",
+ "typescript": "3.2.2"
+ },
+ "prettier": {
+ "singleQuote": true
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "*.{js,json,css,md}": [
+ "prettier --write",
+ "git add"
+ ]
+ }
+}
diff --git a/src/alias.ts b/src/alias.ts
new file mode 100644
index 0000000..b7bff8f
--- /dev/null
+++ b/src/alias.ts
@@ -0,0 +1,156 @@
+import {domainHasCorrectDNSRecords} from './domains';
+import { exec, isRemove, logError, run } from './utils';
+
+export default async (subcommand?: string, aliasOrDeploymentPrefix?: string, hostname?: string) => {
+ interface IRule {
+ host: string;
+ }
+
+ interface ITLS {
+ hosts: string[];
+ }
+
+ function hasExistingRuleFactory(host?: string) {
+ return function hasExistingRule(rule: IRule) {
+ return rule.host === host;
+ };
+ }
+
+ function hasExistingTLSFactory(host?: string) {
+ return function hasExistingTLS(tls: ITLS) {
+ if (!host) {
+ return false;
+ }
+ return tls.hosts.indexOf(host) !== -1;
+ };
+ }
+
+ if (!subcommand || subcommand === 'ls') {
+ await run('kubectl get ingress/snow-ingress -o=jsonpath=\"{\'Host\'} {\'\'}{\'\\n\'}{range .spec.rules[*]}{.host} {.http.paths[*].backend.serviceName}{\'\\n\'}{end}\" | column -t');
+ return;
+ }
+
+ if (subcommand === 'set') {
+ const deployment = aliasOrDeploymentPrefix;
+
+ if (!aliasOrDeploymentPrefix) {
+ logError('deployment (required): snow alias set ');
+ return;
+ }
+
+ if (!hostname) {
+ logError('alias (required): snow alias set ');
+ return;
+ }
+
+ // Verify the domain name has the correct DNS records.
+ try {
+ await domainHasCorrectDNSRecords(hostname);
+ } catch (e) {
+ return;
+ }
+
+ // Given a deployment name, verify both a deployment and service exist.
+ try {
+ await run(`kubectl get service/${deployment}`);
+ } catch (e) {
+ logError(`No service exists for deployment '${deployment}'`);
+ return;
+ }
+
+ try {
+ await run(`kubectl get deployment/${deployment}`);
+ } catch (e) {
+ logError(`No deployment exists for '${deployment}'`);
+ return;
+ }
+
+ // Fetch the ingress spec.
+ const { stdout } = await exec('kubectl get ingress/snow-ingress -o json');
+ const ingressSpec = JSON.parse(stdout).spec;
+
+ const rule = {
+ host: hostname,
+ http: {
+ paths: [
+ {
+ backend: {
+ serviceName: deployment,
+ servicePort: 8080
+ }
+ }
+ ]
+ }
+ };
+
+ // See if an existing rule exists.
+ const ruleIndex = ingressSpec.rules.findIndex(hasExistingRuleFactory(hostname));
+
+ if (ruleIndex === -1) {
+ // No rule exists. Create one.
+ ingressSpec.rules.push(rule);
+ } else {
+ // Update existing rule.
+ ingressSpec.rules[ruleIndex] = rule;
+ }
+
+ if (!ingressSpec.tls) {
+ ingressSpec.tls = [];
+ }
+
+ // See if an existing TLS entry exists.
+ const tlsIndex = ingressSpec.tls.findIndex(hasExistingTLSFactory(hostname));
+ if (tlsIndex === -1) {
+ ingressSpec.tls.push({
+ hosts: [hostname],
+ secretName: hostname
+ });
+ }
+
+ const ingressPatchString = JSON.stringify({spec: ingressSpec});
+
+ // Patch the ingress resource.
+ await run(`kubectl patch ingress/snow-ingress --patch '${ingressPatchString}'`);
+
+ return;
+ }
+
+ if (isRemove(subcommand)) {
+ const alias = aliasOrDeploymentPrefix;
+
+ if (!alias) {
+ logError('alias (required): snow alias rm ');
+ return;
+ }
+
+ // Fetch the ingress spec.
+ const { stdout } = await exec('kubectl get ingress/snow-ingress -o json');
+ const ingressSpec = JSON.parse(stdout).spec;
+
+ // See if an existing rule exists.
+ const ruleIndex = ingressSpec.rules.findIndex(hasExistingRuleFactory(alias));
+
+ if (ruleIndex !== -1) {
+ ingressSpec.rules.splice(ruleIndex, 1);
+ }
+
+ // See if an existing TLS entry exists.
+ const tlsIndex = ingressSpec.tls.findIndex(hasExistingTLSFactory(alias));
+
+ /* Only remove the TLS entry if it exists as a certificate with
+ * a single hostname (e.g., the certificate has no SANs).
+ */
+ if (tlsIndex !== -1 && ingressSpec.tls[tlsIndex].hosts.length === 1) {
+ ingressSpec.tls.splice(tlsIndex, 1);
+ }
+
+ const ingressPatchString = JSON.stringify({spec: ingressSpec});
+
+ // Patch the ingress resource.
+ await run(`kubectl patch ingress/snow-ingress --patch '${ingressPatchString}'`);
+
+ return;
+ }
+
+ logError('Invalid usage');
+};
diff --git a/src/certs.ts b/src/certs.ts
new file mode 100644
index 0000000..5a2eaea
--- /dev/null
+++ b/src/certs.ts
@@ -0,0 +1,51 @@
+import { isRemove, logError, run } from './utils';
+
+export default async function certs(subcommand?: string, commonName?: string, ...subAltNames: string[]) {
+ if (!subcommand || subcommand === 'ls') {
+ await run('kubectl get certificates -o=custom-columns="Common Name":.metadata.name,SANs:.spec.dnsNames[*],"Created At":.metadata.creationTimestamp');
+ return;
+ }
+
+ if (subcommand === 'issue') {
+ if (!commonName) {
+ logError('cn (required): snow certs []');
+ return;
+ }
+ const domains = [commonName, ...subAltNames].map(domain => `"${domain}"`).join();
+ await run(`
+ cat < {
+ const question = 'Which cloud provider are you hosting with';
+ const provider = await pickOne(question, cloudProviders);
+
+ if (!opts['skip-provision']) {
+ switch (provider) {
+ case 'minikube': {
+ logInfo('Note: Minikube is for development purposes only.');
+
+ // Start minikube if it isn't running
+ try {
+ await run('minikube status');
+ } catch (error) {
+ logInfo('Starting Minikube. This may take a minute.');
+ await run('minikube start');
+ }
+
+ await run('minikube addons enable ingress');
+ await run('helm init --wait');
+
+ const { stdout: minikubeIP } = await run('minikube ip');
+
+ // Install traefik
+ await run(`
+ helm install stable/traefik \
+ --name traefik \
+ --namespace kube-system \
+ --set serviceType=NodePort \
+ --set dashboard.enabled=true \
+ --set dashboard.domain=traefik-ui.minikube
+ `);
+ const traefikHost = `${minikubeIP} traefik-ui.minikube`;
+ logInfo(
+ `Add "${traefikHost}" to your hosts file to access traefik's dashboard.`
+ );
+
+ // Install docker registry
+ await run('helm install stable/docker-registry');
+
+ if (await confirm('Install an example app on Minikube')) {
+ const deployFile = path.resolve(
+ __dirname,
+ '../config/deployment-minikube.yaml'
+ );
+ await run(`kubectl apply -f ${deployFile}`);
+ const whoAmIHost = `${minikubeIP} whoami.minikube`;
+ logInfo(
+ `Add "${whoAmIHost}" to your hosts file to access example app.`
+ );
+ }
+
+ const completeMsg =
+ 'Creation complete. It may take a few minutes for services to become available.';
+ logInfo(completeMsg);
+ break;
+ }
+ case 'gcp': {
+ // Check if we need to authenticate.
+ try {
+ const { stdout } = await run('gcloud auth list');
+ const isAuthenticated = stdout.indexOf('*') > -1;
+ if (!isAuthenticated) {
+ await run('gcloud auth login');
+ }
+ } catch (error) {
+ await run('gcloud auth login');
+ }
+
+ const { stdout: projectId } = await run(
+ 'gcloud config get-value project'
+ );
+ await run('gcloud services enable container.googleapis.com');
+
+ // Check if we have an existing cluster
+ let clusterExists = false;
+ const { stdout: clusterData } = await run(
+ 'gcloud container clusters list'
+ );
+ if (clusterData.indexOf('snow-cluster') > -1) {
+ clusterExists = true;
+ }
+
+ const createClusterCmd = `
+ gcloud beta container \
+ clusters create "snow-cluster" \
+ --zone "us-west1-b" \
+ --no-enable-basic-auth \
+ --cluster-version "1.10.9-gke.5" \
+ --image-type "COS" \
+ --machine-type "f1-micro" \
+ --disk-type "pd-standard" \
+ --disk-size "10" \
+ --default-max-pods-per-node "110" \
+ --num-nodes "3" \
+ --enable-cloud-logging \
+ --enable-cloud-monitoring \
+ --enable-ip-alias \
+ --network "projects/${projectId}/global/networks/default" \
+ --subnetwork "projects/${projectId}/regions/us-west1/subnetworks/default" \
+ --default-max-pods-per-node "110" \
+ --addons HorizontalPodAutoscaling,HttpLoadBalancing \
+ --enable-autoupgrade \
+ --enable-autorepair \
+ --scopes "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/trace.append" \
+ `;
+
+ if (!clusterExists) {
+ await run(createClusterCmd);
+ }
+ break;
+ }
+ case 'digitalocean': {
+ // Authenticate.
+ logInfo('Obtain a Digital Ocean access token here: https://cloud.digitalocean.com/account/api/tokens');
+ const token = await askForInput(
+ 'Enter your digital ocean access token'
+ );
+ await run(`doctl auth init -t ${token}`);
+
+ // Check if we have an existing cluster.
+ let clusterExists = true;
+ try {
+ await run('doctl k8s cluster get snow-cluster', {silent: true});
+ } catch (e) {
+ clusterExists = false;
+ }
+
+ const region = 'nyc1';
+ const name = 'snow-cluster';
+
+ if (!clusterExists) {
+ // Create the cluster.
+ logInfo('Creating cluster. This will take a few minutes...');
+ await run(`
+ doctl k8s clusters create ${name} \
+ --region ${region} \
+ --version 1.13.1-do.2 \
+ --node-pool "name=node-pool-0;size=s-1vcpu-2gb;count=2" \
+ --wait
+ `);
+ } else {
+ logInfo('Cluster exists.');
+ }
+
+ await run(`kubectl config use-context do-${region}-${name}`);
+
+ break;
+ }
+ case 'azure': {
+ const resourceGroup = 'snow';
+ const clusterName = 'snow-cluster';
+ // Create the resource group.
+ await run(`
+ az group create
+ -n ${resourceGroup} \
+ -l westus \
+ `);
+
+ // Create the cluster.
+ await run(`
+ az aks create \
+ -n ${clusterName} \
+ -g ${resourceGroup} \
+ --no-ssh-key \
+ -k 1.14.5 \
+ --node-count 2
+ `);
+
+ // Get credentials.
+ await run(`
+ az aks get-credentials \
+ -g ${resourceGroup} \
+ -n ${clusterName}
+ `);
+ break;
+ }
+ default: {
+ logError('No valid cloud provider selected.');
+ }
+ }
+ }
+
+ /*
+ * Authentication complete
+ * Cluster exists
+ * kubeconfig has properly configured auth credentials
+ * Now the fun part: time to set up Kubernetes!
+ */
+
+ /*
+ * Install helm with TLS.
+ * https://medium.com/google-cloud/install-secure-helm-in-gke-254d520061f7
+ */
+ await run('helm init --client-only');
+
+ const BYTES = 2048;
+
+ // Create Certificate Authority
+ await run(`openssl genrsa -out $(helm home)/ca.key.pem ${BYTES}`);
+ const config = `
+ [req]\\n
+ req_extensions=v3_ca\\n
+ distinguished_name=req_distinguished_name\\n
+ [req_distinguished_name]\\n
+ [ v3_ca ]\\n
+ basicConstraints=critical,CA:TRUE\\n
+ subjectKeyIdentifier=hash\\n
+ authorityKeyIdentifier=keyid:always,issuer:always`;
+ await run(
+ `
+ openssl req \\
+ -config <(printf '${config}') \\
+ -key $(helm home)/ca.key.pem \\
+ -new \\
+ -x509 \\
+ -days 7300 \\
+ -sha256 \\
+ -out $(helm home)/ca.cert.pem \\
+ -extensions v3_ca \\
+ -subj "/C=US"`,
+ { shell: '/bin/bash' }
+ );
+
+ // Create credentials for tiller (server)
+ await run(`openssl genrsa -out $(helm home)/tiller.key.pem ${BYTES}`);
+ await run(
+ `
+ openssl req \
+ -new \
+ -sha256 \
+ -key $(helm home)/tiller.key.pem \
+ -out $(helm home)/tiller.csr.pem \
+ -subj "/C=US/O=Snow/CN=tiller-server"`,
+ { shell: '/bin/bash' }
+ );
+ const tillerConfig = `
+ [SAN]\\n
+ subjectAltName=IP:127.0.0.1`;
+ await run(
+ `
+ openssl x509 -req -days 365 \
+ -CA $(helm home)/ca.cert.pem \
+ -CAkey $(helm home)/ca.key.pem \
+ -CAcreateserial \
+ -in $(helm home)/tiller.csr.pem \
+ -out $(helm home)/tiller.cert.pem \
+ -extfile <(printf '${tillerConfig}') \
+ -extensions SAN`,
+ { shell: '/bin/bash' }
+ );
+
+ // Create credentials for helm (client)
+ await run(`openssl genrsa -out $(helm home)/helm.key.pem ${BYTES}`);
+ await run(`
+ openssl req -new -sha256 \
+ -key $(helm home)/helm.key.pem \
+ -out $(helm home)/helm.csr.pem \
+ -subj "/C=US"
+ `);
+ await run(`
+ openssl x509 -req -days 365 \
+ -CA $(helm home)/ca.cert.pem \
+ -CAkey $(helm home)/ca.key.pem \
+ -CAcreateserial \
+ -in $(helm home)/helm.csr.pem \
+ -out $(helm home)/helm.cert.pem
+ `);
+
+ // Create service account and clusterrolebinding
+ await run(`kubectl create serviceaccount \
+ --namespace kube-system tiller
+ `);
+ await run(`kubectl create clusterrolebinding \
+ tiller-cluster-rule \
+ --clusterrole=cluster-admin \
+ --serviceaccount=kube-system:tiller
+ `);
+
+ // Install Helm w/ 'tiller' service account
+ await run(`
+ helm init \
+ --debug \
+ --tiller-tls \
+ --tiller-tls-cert $(helm home)/tiller.cert.pem \
+ --tiller-tls-key $(helm home)/tiller.key.pem \
+ --tiller-tls-verify \
+ --tls-ca-cert $(helm home)/ca.cert.pem \
+ --service-account tiller \
+ --wait
+ `);
+
+ // Verify helm installation
+ await run(`
+ helm ls --tls \
+ --tls-ca-cert $(helm home)/ca.cert.pem \
+ --tls-cert $(helm home)/helm.cert.pem \
+ --tls-key $(helm home)/helm.key.pem
+ `);
+
+ /**
+ *
+ * To remove helm:
+ *
+ * helm reset
+ * --tls
+ * --tls-ca-cert $(helm home)/ca.cert.pem
+ * --tls-cert $(helm home)/helm.cert.pem
+ * --tls-key $(helm home)/helm.key.pem
+ */
+
+ // Install nginx ingress
+ await run(`
+ helm install stable/nginx-ingress \
+ --tls \
+ --tls-ca-cert $(helm home)/ca.cert.pem \
+ --tls-cert $(helm home)/helm.cert.pem \
+ --tls-key $(helm home)/helm.key.pem \
+ --namespace kube-system \
+ --name nginx-ingress \
+ --version 0.31.0 \
+ --set controller.ingressClass=nginx \
+ --set rbac.create=true
+ `);
+
+ /**
+ * Cert-manager. We need to use an alpha release since it supports
+ * certificate generation with SAN's with IP addresses.
+ */
+
+ // Install the CustomResourceDefinition resources
+ await run('kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7/deploy/manifests/00-crds.yaml');
+
+ // Add new helm repo for 0.7.0 alpha release of cert-manager
+ await run('helm repo add jetstack https://charts.jetstack.io');
+
+ // Create a new namespace for cert-manager
+ await run('kubectl create namespace cert-manager');
+
+ // Label the cert-manager namespace to disable resource validation
+ await run('kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true');
+
+ // Install cert-manager
+ await run(`
+ helm install jetstack/cert-manager \
+ --tls \
+ --tls-ca-cert $(helm home)/ca.cert.pem \
+ --tls-cert $(helm home)/helm.cert.pem \
+ --tls-key $(helm home)/helm.key.pem \
+ --version v0.7.0-alpha.1 \
+ --namespace cert-manager \
+ --name cert-manager \
+ --set ingressShim.defaultIssuerName=letsencrypt-prod \
+ --set ingressShim.defaultIssuerKind=ClusterIssuer \
+ --set webhook.enabled=false
+ `);
+
+ let email = await askForInput(
+ 'Provide an email address for Let\'s Encrypt'
+ );
+ while (!(await confirm(`Confirm email: "${email}"`))) {
+ email = await askForInput('Provide an email address for Let\'s Encrypt');
+ }
+
+ // Create cluster issuer
+ await run(`
+ cat < {
+ if (!name) {
+ const msg = 'Specify a "name" (string) property in now.json';
+ logError(msg);
+ return Promise.reject();
+ }
+
+ if (!alias) {
+ const msg = 'Specify an "alias" (string[]) property in now.json';
+ logError(msg);
+ return Promise.reject();
+ }
+
+ return Promise.resolve();
+}
+
+function getDockerfilePort(commands: parser.CommandEntry[]): Promise {
+ let dockerFilePort: string | undefined;
+ commands.forEach(({args, name}) => {
+ if (name !== 'EXPOSE') {
+ return;
+ }
+ console.log('args is', args);
+ if (typeof args === 'string') {
+ const port = args.match(/\d/g);
+ if (port) {
+ dockerFilePort = port.join('');
+ }
+ } else if (Array.isArray(args)) {
+ const argsAsString = args.join(' ');
+ const port = argsAsString.match(/\d/g);
+ if (port) {
+ dockerFilePort = port.join('');
+ }
+ }
+ });
+
+ if (!dockerFilePort) {
+ return Promise.reject();
+ }
+
+ return Promise.resolve(dockerFilePort);
+}
+
+export default async () => {
+ // Verify we have a Dockerfile
+ let dockerFile: parser.CommandEntry[];
+ const dockerFilePath = path.resolve(process.cwd(), 'Dockerfile');
+ try {
+ const content = await readFile(dockerFilePath);
+ dockerFile = parser.parse(content.toString());
+ } catch (error) {
+ const msg = `Error finding Dockerfile.\n${error.message}`;
+ logError(msg);
+ return;
+ }
+
+ // Verify we have a well-formatted now.json file
+ const nowFilePath = path.resolve(process.cwd(), 'now.json');
+ let nowConfig;
+ try {
+ const nowFile = await readFile(nowFilePath);
+ nowConfig = JSON.parse(nowFile.toString());
+ } catch (error) {
+ const msg = `Error reading now.json.\n${error.message}`;
+ logError(msg);
+ return;
+ }
+ verifyNowJSON(nowConfig);
+ const {name, files} = nowConfig;
+
+ // Get revision number.
+ let deployment: IDeployment | undefined;
+ let revision: number;
+ try {
+ // See if we have a deployment.
+ const {stdout: deploymentStr} = await run(`kubectl get deployments/${name} -o json`);
+ // Deployment exists. Update it.
+ deployment = JSON.parse(deploymentStr);
+ const {metadata: {generation}} : IDeployment = JSON.parse(deploymentStr);
+ revision = generation + 1;
+ } catch (e) {
+ // No deployment exists.
+ revision = 1;
+ }
+
+ // Create a volume for persisting the build context
+ const pvcName = `${name}-buildctx`;
+ await run(`
+ cat < {
+ return new Promise(async (resolve, reject) => {
+ let dnsIPs;
+ let {stdout: clusterIP} = await exec(cmd);
+ clusterIP = clusterIP.trim();
+ try {
+ dnsIPs = await dnsResolve(domainName, 'A');
+ } catch (e) {
+ if (e.code === 'ENOTFOUND') {
+ logError(`⚠️ Domain name '${domainName}' has no DNS records.`);
+ logError(`⚠️ Create an A record for '${domainName}' and set its value to ${clusterIP}.`);
+ } else {
+ logError('⚠️ Unknown error occurred while verifying DNS records.');
+ }
+ return;
+ }
+
+ dnsIPs.forEach(dnsIP => {
+ if (clusterIP.trim() === dnsIP) {
+ return;
+ }
+
+ logError(`⚠️ Domain name '${domainName}' has an A record for IP address ${dnsIP}, but should be set to ${clusterIP}.`);
+ logError(`⚠️ Update DNS records for '${domainName}'.`);
+ return reject();
+ });
+
+ resolve();
+ });
+};
+
+interface IngressSpec {
+ spec: {
+ rules: [{
+ host: string;
+ }];
+ tls: [{
+ hosts: string[];
+ secretName: string;
+ }];
+ };
+}
+
+function addDomainToIngress(domainName: string, ingressConfig: string): IngressSpec {
+ const ingress = JSON.parse(ingressConfig);
+ const {spec: {rules = [], tls = []}} = ingress;
+ rules.push({host: domainName});
+ tls.push({
+ hosts: [domainName],
+ secretName: domainName
+ });
+ return {spec: {rules, tls}};
+}
+
+export default async (subcommand?: string, domainName?: string) => {
+ switch (subcommand) {
+ case 'add': {
+ if (!domainName) {
+ logError('Usage: snow domains add [name]');
+ return;
+ }
+
+ // Check that domain has proper DNS records.
+ // Bail early if they're bad.
+ try {
+ await domainHasCorrectDNSRecords(domainName);
+ } catch (error) {
+ return;
+ }
+
+ // Check if domain already is already associated with an ingress.
+ // Bail early if it is.
+ const {stdout} = await exec('kubectl get ingress/snow-ingress -o jsonpath="{range .spec.rules[*]}{@.host}{\'\\n\'}{end}"');
+ const domains = stdout.split('\n');
+ if (domains.indexOf(domainName) !== -1) {
+ logError(`Domain '${domainName}' already configured.`);
+ return;
+ }
+
+ /* Domain not in ingress. Add it to 'rules' and 'tls'. By default, traffic
+ * will go to ingress default backend ("default backend - 404")
+ */
+ const {stdout: addIngressConfig} = await exec('kubectl get ingress/snow-ingress -o json');
+ const {spec: addSpec} = addDomainToIngress(domainName, addIngressConfig);
+ await exec(`kubectl patch ingress snow-ingress --patch '${JSON.stringify({spec: addSpec})}'`);
+
+ let hasSSLCert = true;
+ try {
+ await exec(`kubectl get secrets/${domainName}`);
+ } catch (e) {
+ hasSSLCert = false;
+ }
+
+ logInfo(`✅ Domain '${domainName}' added.`);
+
+ if (!hasSSLCert) {
+ logInfo('ℹ️ Wait a minute for SSL Certificate creation.');
+ }
+
+ break;
+ }
+ case undefined:
+ case 'ls': {
+ const path =
+ '{"Domains"}{"\\n\\n"}{range .items[*].spec.rules[*]}{.host}{"\\n"}{end}{"\\n"}';
+ await run(`kubectl get ingress -o jsonpath='${path}'`);
+ break;
+ }
+ case 'remove':
+ case 'rm':
+ if (!domainName) {
+ logError('⚠️ No domain name specified.');
+ return;
+ }
+
+ const {stdout: rmIngressConfig} = await exec('kubectl get ingress/snow-ingress -o json');
+ const {spec: {rules = [], tls = []}} = JSON.parse(rmIngressConfig);
+ const patch = [
+ rules.length && {
+ op: 'replace',
+ path: '/spec/rules',
+ value: rules.filter(({host}: { host: string; }) => (host !== domainName))
+ },
+ tls.length && {
+ op: 'replace',
+ path: '/spec/tls',
+ value: tls.filter(({secretName}: { secretName: string; }) => (secretName !== domainName))
+ }
+ ].filter(Boolean);
+
+ // Check if domain already is already associated with an ingress.
+ // Bail early if it is.
+ const {stdout: domainList} = await exec('kubectl get ingress/snow-ingress -o jsonpath="{range .spec.rules[*]}{@.host}{\'\\n\'}{end}"');
+ const currDomains = domainList.split('\n');
+ if (currDomains.indexOf(domainName) === -1) {
+ logError(`⚠️ Domain '${domainName}' has no existing configuration.`);
+ return;
+ }
+
+ await exec(`kubectl patch ingress snow-ingress --type='json' --patch '${JSON.stringify(patch)}'`);
+ logInfo(`✅ Domain '${domainName}' removed.`);
+
+ break;
+ default: {
+ console.log('usage error');
+ }
+ }
+};
diff --git a/src/index.ts b/src/index.ts
new file mode 100755
index 0000000..0b71e4e
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,89 @@
+#!/usr/bin/env node
+
+import * as sourcemapsupport from 'source-map-support';
+sourcemapsupport.install();
+
+import * as mri from 'mri';
+import alias from './alias';
+import certs from './certs';
+import create from './create';
+import deploy from './deploy';
+import domains from './domains';
+import install from './install';
+import ip from './ip';
+import login from './login';
+import logout from './logout';
+import ls from './ls';
+import remove from './remove';
+import scale from './scale';
+import secrets from './secrets';
+import { logError } from './utils';
+
+async function main() {
+ const { _, ...opts } = mri(process.argv.slice(2));
+ const [command, ...rest] = _;
+
+ switch (command) {
+ case 'alias': {
+ await alias(rest[0], rest[1], rest[2]);
+ break;
+ }
+ case 'certs': {
+ await certs(...rest);
+ break;
+ }
+ case 'create': {
+ await create(opts);
+ break;
+ }
+ case undefined:
+ case 'deploy': {
+ await deploy();
+ break;
+ }
+ case 'domains': {
+ await domains(...rest);
+ break;
+ }
+ case 'install': {
+ await install();
+ break;
+ }
+ case 'ip': {
+ await ip();
+ break;
+ }
+ case 'login':
+ await login();
+ break;
+ case 'logout':
+ await logout();
+ break;
+ case 'ls': {
+ await ls();
+ break;
+ }
+ case 'remove':
+ case 'rm': {
+ await remove(rest[0]);
+ break;
+ }
+ case 'scale': {
+ await scale(rest[0], rest[1], rest[2]);
+ break;
+ }
+ case 'secrets': {
+ await secrets(...rest);
+ break;
+ }
+ default: {
+ const errMsg = `Error: Invalid command: snow ${command} ${rest.join(
+ ' '
+ )}`;
+ logError(errMsg);
+ }
+ }
+}
+
+// tslint:disable-next-line:no-floating-promises
+main();
diff --git a/src/install.ts b/src/install.ts
new file mode 100644
index 0000000..d38d4c1
--- /dev/null
+++ b/src/install.ts
@@ -0,0 +1,73 @@
+import {confirm, run} from './utils';
+
+export default async () => {
+ interface IDependency {
+ label: string;
+ detectCmd: string;
+ installCmd: string;
+ detectAdvanced?(output: string): boolean;
+ }
+
+ const dependencies: IDependency[] = [
+ {
+ label: 'Virtualbox',
+ detectCmd: 'which virtualbox',
+ installCmd: 'brew cask install virtualbox'
+ },
+ {
+ label: 'Docker',
+ detectCmd: 'which docker',
+ installCmd: 'brew cask install docker'
+ },
+ {
+ label: 'Kubernetes (kubectl)',
+ detectCmd: 'which kubectl',
+ installCmd: 'brew install kubernetes-cli'
+ },
+ {
+ label: 'Helm',
+ detectCmd: 'which helm',
+ installCmd: 'brew install kubernetes-helm'
+ },
+ {
+ label: 'Minikube',
+ detectCmd: 'which minikube',
+ installCmd: 'brew cask install minikube'
+ },
+ {
+ label: 'Google Cloud SDK (gcloud)',
+ detectCmd: 'which gcloud',
+ installCmd: 'brew cask install google-cloud-sdk'
+ },
+ {
+ label: 'Google Cloud SDK (gcloud) Beta Commands',
+ detectCmd: 'gcloud components list --filter="gcloud Beta Commands"',
+ installCmd: 'gcloud components install beta --quiet',
+ detectAdvanced: output => {
+ return output.indexOf('Not Installed | gcloud Beta Commands') > -1;
+ }
+ },
+ {
+ label: 'Digital Ocean CLI (doctl)',
+ detectCmd: 'which doctl',
+ installCmd: 'brew install doctl'
+ },
+ ];
+
+ async function tryInstall(label: string, installCmd: string) {
+ if (await confirm(`Install ${label}`)) {
+ await run(installCmd);
+ }
+ }
+
+ for (const { label, detectCmd, detectAdvanced, installCmd } of dependencies) {
+ try {
+ const { stdout } = await run(detectCmd, {silent: true});
+ if (detectAdvanced && detectAdvanced(stdout)) {
+ await tryInstall(label, installCmd);
+ }
+ } catch (error) {
+ await tryInstall(label, installCmd);
+ }
+ }
+};
diff --git a/src/ip.ts b/src/ip.ts
new file mode 100644
index 0000000..8c2008c
--- /dev/null
+++ b/src/ip.ts
@@ -0,0 +1,11 @@
+import { run } from './utils';
+
+const path = '{.status.loadBalancer.ingress[0].ip}{"\\n"}';
+export const cmd = `
+ kubectl get services/nginx-ingress-controller \
+ --namespace=kube-system \
+ -o jsonpath='${path}'`;
+
+export default async () => {
+ await run(cmd);
+};
diff --git a/src/login.ts b/src/login.ts
new file mode 100644
index 0000000..772b2bb
--- /dev/null
+++ b/src/login.ts
@@ -0,0 +1,51 @@
+import cloudProviders from './providers';
+import { askForInput, logError, logInfo, pickOne, run } from './utils';
+
+export default async () => {
+ const question = 'Which cloud provider do you want to login to';
+ const provider = await pickOne(question, cloudProviders);
+
+ switch (provider) {
+ case 'minikube': {
+ await run('kubectl config use-context minikube');
+ break;
+ }
+ case 'gcp': {
+ await run('gcloud auth login');
+
+ // Setup default project
+ const {stdout: projectsStr} = await run('gcloud projects list --format json');
+ const projects = JSON.parse(projectsStr);
+
+ if (projects.length === 0) {
+ logError('No projects exist. Create a project.');
+ return;
+ }
+
+ const {projectId} = projects[0];
+ await run(`gcloud config set project ${projectId}`);
+
+ // Get cluster credentials
+ const { stdout: clusterData } = await run(
+ 'gcloud container clusters list --format="json"'
+ );
+ const { location, name } = JSON.parse(clusterData)[0];
+ await run(
+ `gcloud container clusters get-credentials ${name} --zone "${location}"`
+ );
+ break;
+ }
+ case 'digitalocean': {
+ logInfo('Obtain a Digital Ocean access token here: https://cloud.digitalocean.com/account/api/tokens');
+ const token = await askForInput(
+ 'Enter your digital ocean access token'
+ );
+ await run(`doctl auth init -t ${token}`);
+ await run('doctl k8s cluster kubeconfig save snow-cluster');
+ break;
+ }
+ default: {
+ logError('No valid cloud provider selected.');
+ }
+ }
+};
diff --git a/src/logout.ts b/src/logout.ts
new file mode 100644
index 0000000..2157718
--- /dev/null
+++ b/src/logout.ts
@@ -0,0 +1,31 @@
+import { logError, pickOne, run } from './utils';
+import cloudProviders from './providers';
+
+export default async () => {
+ const question = 'Which cloud provider do you want to logout of';
+ const provider = await pickOne(question, cloudProviders);
+
+ switch (provider) {
+ case 'minikube': {
+ // In the case of minikube, this is a no-op.
+ break;
+ }
+ case 'gcp': {
+ const { stdout: projectId } = await run(
+ 'gcloud config get-value core/project'
+ );
+ const { stdout: clusterData } = await run(
+ 'gcloud container clusters list --format="json"'
+ );
+ const { location, name } = JSON.parse(clusterData)[0];
+ const key = `gke_${projectId}_${location}_${name}`;
+ await run(`kubectl config unset clusters.${key}`);
+ await run(`kubectl config unset contexts.${key}`);
+ await run(`kubectl config unset users.${key}`);
+ break;
+ }
+ default: {
+ logError('No valid cloud provider selected.');
+ }
+ }
+};
diff --git a/src/ls.ts b/src/ls.ts
new file mode 100644
index 0000000..f5dd61c
--- /dev/null
+++ b/src/ls.ts
@@ -0,0 +1,8 @@
+import { run } from './utils';
+
+export default async (deployment?: string) => {
+ if (!deployment) {
+ return run('kubectl get deployments');
+ }
+ await run(`kubectl get deployments ${deployment}`);
+};
diff --git a/src/providers.ts b/src/providers.ts
new file mode 100644
index 0000000..e8772d6
--- /dev/null
+++ b/src/providers.ts
@@ -0,0 +1,4 @@
+type Provider = 'minikube' | 'gcp' | 'digitalocean' | 'azure';
+const cloudProviders: Provider[] = ['minikube', 'gcp', 'digitalocean', 'azure'];
+
+export default cloudProviders;
diff --git a/src/remove.ts b/src/remove.ts
new file mode 100644
index 0000000..d8f1bd1
--- /dev/null
+++ b/src/remove.ts
@@ -0,0 +1,8 @@
+import { logError, run } from './utils';
+
+export default async (deployment?: string) => {
+ if (!deployment) {
+ return logError('No deployment specified.');
+ }
+ await run(`kubectl delete deploy ${deployment}`);
+};
diff --git a/src/scale.ts b/src/scale.ts
new file mode 100644
index 0000000..4500bdd
--- /dev/null
+++ b/src/scale.ts
@@ -0,0 +1,38 @@
+import {logError, run} from './utils';
+
+export default async (name?: string, min?: string, max?: string) => {
+ if (!name) {
+ return logError('name (required): snow scale ');
+ }
+ if (!min) {
+ return logError('min (required): snow scale ');
+ }
+ if (isNaN(Number(min))) {
+ return logError(
+ 'min (typecheck: must be numberic): snow scale '
+ );
+ }
+
+ if (max && isNaN(Number(max))) {
+ return logError(
+ 'max (typecheck: must be numberic): snow scale '
+ );
+ }
+
+ // If CLI called without 'max' supplied, default to 'min'.
+ const actualMax = max ? max : min;
+
+ // Check for existing Horizontal Pod Autoscaler (HPA)
+ try {
+ await run(`kubectl get hpa ${name}`);
+ // HPA exists. Patch it.
+ await run(
+ `kubectl patch hpa ${name} --patch '{"spec":{"minReplicas":${min},"maxReplicas":${actualMax}}}'`
+ );
+ } catch (error) {
+ // No HPA. Create one.
+ await run(
+ `kubectl autoscale deployment/${name} --min=${min} --max=${actualMax}`
+ );
+ }
+};
diff --git a/src/secrets.ts b/src/secrets.ts
new file mode 100644
index 0000000..c4e8726
--- /dev/null
+++ b/src/secrets.ts
@@ -0,0 +1,62 @@
+import { exec, isRemove, logError, run } from './utils';
+
+/*
+ * All secrets are stored as individual objects with a single key-value pair (key is string "key").
+ * All secrets created via the snow API are labelled with {"snowsecret":"true"}.
+ */
+export default async function secrets(subcommand?: string, key?: string, valueOrNewKey?: string) {
+ if (!subcommand || subcommand === 'ls') {
+ await run('kubectl get secrets -l snowsecret=true -o=custom-columns=Name:.metadata.name,Age:.metadata.creationTimestamp');
+ return;
+ }
+
+ if (subcommand === 'add') {
+ if (!key || !valueOrNewKey) {
+ return logError('Key or value missing. Usage: now secrets add ');
+ }
+ const value = valueOrNewKey;
+ const valueBase64 = Buffer.from(value).toString('base64');
+ await run(`
+ cat < ');
+ }
+
+ const {stdout: valueBase64} = await exec(`kubectl get secret/${oldKey} --output=jsonpath=\'{.data.key}\'`);
+ const newValue = Buffer.from(valueBase64, 'base64').toString();
+
+ // Create secret with name 'newKey'
+ await exec(`kubectl create secret generic ${newKey} --from-literal=key=${newValue}`);
+
+ // Delete secret with name 'oldKey'
+ await exec(`kubectl delete secret/${oldKey}`);
+ return;
+ }
+
+ if (isRemove(subcommand)) {
+ await run(`kubectl delete secret ${key}`);
+ }
+}
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..35deb9f
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,149 @@
+import chalk from 'chalk';
+import * as childProcess from 'child_process';
+import * as fs from 'fs';
+import {promisify} from 'util';
+
+export const exec = promisify(childProcess.exec);
+export const stat = promisify(fs.stat);
+export const readFile = promisify(fs.readFile);
+
+function normalizeString(str: string) {
+ return str
+ .toString()
+ .trim()
+ .toLowerCase();
+}
+
+export async function askForInput(msg: string) {
+ const question = chalk.bold.red(`> ${msg}: `);
+ process.stdout.write(question);
+
+ return new Promise(resolve => {
+ function data(d: any) {
+ const input = d.toString().trim();
+ process.stdin.removeListener('data', data).pause();
+ resolve(input);
+ }
+
+ process.stdin.on('data', data).resume();
+ });
+}
+
+export async function confirm(msg: string) {
+ const question = chalk.bold.red(`> ${msg}?`);
+ const options = chalk.gray('[y/N] ');
+
+ process.stdout.write(`${question} ${options} `);
+
+ return new Promise(resolve => {
+ function data(d: any) {
+ process.stdin.pause();
+ const isYes = normalizeString(d) === 'y';
+
+ if (!isYes) {
+ console.log(`${chalk.grey('> ')} Aborted`);
+ }
+
+ process.stdin.removeListener('data', data).pause();
+ resolve(isYes);
+ }
+
+ process.stdin.on('data', data).resume();
+ });
+}
+
+export function isRemove(str: string | undefined) {
+ return str === 'rm' || str === 'remove';
+}
+
+export function logDebug(msg: string) {
+ console.log(chalk.gray(msg));
+}
+
+export function logError(msg: string) {
+ console.log(chalk.bold.red(msg));
+}
+
+export function logInfo(msg: string) {
+ console.log(chalk.bold.white(msg));
+}
+
+export async function pickOne(msg: string, options: string[]) : Promise {
+ const question = chalk.bold.red(`> ${msg}?`);
+ const prefixes = options.map(option => option.charAt(0));
+ const prefixesStr = chalk.gray(`[${prefixes.join(',')}]`);
+
+ process.stdout.write(`${question} (${options.join(',')}) ${prefixesStr} `);
+
+ return new Promise(resolve => {
+ function data(d: any) {
+ const input = normalizeString(d);
+ const index = prefixes.indexOf(input);
+ const option = options[index];
+
+ process.stdin.removeListener('data', data).pause();
+
+ resolve(option);
+ }
+
+ process.stdin.on('data', data).resume();
+ });
+}
+
+interface IRunResolve {
+ stdout: string;
+ stderr: string;
+}
+
+export async function run(
+ str: string,
+ opts?: childProcess.ExecOptions & {
+ silent?: boolean;
+ }
+) : Promise {
+ const runString = str.replace(/(\s+)/gm, ' ').trim();
+ const silent = opts ? opts.silent : false;
+
+ if (!silent) {
+ logInfo(`> ${runString}`);
+ }
+
+ return new Promise((resolve, reject) => {
+ function callback(
+ error: childProcess.ExecException | null,
+ stdout: string | Buffer,
+ stderr: string | Buffer
+ ) {
+ if (error) {
+ if (!silent) {
+ process.stderr.write(chalk.red(error.message));
+
+ if (error.stack) {
+ process.stderr.write(chalk.white(error.stack));
+ }
+ }
+
+ return reject(error);
+ }
+
+ return resolve({
+ stdout: stdout.toString().trim(),
+ stderr: stderr.toString().trim()
+ });
+ }
+
+ const cmd = childProcess.exec(str, opts, callback);
+
+ if (silent) {
+ return;
+ }
+
+ cmd.stderr.on('data', (data: string) => {
+ process.stdout.write(chalk.gray(data));
+ });
+
+ cmd.stdout.on('data', (data: string) => {
+ process.stdout.write(chalk.gray(data));
+ });
+ });
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..f253002
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "lib": ["es2017"],
+ "moduleResolution": "node",
+ "downlevelIteration": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "outDir": "dist",
+ "preserveConstEnums": true,
+ "removeComments": false,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "typeRoots": ["packages/types/", "node_modules/@types/"]
+ },
+ "include": ["src/*"]
+}
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000..0977773
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,21 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended",
+ "tslint-config-prettier",
+ "tslint-xo/space"
+ ],
+ "jsRules": {
+ "no-console": false,
+ "no-unused": false,
+ "object-literal-sort-keys": false,
+ "object-curly-spacing": false
+ },
+ "rules": {
+ "no-console": false,
+ "no-unused": false,
+ "object-literal-sort-keys": false,
+ "object-curly-spacing": false
+ },
+ "rulesDirectory": []
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..b991dc8
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,1962 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@fimbul/bifrost@^0.17.0":
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.17.0.tgz#f0383ba7e40992e3193dc87e2ddfde2ad62a9cf4"
+ integrity sha512-gVTkJAOef5HtN6LPmrtt5fAUmBywwlgmObsU3FBhPoNeXPLaIl2zywXkJEtvvVLQnaFmtff3x+wIj5lHRCDE3Q==
+ dependencies:
+ "@fimbul/ymir" "^0.17.0"
+ get-caller-file "^2.0.0"
+ tslib "^1.8.1"
+ tsutils "^3.5.0"
+
+"@fimbul/ymir@^0.17.0":
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/@fimbul/ymir/-/ymir-0.17.0.tgz#4f28389b9f804d1cd202e11983af1743488b7815"
+ integrity sha512-xMXM9KTXRLHLVS6dnX1JhHNEkmWHcAVCQ/4+DA1KKwC/AFnGHzu/7QfQttEPgw3xplT+ILf9e3i64jrFwB3JtA==
+ dependencies:
+ inversify "^5.0.0"
+ reflect-metadata "^0.1.12"
+ tslib "^1.8.1"
+
+"@iamstarkov/listr-update-renderer@0.4.1":
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz#d7c48092a2dcf90fd672b6c8b458649cb350c77e"
+ integrity sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA==
+ dependencies:
+ chalk "^1.1.3"
+ cli-truncate "^0.2.1"
+ elegant-spinner "^1.0.1"
+ figures "^1.7.0"
+ indent-string "^3.0.0"
+ log-symbols "^1.0.2"
+ log-update "^2.3.0"
+ strip-ansi "^3.0.1"
+
+"@samverschueren/stream-to-observable@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
+ integrity sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==
+ dependencies:
+ any-observable "^0.3.0"
+
+"@types/mri@1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@types/mri/-/mri-1.1.0.tgz#66555e4d797713789ea0fefdae0898d8170bf5af"
+ integrity sha512-fMl88ZoZXOB7VKazJ6wUMpZc9QIn+jcigSFRf2K/rrw4DcXn+/uGxlWX8DDlcE7JkwgIZ7BDH+JgxZPlc/Ap3g==
+
+"@types/node@*":
+ version "11.9.5"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.5.tgz#011eece9d3f839a806b63973e228f85967b79ed3"
+ integrity sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==
+
+"@types/node@10.12.18":
+ version "10.12.18"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
+ integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
+
+"@types/source-map-support@0.4.2":
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.4.2.tgz#5b9f4502183430511bb90e84a6e4bf4214e55a67"
+ integrity sha512-GbGWx39O8NdUHSChdrU0XeigBAgu1Teg3llwE0slSVcH2qISaQT70ftAiH+h4HIt3VIObFU34PSpXIKJuXCybQ==
+ dependencies:
+ "@types/node" "*"
+
+ansi-escapes@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
+ integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+ integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+ integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+any-observable@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
+ integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+arr-diff@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+ integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+ integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+ integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+ integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
+
+array-unique@^0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+ integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+arrify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+ integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
+
+assign-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+ integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+atob@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+ integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+babel-code-frame@^6.22.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+ integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
+ dependencies:
+ chalk "^1.1.3"
+ esutils "^2.0.2"
+ js-tokens "^3.0.2"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base@^0.11.1:
+ version "0.11.2"
+ resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+ integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+ dependencies:
+ 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"
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+ integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+ dependencies:
+ 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"
+
+buffer-from@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+builtin-modules@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+ integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
+
+cache-base@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+ integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+ dependencies:
+ 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"
+
+caller-callsite@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+ integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+ dependencies:
+ callsites "^2.0.0"
+
+caller-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+ integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+ dependencies:
+ caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+ integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+chalk@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
+ integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+chalk@^1.0.0, chalk@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+ dependencies:
+ 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"
+
+chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+ci-info@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+ integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
+class-utils@^0.3.5:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+ integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+ dependencies:
+ arr-union "^3.1.0"
+ define-property "^0.2.5"
+ isobject "^3.0.0"
+ static-extend "^0.1.1"
+
+cli-cursor@^2.0.0, cli-cursor@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+ dependencies:
+ restore-cursor "^2.0.0"
+
+cli-truncate@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
+ integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=
+ dependencies:
+ slice-ansi "0.0.4"
+ string-width "^1.0.1"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+collection-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+ integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+ dependencies:
+ map-visit "^1.0.0"
+ object-visit "^1.0.0"
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+commander@^2.12.1, commander@^2.14.1, commander@^2.9.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+ integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+
+component-emitter@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
+ integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+copy-descriptor@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+ integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+cosmiconfig@5.0.6:
+ version "5.0.6"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39"
+ integrity sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==
+ dependencies:
+ is-directory "^0.3.1"
+ js-yaml "^3.9.0"
+ parse-json "^4.0.0"
+
+cosmiconfig@^5.0.7:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.1.0.tgz#6c5c35e97f37f985061cdf653f114784231185cf"
+ integrity sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q==
+ dependencies:
+ import-fresh "^2.0.0"
+ is-directory "^0.3.1"
+ js-yaml "^3.9.0"
+ lodash.get "^4.4.2"
+ parse-json "^4.0.0"
+
+cross-spawn@^6.0.0:
+ version "6.0.5"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+ dependencies:
+ nice-try "^1.0.4"
+ path-key "^2.0.1"
+ semver "^5.5.0"
+ shebang-command "^1.2.0"
+ which "^1.2.9"
+
+date-fns@^1.27.2:
+ version "1.30.1"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
+ integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
+
+debug@^2.2.0, debug@^2.3.3:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^3.1.0:
+ version "3.2.6"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+ integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+ dependencies:
+ ms "^2.1.1"
+
+debug@^4.0.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+ integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+ dependencies:
+ ms "^2.1.1"
+
+decode-uri-component@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+ integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+dedent@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
+ integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
+
+define-property@^0.2.5:
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+ integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+ dependencies:
+ is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+ integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+ dependencies:
+ is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+ integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+ dependencies:
+ is-descriptor "^1.0.2"
+ isobject "^3.0.1"
+
+del@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
+ integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
+ dependencies:
+ 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"
+
+diff@^3.2.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+ integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
+
+docker-file-parser@1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/docker-file-parser/-/docker-file-parser-1.0.4.tgz#723156944d6ee52e4f0e45c1a9eb7b36124d0dd9"
+ integrity sha512-djh3R7KXkEPm80PXK9xbz8bCfEFuU11Tmf5l9IXKdjBPx91/cOqhwOwtOq6s35B8TqrwY6L4xLphmyYmJT0ZXw==
+
+doctrine@0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523"
+ integrity sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=
+ dependencies:
+ esutils "^1.1.6"
+ isarray "0.0.1"
+
+elegant-spinner@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
+ integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
+
+end-of-stream@^1.1.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+ integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
+ dependencies:
+ once "^1.4.0"
+
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esutils@^1.1.6:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.1.6.tgz#c01ccaa9ae4b897c6d0c3e210ae52f3c7a844375"
+ integrity sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=
+
+esutils@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+ integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
+
+execa@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+ integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+ dependencies:
+ 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"
+
+expand-brackets@^2.1.4:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+ integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+ dependencies:
+ 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"
+
+extend-shallow@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+ integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+ dependencies:
+ is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+ integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+ dependencies:
+ assign-symbols "^1.0.0"
+ is-extendable "^1.0.1"
+
+extglob@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+ integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+ dependencies:
+ 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"
+
+figures@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+ integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=
+ dependencies:
+ escape-string-regexp "^1.0.5"
+ object-assign "^4.1.0"
+
+figures@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
+fill-range@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+ integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+ to-regex-range "^2.1.0"
+
+find-parent-dir@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54"
+ integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+for-in@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+ integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+fragment-cache@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+ integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+ dependencies:
+ map-cache "^0.2.2"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+g-status@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97"
+ integrity sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA==
+ dependencies:
+ arrify "^1.0.1"
+ matcher "^1.0.0"
+ simple-git "^1.85.0"
+
+get-caller-file@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.1.tgz#25835260d3a2b9665fcdbb08cb039a7bbf7011c0"
+ integrity sha512-SpOZHfz845AH0wJYVuZk2jWDqFmu7Xubsx+ldIpwzy5pDUpu7OJHK7QYNSA2NPlDSKQwM1GFaAkciOWjjW92Sg==
+
+get-own-enumerable-property-symbols@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203"
+ integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==
+
+get-stdin@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
+ integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
+
+get-stream@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+ integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+ dependencies:
+ pump "^3.0.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+ integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+glob@^7.0.3, glob@^7.1.1, glob@^7.1.3:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+ integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+ dependencies:
+ 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"
+
+globby@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
+ integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=
+ dependencies:
+ array-union "^1.0.1"
+ glob "^7.0.3"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-value@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+ integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+ dependencies:
+ get-value "^2.0.3"
+ has-values "^0.1.4"
+ isobject "^2.0.0"
+
+has-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+ integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+ dependencies:
+ get-value "^2.0.6"
+ has-values "^1.0.0"
+ isobject "^3.0.0"
+
+has-values@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+ integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+ integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+ dependencies:
+ is-number "^3.0.0"
+ kind-of "^4.0.0"
+
+hosted-git-info@^2.1.4:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
+ integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
+
+husky@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/husky/-/husky-1.3.1.tgz#26823e399300388ca2afff11cfa8a86b0033fae0"
+ integrity sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==
+ dependencies:
+ cosmiconfig "^5.0.7"
+ execa "^1.0.0"
+ find-up "^3.0.0"
+ get-stdin "^6.0.0"
+ is-ci "^2.0.0"
+ pkg-dir "^3.0.0"
+ please-upgrade-node "^3.1.1"
+ read-pkg "^4.0.1"
+ run-node "^1.0.0"
+ slash "^2.0.0"
+
+import-fresh@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+ integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+ dependencies:
+ caller-path "^2.0.0"
+ resolve-from "^3.0.0"
+
+indent-string@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
+ integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+ integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+inversify@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.0.1.tgz#500d709b1434896ce5a0d58915c4a4210e34fb6e"
+ integrity sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==
+
+is-accessor-descriptor@^0.1.6:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+ integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+ integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+ dependencies:
+ kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-buffer@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-ci@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+ integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+ dependencies:
+ ci-info "^2.0.0"
+
+is-data-descriptor@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+ integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+ integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+ dependencies:
+ kind-of "^6.0.0"
+
+is-descriptor@^0.1.0:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+ integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+ dependencies:
+ is-accessor-descriptor "^0.1.6"
+ is-data-descriptor "^0.1.4"
+ kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+ integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+ dependencies:
+ is-accessor-descriptor "^1.0.0"
+ is-data-descriptor "^1.0.0"
+ kind-of "^6.0.2"
+
+is-directory@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+ integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+ integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+ integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+ dependencies:
+ is-plain-object "^2.0.4"
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0"
+ integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-obj@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+ integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
+
+is-observable@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e"
+ integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==
+ dependencies:
+ symbol-observable "^1.1.0"
+
+is-path-cwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+ integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
+
+is-path-in-cwd@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
+ integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
+ dependencies:
+ is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
+ integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+ dependencies:
+ path-is-inside "^1.0.1"
+
+is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+ integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
+
+is-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+ integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
+
+is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-windows@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+ integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+ integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
+
+isarray@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+ dependencies:
+ isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+jest-get-type@^22.1.0:
+ version "22.4.3"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4"
+ integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==
+
+jest-validate@^23.5.0:
+ version "23.6.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474"
+ integrity sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==
+ dependencies:
+ chalk "^2.0.1"
+ jest-get-type "^22.1.0"
+ leven "^2.1.0"
+ pretty-format "^23.6.0"
+
+js-tokens@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+ integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
+
+js-yaml@^3.7.0, js-yaml@^3.9.0:
+ version "3.12.2"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc"
+ integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+json-parse-better-errors@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+ integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+ integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+ integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+ integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+leven@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
+ integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
+
+lint-staged@8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-8.1.0.tgz#dbc3ae2565366d8f20efb9f9799d076da64863f2"
+ integrity sha512-yfSkyJy7EuVsaoxtUSEhrD81spdJOe/gMTGea3XaV7HyoRhTb9Gdlp6/JppRZERvKSEYXP9bjcmq6CA5oL2lYQ==
+ dependencies:
+ "@iamstarkov/listr-update-renderer" "0.4.1"
+ chalk "^2.3.1"
+ commander "^2.14.1"
+ cosmiconfig "5.0.6"
+ debug "^3.1.0"
+ dedent "^0.7.0"
+ del "^3.0.0"
+ execa "^1.0.0"
+ find-parent-dir "^0.3.0"
+ g-status "^2.0.2"
+ is-glob "^4.0.0"
+ is-windows "^1.0.2"
+ jest-validate "^23.5.0"
+ listr "^0.14.2"
+ lodash "^4.17.5"
+ log-symbols "^2.2.0"
+ micromatch "^3.1.8"
+ npm-which "^3.0.1"
+ p-map "^1.1.1"
+ path-is-inside "^1.0.2"
+ pify "^3.0.0"
+ please-upgrade-node "^3.0.2"
+ staged-git-files "1.1.2"
+ string-argv "^0.0.2"
+ stringify-object "^3.2.2"
+
+listr-silent-renderer@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
+ integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=
+
+listr-update-renderer@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2"
+ integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==
+ dependencies:
+ chalk "^1.1.3"
+ cli-truncate "^0.2.1"
+ elegant-spinner "^1.0.1"
+ figures "^1.7.0"
+ indent-string "^3.0.0"
+ log-symbols "^1.0.2"
+ log-update "^2.3.0"
+ strip-ansi "^3.0.1"
+
+listr-verbose-renderer@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db"
+ integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==
+ dependencies:
+ chalk "^2.4.1"
+ cli-cursor "^2.1.0"
+ date-fns "^1.27.2"
+ figures "^2.0.0"
+
+listr@^0.14.2:
+ version "0.14.3"
+ resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586"
+ integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==
+ dependencies:
+ "@samverschueren/stream-to-observable" "^0.3.0"
+ is-observable "^1.1.0"
+ is-promise "^2.1.0"
+ is-stream "^1.1.0"
+ listr-silent-renderer "^1.1.1"
+ listr-update-renderer "^0.5.0"
+ listr-verbose-renderer "^0.5.0"
+ p-map "^2.0.0"
+ rxjs "^6.3.3"
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+ integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
+
+lodash@^4.17.5:
+ version "4.17.11"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+ integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
+log-symbols@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
+ integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=
+ dependencies:
+ chalk "^1.0.0"
+
+log-symbols@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
+ integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
+ dependencies:
+ chalk "^2.0.1"
+
+log-update@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708"
+ integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg=
+ dependencies:
+ ansi-escapes "^3.0.0"
+ cli-cursor "^2.0.0"
+ wrap-ansi "^3.0.1"
+
+map-cache@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+ integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+ integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+ dependencies:
+ object-visit "^1.0.0"
+
+matcher@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/matcher/-/matcher-1.1.1.tgz#51d8301e138f840982b338b116bb0c09af62c1c2"
+ integrity sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==
+ dependencies:
+ escape-string-regexp "^1.0.4"
+
+micromatch@^3.1.8:
+ version "3.1.10"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+ integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+ dependencies:
+ 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"
+
+mimic-fn@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+ integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+mixin-deep@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
+ integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
+ dependencies:
+ for-in "^1.0.2"
+ is-extendable "^1.0.1"
+
+mri@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.1.tgz#85aa26d3daeeeedf80dc5984af95cc5ca5cad9f1"
+ integrity sha1-haom09ru7t+A3FmEr5XMXKXK2fE=
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+ integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+nanomatch@^1.2.9:
+ version "1.2.13"
+ resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+ integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+ dependencies:
+ 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-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"
+
+nice-try@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+ integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+normalize-package-data@^2.3.2:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+ integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+ dependencies:
+ hosted-git-info "^2.1.4"
+ resolve "^1.10.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+npm-path@^2.0.2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64"
+ integrity sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==
+ dependencies:
+ which "^1.2.10"
+
+npm-run-path@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+ integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
+ dependencies:
+ path-key "^2.0.0"
+
+npm-which@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
+ integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=
+ dependencies:
+ commander "^2.9.0"
+ npm-path "^2.0.2"
+ which "^1.2.10"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+ integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+object-assign@^4.0.1, object-assign@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+ integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+ dependencies:
+ copy-descriptor "^0.1.0"
+ define-property "^0.2.5"
+ kind-of "^3.0.3"
+
+object-visit@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+ integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+ dependencies:
+ isobject "^3.0.0"
+
+object.pick@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+ integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+ dependencies:
+ isobject "^3.0.1"
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+onetime@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+ dependencies:
+ mimic-fn "^1.0.0"
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
+p-limit@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.1.0.tgz#1d5a0d20fb12707c758a655f6bbc4386b5930d68"
+ integrity sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-map@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
+ integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
+
+p-map@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.0.0.tgz#be18c5a5adeb8e156460651421aceca56c213a50"
+ integrity sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==
+
+p-try@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
+ integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
+
+parse-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+ dependencies:
+ error-ex "^1.3.1"
+ json-parse-better-errors "^1.0.1"
+
+pascalcase@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+ integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-is-inside@^1.0.1, path-is-inside@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+ integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
+
+path-key@^2.0.0, path-key@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+ integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+pify@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
+
+pify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+ integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+ dependencies:
+ find-up "^3.0.0"
+
+please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac"
+ integrity sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==
+ dependencies:
+ semver-compare "^1.0.0"
+
+posix-character-classes@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+ integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+prettier@1.15.3:
+ version "1.15.3"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a"
+ integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg==
+
+pretty-format@^23.6.0:
+ version "23.6.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
+ integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==
+ dependencies:
+ ansi-regex "^3.0.0"
+ ansi-styles "^3.2.0"
+
+pump@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+ integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+read-pkg@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237"
+ integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc=
+ dependencies:
+ normalize-package-data "^2.3.2"
+ parse-json "^4.0.0"
+ pify "^3.0.0"
+
+reflect-metadata@^0.1.12:
+ version "0.1.13"
+ resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
+ integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+ integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+ dependencies:
+ extend-shallow "^3.0.2"
+ safe-regex "^1.1.0"
+
+repeat-element@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+ integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+ integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+resolve-from@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-url@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+ integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@^1.10.0, resolve@^1.3.2:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
+ integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
+ dependencies:
+ path-parse "^1.0.6"
+
+restore-cursor@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+ dependencies:
+ onetime "^2.0.0"
+ signal-exit "^3.0.2"
+
+ret@~0.1.10:
+ version "0.1.15"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+ integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@^2.2.8:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+ dependencies:
+ glob "^7.1.3"
+
+run-node@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e"
+ integrity sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==
+
+rxjs@^6.3.3:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504"
+ integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==
+ dependencies:
+ tslib "^1.9.0"
+
+safe-regex@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+ integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+ dependencies:
+ ret "~0.1.10"
+
+semver-compare@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
+ integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
+ integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
+
+set-value@^0.4.3:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
+ integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE=
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-extendable "^0.1.1"
+ is-plain-object "^2.0.1"
+ to-object-path "^0.3.0"
+
+set-value@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274"
+ integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-extendable "^0.1.1"
+ is-plain-object "^2.0.3"
+ split-string "^3.0.1"
+
+shebang-command@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+ dependencies:
+ shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+ integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+ integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+simple-git@^1.85.0:
+ version "1.107.0"
+ resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.107.0.tgz#12cffaf261c14d6f450f7fdb86c21ccee968b383"
+ integrity sha512-t4OK1JRlp4ayKRfcW6owrWcRVLyHRUlhGd0uN6ZZTqfDq8a5XpcUdOKiGRNobHEuMtNqzp0vcJNvhYWwh5PsQA==
+ dependencies:
+ debug "^4.0.1"
+
+slash@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+ integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
+slice-ansi@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+ integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=
+
+snapdragon-node@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+ integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+ dependencies:
+ define-property "^1.0.0"
+ isobject "^3.0.0"
+ snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+ integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+ dependencies:
+ kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+ integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+ dependencies:
+ 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"
+
+source-map-resolve@^0.5.0:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+ integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+ dependencies:
+ 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@0.5.10:
+ version "0.5.10"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c"
+ integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+ integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.6:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+source-map@^0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+spdx-correct@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+ integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+ dependencies:
+ spdx-expression-parse "^3.0.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+ integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
+
+spdx-expression-parse@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+ integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e"
+ integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==
+
+split-string@^3.0.1, split-string@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+ integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+ dependencies:
+ extend-shallow "^3.0.0"
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+staged-git-files@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.2.tgz#4326d33886dc9ecfa29a6193bf511ba90a46454b"
+ integrity sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==
+
+static-extend@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+ integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+ dependencies:
+ define-property "^0.2.5"
+ object-copy "^0.1.0"
+
+string-argv@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736"
+ integrity sha1-2sMECGkMIfPDYwo/86BYd73L1zY=
+
+string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+string-width@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+ dependencies:
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^4.0.0"
+
+stringify-object@^3.2.2:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629"
+ integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==
+ dependencies:
+ get-own-enumerable-property-symbols "^3.0.0"
+ is-obj "^1.0.1"
+ is-regexp "^1.0.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+ dependencies:
+ ansi-regex "^3.0.0"
+
+strip-eof@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+ integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+ integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+symbol-observable@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+ integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
+to-object-path@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+ integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+ dependencies:
+ kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+ integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+ dependencies:
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+ integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+ dependencies:
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ regex-not "^1.0.2"
+ safe-regex "^1.1.0"
+
+tslib@1.9.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
+ integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==
+
+tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
+ integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
+
+tslint-config-prettier@1.17.0:
+ version "1.17.0"
+ resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.17.0.tgz#946ed6117f98f3659a65848279156d87628c33dc"
+ integrity sha512-NKWNkThwqE4Snn4Cm6SZB7lV5RMDDFsBwz6fWUkTxOKGjMx8ycOHnjIbhn7dZd5XmssW3CwqUjlANR6EhP9YQw==
+
+tslint-consistent-codestyle@^1.14.1:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.15.0.tgz#a3acf8d0a3ca0dc7d1285705102ba1fe4a17c4cb"
+ integrity sha512-6BNDBbZh2K0ibRXe70Mkl9gfVttxQ3t3hqV1BRDfpIcjrUoOgD946iH4SrXp+IggDgeMs3dJORjD5tqL5j4jXg==
+ dependencies:
+ "@fimbul/bifrost" "^0.17.0"
+ tslib "^1.7.1"
+ tsutils "^2.29.0"
+
+tslint-eslint-rules@^5.4.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5"
+ integrity sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==
+ dependencies:
+ doctrine "0.7.2"
+ tslib "1.9.0"
+ tsutils "^3.0.0"
+
+tslint-microsoft-contrib@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.0.0.tgz#7bff73c9ad7a0b7eb5cdb04906de58f42a2bf7a2"
+ integrity sha512-R//efwn+34IUjTJeYgNDAJdzG0jyLWIehygPt/PHuZAieTolFVS56FgeFW7DOLap9ghXzMiFPTmDgm54qaL7QA==
+ dependencies:
+ tsutils "^2.27.2 <2.29.0"
+
+tslint-xo@0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/tslint-xo/-/tslint-xo-0.11.0.tgz#7cc78eb259823371edc71deff126a23afc28cef9"
+ integrity sha512-7CiaVI+5LxCtqqW3fl5/orjDmmH1MNTlw1/E2eC71XYengbINokLG1dY/024jofYDdcoWgAtxhVnV6PyNrndnQ==
+ dependencies:
+ tslint-consistent-codestyle "^1.14.1"
+ tslint-eslint-rules "^5.4.0"
+ tslint-microsoft-contrib "^6.0.0"
+
+tslint@5.12.0:
+ version "5.12.0"
+ resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.0.tgz#47f2dba291ed3d580752d109866fb640768fca36"
+ integrity sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ==
+ dependencies:
+ 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"
+ resolve "^1.3.2"
+ semver "^5.3.0"
+ tslib "^1.8.0"
+ tsutils "^2.27.2"
+
+tsutils@^2.27.2, tsutils@^2.29.0:
+ version "2.29.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
+ integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
+ dependencies:
+ tslib "^1.8.1"
+
+"tsutils@^2.27.2 <2.29.0":
+ version "2.28.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.28.0.tgz#6bd71e160828f9d019b6f4e844742228f85169a1"
+ integrity sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==
+ dependencies:
+ tslib "^1.8.1"
+
+tsutils@^3.0.0, tsutils@^3.5.0:
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.8.0.tgz#7a3dbadc88e465596440622b65c04edc8e187ae5"
+ integrity sha512-XQdPhgcoTbCD8baXC38PQ0vpTZ8T3YrE+vR66YIj/xvDt1//8iAhafpIT/4DmvzzC1QFapEImERu48Pa01dIUA==
+ dependencies:
+ tslib "^1.8.1"
+
+typescript@3.2.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5"
+ integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==
+
+union-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
+ integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=
+ dependencies:
+ arr-union "^3.1.0"
+ get-value "^2.0.6"
+ is-extendable "^0.1.1"
+ set-value "^0.4.3"
+
+unset-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+ integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+ dependencies:
+ has-value "^0.3.1"
+ isobject "^3.0.0"
+
+urix@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+ integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+use@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+ integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+ integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+ dependencies:
+ spdx-correct "^3.0.0"
+ spdx-expression-parse "^3.0.0"
+
+which@^1.2.10, which@^1.2.9:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+wrap-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba"
+ integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=
+ dependencies:
+ string-width "^2.1.1"
+ strip-ansi "^4.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=