You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2020/10/29 19:45:39 UTC

[trafficcontrol] branch 5.0.x updated (fd626f2 -> ab198b3)

This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a change to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git.


    from fd626f2  Fix POST /profileparameters validation error message (#5210) (#5224)
     new 3999693  Fix 403 forbidden in deliveryservices_test (#5214)
     new 7f25103  fixes broken context menus due to missing id (#5215)
     new 45f1954  Update Go version to 1.15.3 (#5158)
     new a9e5342  Enable includeSystemTests in the API tests GitHub Action (#5168)
     new defbee0  Servers Table - Single Click Navigation (#5217)
     new ab198b3  Fix compilation error in experimental tool (#5220)

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/actions/to-integration-tests/.eslintrc     |  333 ++++
 .github/actions/to-integration-tests/Dockerfile    |   32 -
 .github/actions/to-integration-tests/README.md     |   34 +-
 .github/actions/to-integration-tests/action.yml    |   20 +-
 .github/actions/to-integration-tests/cdn.json      |   59 +-
 .github/actions/to-integration-tests/database.json |    2 +-
 .github/actions/to-integration-tests/dbconf.yml    |    2 +-
 .../dist/to-integration-tests.js                   |   31 +
 .github/actions/to-integration-tests/entrypoint.sh |  129 +-
 .../actions/to-integration-tests/package-lock.json | 1994 ++++++++++++++++++++
 .github/actions/to-integration-tests/package.json  |   24 +
 .../actions/to-integration-tests/riak.json         |    4 +-
 .../to-integration-tests/to-integration-tests.ts   |   31 +
 .../to-integration-tests/traffic-ops-test.json     |    7 +-
 .github/actions/to-integration-tests/tsconfig.json |   14 +
 .github/workflows/go.fmt.yml                       |    2 +
 .github/workflows/go.unit.tests.yaml               |    2 +
 .github/workflows/traffic ops.yml                  |   18 +
 .gitignore                                         |    1 +
 GO_VERSION                                         |    2 +-
 experimental/traffic_ops_auth/traffic_ops_auth.go  |   25 +-
 .../testing/api/v3/deliveryservices_test.go        |    4 -
 .../testing/api/v3/serverservercapability_test.go  |    4 +-
 traffic_ops/testing/api/v3/tc-fixtures.json        |    6 +-
 .../table.cacheGroupServers.tpl.html               |    2 +-
 .../table/cdnServers/table.cdnServers.tpl.html     |    2 +-
 .../table.deliveryServiceJobs.tpl.html             |    2 +-
 .../table.deliveryServiceServers.tpl.html          |    2 +-
 .../table.physLocationServers.tpl.html             |    2 +-
 .../profileServers/table.profileServers.tpl.html   |    2 +-
 .../table/servers/TableServersController.js        |   17 +-
 .../statusServers/table.statusServers.tpl.html     |    2 +-
 .../topologyServers/table.topologyServers.tpl.html |    2 +-
 .../table/typeServers/table.typeServers.tpl.html   |    2 +-
 .../test/end_to_end/servers/servers-spec.js        |    2 +-
 35 files changed, 2687 insertions(+), 130 deletions(-)
 create mode 100644 .github/actions/to-integration-tests/.eslintrc
 delete mode 100644 .github/actions/to-integration-tests/Dockerfile
 create mode 100644 .github/actions/to-integration-tests/dist/to-integration-tests.js
 create mode 100644 .github/actions/to-integration-tests/package-lock.json
 create mode 100644 .github/actions/to-integration-tests/package.json
 copy traffic_ops/app/conf/development/riak.conf => .github/actions/to-integration-tests/riak.json (54%)
 create mode 100644 .github/actions/to-integration-tests/to-integration-tests.ts
 create mode 100644 .github/actions/to-integration-tests/tsconfig.json


[trafficcontrol] 05/06: Servers Table - Single Click Navigation (#5217)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit defbee0b66131ed4d0eb900fea526dbe591e6b9c
Author: Steve Hamrick <sh...@users.noreply.github.com>
AuthorDate: Thu Oct 29 12:17:46 2020 -0600

    Servers Table - Single Click Navigation (#5217)
    
    * Remove double click, allow for copying
    
    * Use tabs
    
    (cherry picked from commit cfcf2b8d2789a10d3beea25fc56bfdc892229d96)
---
 .../modules/table/servers/TableServersController.js     | 17 +++++++++++++----
 traffic_portal/test/end_to_end/servers/servers-spec.js  |  2 +-
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/traffic_portal/app/src/common/modules/table/servers/TableServersController.js b/traffic_portal/app/src/common/modules/table/servers/TableServersController.js
index 9432bbf..18cad9f 100644
--- a/traffic_portal/app/src/common/modules/table/servers/TableServersController.js
+++ b/traffic_portal/app/src/common/modules/table/servers/TableServersController.js
@@ -299,6 +299,8 @@ var TableServersController = function(tableName, servers, filter, $scope, $state
 
 	$scope.pageSize = 100;
 
+	$scope.mouseDownSelectionText = "";
+
 	/** Options, configuration, data and callbacks for the ag-grid table. */
 	$scope.gridOptions = {
 		components: {
@@ -358,10 +360,17 @@ var TableServersController = function(tableName, servers, filter, $scope, $state
 			$scope.server = params.data;
 			$scope.$apply();
 		},
-		onRowDoubleClicked: function(params) {
-			locationUtils.navigateToPath('/servers/' + params.data.id);
-			// Event is outside the digest cycle, so we need to trigger one.
-			$scope.$apply();
+		onCellMouseDown: function() {
+			$scope.mouseDownSelectionText = window.getSelection().toString();
+		},
+		onRowClicked: function(params) {
+			const selection = window.getSelection().toString();
+			if(selection === "" || selection === $scope.mouseDownSelectionText) {
+				locationUtils.navigateToPath('/servers/' + params.data.id);
+				// Event is outside the digest cycle, so we need to trigger one.
+				$scope.$apply();
+			}
+			$scope.mouseDownSelectionText = "";
 		},
 		onColumnResized: function(params) {
 			localStorage.setItem(tableName + "_table_columns", JSON.stringify($scope.gridOptions.columnApi.getColumnState()));
diff --git a/traffic_portal/test/end_to_end/servers/servers-spec.js b/traffic_portal/test/end_to_end/servers/servers-spec.js
index 9953a3b..61319a0 100644
--- a/traffic_portal/test/end_to_end/servers/servers-spec.js
+++ b/traffic_portal/test/end_to_end/servers/servers-spec.js
@@ -105,7 +105,7 @@ describe('Traffic Portal Servers Test Suite', function() {
 		console.log('Verifying new server added and updating ' + mockVals.hostName);
 		browser.sleep(1000);
 		let row = element(by.cssContainingText('.ag-cell', mockVals.hostName));
-		browser.actions().doubleClick(row).perform();
+		browser.actions().click(row).perform();
 		browser.sleep(1000);
 		pageData.domainName.clear();
 		pageData.domainName.sendKeys('testupdated.com');


[trafficcontrol] 03/06: Update Go version to 1.15.3 (#5158)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 45f1954f2858c867f972ce9f45ceb0ae59c161f4
Author: Zach Hoffman <zr...@apache.org>
AuthorDate: Thu Oct 29 11:17:27 2020 -0600

    Update Go version to 1.15.3 (#5158)
    
    * Update Go version to 1.15.3
    
    * Run Go unit tests and Traffic Ops API tests when the Go version is
    updated
    
    * Run gofmt action when GO_VERSION file is changed
    
    (cherry picked from commit a673e95e15656d1efbe31ec342a729a16cd97d04)
---
 .github/workflows/go.fmt.yml         | 2 ++
 .github/workflows/go.unit.tests.yaml | 2 ++
 .github/workflows/traffic ops.yml    | 2 ++
 GO_VERSION                           | 2 +-
 4 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/go.fmt.yml b/.github/workflows/go.fmt.yml
index d44f330..b4a09df 100644
--- a/.github/workflows/go.fmt.yml
+++ b/.github/workflows/go.fmt.yml
@@ -20,10 +20,12 @@ name: Go Format
 on:
   push:
     paths:
+      - GO_VERSION
       - "**.go"
   create:
   pull_request:
     paths:
+      - GO_VERSION
       - "**.go"
     types: [opened, reopened, edited, synchronize]
 
diff --git a/.github/workflows/go.unit.tests.yaml b/.github/workflows/go.unit.tests.yaml
index d01525a..45dd623 100644
--- a/.github/workflows/go.unit.tests.yaml
+++ b/.github/workflows/go.unit.tests.yaml
@@ -20,6 +20,7 @@ name: Go Unit Tests
 on:
   push:
     paths:
+      - GO_VERSION
       - grove/**.go
       - lib/**.go
       - traffic_monitor/**.go
@@ -29,6 +30,7 @@ on:
   create:
   pull_request:
     paths:
+      - GO_VERSION
       - grove/**.go
       - lib/**.go
       - traffic_monitor/**.go
diff --git a/.github/workflows/traffic ops.yml b/.github/workflows/traffic ops.yml
index 0db639f..c568431 100644
--- a/.github/workflows/traffic ops.yml	
+++ b/.github/workflows/traffic ops.yml	
@@ -20,12 +20,14 @@ name: Traffic Ops Go client/API integration tests
 on:
   push:
     paths:
+      - GO_VERSION
       - traffic_ops/traffic_ops_golang/**.go
       - traffic_ops/testing/api/**.go
       - traffic_ops/*client/**.go
   create:
   pull_request:
     paths:
+      - GO_VERSION
       - traffic_ops/traffic_ops_golang/**.go
       - traffic_ops/testing/api/**.go
       - traffic_ops/*client/**.go
diff --git a/GO_VERSION b/GO_VERSION
index 42cf067..f2380cc 100644
--- a/GO_VERSION
+++ b/GO_VERSION
@@ -1 +1 @@
-1.15.2
+1.15.3


[trafficcontrol] 04/06: Enable includeSystemTests in the API tests GitHub Action (#5168)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit a9e53424618b71ab686d1953d1fafe5f5b2707de
Author: Zach Hoffman <zr...@apache.org>
AuthorDate: Thu Oct 29 11:26:15 2020 -0600

    Enable includeSystemTests in the API tests GitHub Action (#5168)
    
    * Add Riak server to TO integration tests action
    
    * Add SMTP server to TO integration tests action
    
    * Specify inputs in action.yml
    
    * Declare $trafficvault outside of start_trafic_vault() because variables
    declared inside of functions are local in Almquist Shell
    
    * Show (and colorize) Traffic Ops logs in real time instead of after tests
    run
    
    * Make "Run API Tests" a JavaScript action
    
    * Add a newline
    
    * Add license to .eslintrc
    
    * Remove unused JS file
    
    * Remove unnecessary cats
    
    * Define yellow_bg
    
    * Remove redundant `docker rm`
    
    * Gitignore GitHub Actions node_modules
    
    * Use tabs
    
    * Add pickier eslintrc, courtesy of @ocket8888
    
    * Satisfy new eslintrc
    
    * Use ?? 1 instead of || 1
    
    * Set "browser" to false in elintrc
    
    * Rebuild script
    
    (cherry picked from commit 4cd6fbace3c450b5345141e4fb38cdd54b040e29)
---
 .github/actions/to-integration-tests/.eslintrc     |  333 ++++
 .github/actions/to-integration-tests/Dockerfile    |   32 -
 .github/actions/to-integration-tests/README.md     |   34 +-
 .github/actions/to-integration-tests/action.yml    |   20 +-
 .github/actions/to-integration-tests/cdn.json      |   59 +-
 .github/actions/to-integration-tests/database.json |    2 +-
 .github/actions/to-integration-tests/dbconf.yml    |    2 +-
 .../dist/to-integration-tests.js                   |   31 +
 .github/actions/to-integration-tests/entrypoint.sh |  129 +-
 .../actions/to-integration-tests/package-lock.json | 1994 ++++++++++++++++++++
 .github/actions/to-integration-tests/package.json  |   24 +
 .github/actions/to-integration-tests/riak.json     |    8 +
 .../to-integration-tests/to-integration-tests.ts   |   31 +
 .../to-integration-tests/traffic-ops-test.json     |    7 +-
 .github/actions/to-integration-tests/tsconfig.json |   14 +
 .github/workflows/traffic ops.yml                  |   16 +
 .gitignore                                         |    1 +
 .../testing/api/v3/serverservercapability_test.go  |    4 +-
 traffic_ops/testing/api/v3/tc-fixtures.json        |    6 +-
 19 files changed, 2644 insertions(+), 103 deletions(-)

diff --git a/.github/actions/to-integration-tests/.eslintrc b/.github/actions/to-integration-tests/.eslintrc
new file mode 100644
index 0000000..f7ae69c
--- /dev/null
+++ b/.github/actions/to-integration-tests/.eslintrc
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+{
+	"env": {
+		"browser": false,
+		"es6": true,
+		"node": true
+	},
+	"parser": "@typescript-eslint/parser",
+	"parserOptions": {
+		"project": "tsconfig.json",
+		"sourceType": "module"
+	},
+	"plugins": [
+		"eslint-plugin-jsdoc",
+		"eslint-plugin-import",
+		"@typescript-eslint",
+		"@typescript-eslint/tslint"
+	],
+	"rules": {
+		"@typescript-eslint/array-type": "off",
+		"@typescript-eslint/await-thenable": "error",
+		"@typescript-eslint/consistent-type-assertions": "error",
+		"@typescript-eslint/consistent-type-definitions": "error",
+		"@typescript-eslint/dot-notation": "error",
+		"@typescript-eslint/explicit-function-return-type": "error",
+		"@typescript-eslint/explicit-member-accessibility": [
+			"error",
+			{
+				"accessibility": "explicit"
+			}
+		],
+		"@typescript-eslint/explicit-module-boundary-types": "error",
+		"@typescript-eslint/indent": [
+			"off",
+			"tab"
+		],
+		"@typescript-eslint/member-delimiter-style": [
+			"error",
+			{
+				"multiline": {
+					"delimiter": "none",
+					"requireLast": true
+				},
+				"singleline": {
+					"delimiter": "semi",
+					"requireLast": false
+				}
+			}
+		],
+		"@typescript-eslint/member-ordering": "error",
+		"@typescript-eslint/naming-convention": "error",
+		"@typescript-eslint/no-empty-function": "error",
+		"@typescript-eslint/no-empty-interface": "error",
+		"@typescript-eslint/no-explicit-any": "error",
+		"@typescript-eslint/no-extraneous-class": "error",
+		"@typescript-eslint/no-floating-promises": "off",
+		"@typescript-eslint/no-inferrable-types": [
+			"error",
+			{
+				"ignoreParameters": true
+			}
+		],
+		"@typescript-eslint/no-misused-new": "error",
+		"@typescript-eslint/no-non-null-assertion": "error",
+		"@typescript-eslint/no-require-imports": "error",
+		"@typescript-eslint/no-this-alias": "error",
+		"@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
+		"@typescript-eslint/no-unnecessary-qualifier": "error",
+		"@typescript-eslint/no-unnecessary-type-arguments": "error",
+		"@typescript-eslint/no-unnecessary-type-assertion": "error",
+		"@typescript-eslint/no-unused-expressions": "error",
+		"@typescript-eslint/no-unused-vars": "error",
+		"@typescript-eslint/no-use-before-define": "error",
+		"@typescript-eslint/no-var-requires": "off",
+		"@typescript-eslint/prefer-for-of": "error",
+		"@typescript-eslint/prefer-function-type": "error",
+		"@typescript-eslint/prefer-readonly": "error",
+		"@typescript-eslint/promise-function-async": "error",
+		"@typescript-eslint/quotes": [
+			"error",
+			"double"
+		],
+		"@typescript-eslint/semi": [
+			"error"
+		],
+		"@typescript-eslint/tslint/config": [
+			"error",
+			{
+				"rules": {
+					"completed-docs": [
+						true,
+						{
+							"classes": {
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						{
+							"enums": {
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						"enum-members",
+						{
+							"functions": {
+								"overloads": true,
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						{
+							"interfaces": {
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						{
+							"namespaces": {
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						{
+							"types": {
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						{
+							"variables": {
+								"visibilities": [
+									"exported"
+								]
+							}
+						},
+						{
+							"methods": {
+								"overloads": true,
+								"privacies": [
+									"public"
+								]
+							}
+						},
+						{
+							"properties": {
+								"privacies": [
+									"public"
+								]
+							}
+						}
+					],
+					"import-spacing": true,
+					"invalid-void": true,
+					"no-dynamic-delete": true,
+					"no-inferred-empty-object-type": true,
+					"no-null-undefined-union": true,
+					"no-promise-as-boolean": true,
+					"no-tautology-expression": true,
+					"no-unnecessary-callback-wrapper": true,
+					"number-literal-format": true,
+					"object-literal-sort-keys": true,
+					"prefer-while": true,
+					"static-this": true,
+					"typedef": [
+						true,
+						"call-signature",
+						"parameter",
+						"property-declaration",
+						"member-variable",
+						"object-destructuring",
+						"array-destructuring"
+					],
+					"unnecessary-else": true,
+					"whitespace": [
+						true,
+						"check-decl",
+						"check-module",
+						"check-separator",
+						"check-rest-spread",
+						"check-type",
+						"check-typecast",
+						"check-type-operator"
+					]
+				}
+			}
+		],
+		"@typescript-eslint/unbound-method": "error",
+		"arrow-body-style": "error",
+		"arrow-parens": [
+			"off",
+			"always"
+		],
+		"class-methods-use-this": "error",
+		"comma-dangle": "off",
+		"constructor-super": "error",
+		"curly": "error",
+		"eol-last": "error",
+		"eqeqeq": [
+			"error",
+			"always"
+		],
+		"id-blacklist": [
+			"error",
+			"any",
+			"Number",
+			"number",
+			"String",
+			"string",
+			"Boolean",
+			"boolean",
+			"Undefined",
+			"undefined"
+		],
+		"id-match": "error",
+		"import/no-deprecated": "warn",
+		"import/order": "off",
+		"jsdoc/check-alignment": "error",
+		"jsdoc/check-indentation": "error",
+		"jsdoc/newline-after-description": "error",
+		"jsdoc/no-types": "error",
+		"linebreak-style": [
+			"error",
+			"unix"
+		],
+		"max-classes-per-file": "off",
+		"max-len": [
+			"error",
+			{
+				"code": 140
+			}
+		],
+		"new-parens": "error",
+		"no-caller": "error",
+		"no-console": [
+			"error",
+			{
+				"allow": [
+					"assert",
+					"clear",
+					"Console",
+					"context",
+					"count",
+					"countReset",
+					"dir",
+					"dirxml",
+					"error",
+					"group",
+					"groupCollapsed",
+					"groupEnd",
+					"info",
+					"log",
+					"profile",
+					"profileEnd",
+					"table",
+					"timeLog",
+					"timeStamp",
+					"warn"
+				]
+			}
+		],
+		"no-debugger": "error",
+		"no-duplicate-case": "error",
+		"no-else-return": [
+			"error",
+			{
+				"allowElseIf": false
+			}
+		],
+		"no-empty": "error",
+		"no-eval": "error",
+		"no-extra-bind": "error",
+		"no-fallthrough": "error",
+		"no-invalid-this": "error",
+		"no-multiple-empty-lines": "off",
+		"no-new-func": "error",
+		"no-redeclare": "error",
+		"no-restricted-imports": [
+			"error",
+			"rxjs/Rx"
+		],
+		"no-return-await": "error",
+		"no-shadow": [
+			"error",
+			{
+				"hoist": "all"
+			}
+		],
+		"no-sparse-arrays": "error",
+		"no-template-curly-in-string": "error",
+		"no-throw-literal": "error",
+		"no-trailing-spaces": "error",
+		"no-undef-init": "error",
+		"no-underscore-dangle": "error",
+		"no-unsafe-finally": "error",
+		"no-useless-constructor": "error",
+		"no-var": "error",
+		"one-var": [
+			"error",
+			"never"
+		],
+		"prefer-const": "error",
+		"prefer-object-spread": "error",
+		"prefer-template": "error",
+		"quote-props": [
+			"error",
+			"as-needed"
+		],
+		"radix": "error",
+		"use-isnan": "error"
+	}
+}
diff --git a/.github/actions/to-integration-tests/Dockerfile b/.github/actions/to-integration-tests/Dockerfile
deleted file mode 100644
index 661e0cf..0000000
--- a/.github/actions/to-integration-tests/Dockerfile
+++ /dev/null
@@ -1,32 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-FROM alpine:3.12
-
-RUN architecture=$(uname -m) && \
-    mkdir lib64 && \
-    # Use musl libc where the go binary expects glibc
-    # Less-generalized: ln -s /lib/ld-musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
-    ln -s /lib/ld-musl-${architecture}.so.[0-9] /lib64/ld-linux-${architecture//_/-}.so.2
-ENV GOROOT=/usr/local/go \
-    CGO_ENABLED=0
-
-RUN apk add --no-cache gettext git
-
-COPY entrypoint.sh database.json traffic-ops-test.json cdn.json /
-
-ENTRYPOINT /entrypoint.sh
diff --git a/.github/actions/to-integration-tests/README.md b/.github/actions/to-integration-tests/README.md
index 6f81959..324e9b6 100644
--- a/.github/actions/to-integration-tests/README.md
+++ b/.github/actions/to-integration-tests/README.md
@@ -17,15 +17,28 @@
   under the License.
 -->
 
-# to-integration-tests Docker action
-This action runs the Traffic Ops Go client integration tests with the Traffic
-Ops API.
+# to-integration-tests JavaScript action
+This action runs the Traffic Ops Go client integration tests with the Traffic Ops API.
+- Requires an SMTP service (see `smtp_address` input)
+- Provides a Riak server at address `trafficvault.infra.ciab.test`
 
 ## Inputs
 
 ### `version`
 **Required** Major API version to test e.g. 1, 2, 3 etc.
 
+### `smtp_address`
+**Required** The address of an SMTP server for use by Traffic Ops.
+
+### `smtp_port`
+**Required** The address of an SMTP server for use by Traffic Ops. Required but defaults to `25`.
+
+### `smtp_user`
+**Optional** The user to authenticate with for the SMTP server.
+
+### `smtp_password`
+**Optional** The password to authenticate with for the SMTP server.
+
 ## Outputs
 
 ### `exit-code`
@@ -48,6 +61,20 @@ jobs:
         - 5432:5432
         options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
 
+      smtp:
+        image: maildev/maildev:2.0.0-beta3
+        ports:
+          - 25:25
+        options: >-
+          --entrypoint=bin/maildev
+          --user=root
+          --health-cmd="sh -c \"[[ \$(wget -qO- http://smtp/healthz) == true ]]\""
+          --
+          maildev/maildev:2.0.0-beta3
+          --smtp=25
+          --hide-extensions=STARTTLS
+          --web=80
+
     steps:
       - name: Checkout
         uses: actions/checkout@master
@@ -57,4 +84,5 @@ jobs:
         uses: ./.github/actions/to-integration-tests
         with:
           version: 5
+          smtp_address: localhost
 ```
diff --git a/.github/actions/to-integration-tests/action.yml b/.github/actions/to-integration-tests/action.yml
index 8c9d57f..63df76b 100644
--- a/.github/actions/to-integration-tests/action.yml
+++ b/.github/actions/to-integration-tests/action.yml
@@ -21,8 +21,20 @@ inputs:
   version:
     description: 'Version of Traffic Ops API against which to run tests, e.g. 1, 2, 3, etc.'
     required: true
+    default: '3'
+  smtp_address:
+    description: 'Address of an SMTP server to use for the Traffic Ops API tests'
+    required: true
+  smtp_port:
+    description: 'Port of an SMTP server to use for the Traffic Ops API tests'
+    required: true
+    default: '25'
+  smtp_user:
+    description: 'The user to authenticate with for the SMTP server.'
+    required: false
+  smtp_password:
+    description: 'The user to authenticate with for the SMTP server.'
+    required: false
 runs:
-  using: 'docker'
-  image: 'Dockerfile'
-  args:
-    - ${{ inputs.version }}
+  using: 'node12'
+  main: 'dist/to-integration-tests.js'
diff --git a/.github/actions/to-integration-tests/cdn.json b/.github/actions/to-integration-tests/cdn.json
index efda921..890ade9 100644
--- a/.github/actions/to-integration-tests/cdn.json
+++ b/.github/actions/to-integration-tests/cdn.json
@@ -1,20 +1,20 @@
 {
-	"hypnotoad" : {
-		"listen" : [
+	"hypnotoad": {
+		"listen": [
 			"https://not-a-real-host.test:1?cert=$PWD/localhost.crt&key=$PWD/localhost.key&verify=0x00&ciphers=AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH:!ED"
 		],
-		"user" : "trafops",
-		"group" : "trafops",
-		"heartbeat_timeout" : 20,
-		"pid_file" : "/var/run/traffic_ops.pid",
-		"workers" : 12
+		"user": "trafops",
+		"group": "trafops",
+		"heartbeat_timeout": 20,
+		"pid_file": "/var/run/traffic_ops.pid",
+		"workers": 12
 	},
 	"use_ims": true,
-	"traffic_ops_golang" : {
+	"traffic_ops_golang": {
 		"insecure": true,
-		"port" : "6443",
-		"log_location_error": "stderr",
-		"log_location_warning": "stderr",
+		"port": "6443",
+		"log_location_error": "error.log",
+		"log_location_warning": "warning.log",
 		"log_location_info": null,
 		"log_location_debug": null,
 		"log_location_event": null,
@@ -23,29 +23,32 @@
 		"db_query_timeout_seconds": 20,
 		"supported_ds_metrics": [ "kbps", "tps_total", "tps_2xx", "tps_3xx", "tps_4xx", "tps_5xx" ]
 	},
-	"cors" : {
-		"access_control_allow_origin" : "*"
+	"cors": {
+		"access_control_allow_origin": "*"
 	},
-	"to" : {
-		"base_url" : "https://localhost",
-		"email_from" : "no-reply@traffic-ops.test",
-		"no_account_found_msg" : "A Traffic Ops user account is required for access. Please contact your Traffic Ops user administrator."
+	"to": {
+		"base_url": "https://localhost",
+		"email_from": "no-reply@traffic-ops.test",
+		"no_account_found_msg": "A Traffic Ops user account is required for access. Please contact your Traffic Ops user administrator."
 	},
-	"portal" : {
-		"base_url" : "https://not-a-real-host.test/#!/",
-		"email_from" : "no-reply@traffic-portal.test",
-		"pass_reset_path" : "user",
-		"user_register_path" : "user"
+	"portal": {
+		"base_url": "https://not-a-real-host.test/#!/",
+		"email_from": "no-reply@traffic-portal.test",
+		"pass_reset_path": "user",
+		"user_register_path": "user"
 	},
-	"secrets" : [
+	"secrets": [
 		"blahblah"
 	],
-	"geniso" : {
-		"iso_root_path" : "/opt/traffic_ops/app/public"
+	"geniso": {
+		"iso_root_path": "/opt/traffic_ops/app/public"
 	},
-	"inactivity_timeout" : 60,
-	"smtp" : {
-		"enabled" : false
+	"inactivity_timeout": 60,
+	"smtp": {
+		"enabled": true,
+		"user": "$INPUT_SMTP_USER",
+		"password": "$INPUT_SMTP_PASSWORD",
+		"address": "${INPUT_SMTP_ADDRESS}:${INPUT_SMTP_PORT}"
 	},
 	"InfluxEnabled": false
 }
diff --git a/.github/actions/to-integration-tests/database.json b/.github/actions/to-integration-tests/database.json
index 8d08eb4..778be80 100644
--- a/.github/actions/to-integration-tests/database.json
+++ b/.github/actions/to-integration-tests/database.json
@@ -1,7 +1,7 @@
 {
 	"description": "Local PostgreSQL database on port 5432",
 	"dbname": "traffic_ops",
-	"hostname": "postgres",
+	"hostname": "172.17.0.1",
 	"user": "traffic_ops",
 	"password": "twelve",
 	"port": "5432",
diff --git a/.github/actions/to-integration-tests/dbconf.yml b/.github/actions/to-integration-tests/dbconf.yml
index f565670..3078589 100644
--- a/.github/actions/to-integration-tests/dbconf.yml
+++ b/.github/actions/to-integration-tests/dbconf.yml
@@ -19,5 +19,5 @@ name: dbconf.yml
 
 test:
   driver: postgres
-  open: host=postgres port=5432 user=traffic_ops password=twelve dbname=traffic_ops sslmode=disable
+  open: host=172.17.0.1 port=5432 user=traffic_ops password=twelve dbname=traffic_ops sslmode=disable
 
diff --git a/.github/actions/to-integration-tests/dist/to-integration-tests.js b/.github/actions/to-integration-tests/dist/to-integration-tests.js
new file mode 100644
index 0000000..6d1f833
--- /dev/null
+++ b/.github/actions/to-integration-tests/dist/to-integration-tests.js
@@ -0,0 +1,31 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const child_process_1 = __importDefault(require("child_process"));
+const path_1 = __importDefault(require("path"));
+const spawnOptions = { stdio: "inherit" };
+function runProcess(...commandArguments) {
+    console.info(...commandArguments);
+    const { status } = child_process_1.default.spawnSync(commandArguments[0], commandArguments.slice(1), spawnOptions);
+    if (status === 0) {
+        return;
+    }
+    console.error("Child process \"", ...commandArguments, "\" exited with status code", status, "!");
+    process.exit(status !== null && status !== void 0 ? status : 1);
+}
+runProcess(path_1.default.join(__dirname, "../entrypoint.sh"));
diff --git a/.github/actions/to-integration-tests/entrypoint.sh b/.github/actions/to-integration-tests/entrypoint.sh
index 8fae324..d3c4ff2 100755
--- a/.github/actions/to-integration-tests/entrypoint.sh
+++ b/.github/actions/to-integration-tests/entrypoint.sh
@@ -1,4 +1,4 @@
-#!/bin/sh -l
+#!/bin/bash
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -17,15 +17,90 @@
 # under the License.
 
 download_go() {
+	. build/functions.sh
+	if verify_and_set_go_version; then
+		return
+	fi
 	go_version="$(cat "${GITHUB_WORKSPACE}/GO_VERSION")"
 	wget -O go.tar.gz "https://dl.google.com/go/go${go_version}.linux-amd64.tar.gz"
-	tar -C /usr/local -xzf go.tar.gz
+	echo "Extracting Go ${go_version}..."
+	<<-'SUDO_COMMANDS' sudo sh
+		set -o errexit
+		go_dir="$(
+			dirname "$(
+				dirname "$(
+					realpath "$(
+						which go
+						)")")")"
+		mv "$go_dir" "${go_dir}.unused"
+		tar -C /usr/local -xzf go.tar.gz
+	SUDO_COMMANDS
 	rm go.tar.gz
-	export PATH="${PATH}:${GOROOT}/bin"
 	go version
 }
-download_go
 
+gray_bg="$(printf '%s%s' $'\x1B' '[100m')";
+red_bg="$(printf '%s%s' $'\x1B' '[41m')";
+yellow_bg="$(printf '%s%s' $'\x1B' '[43m')";
+black_fg="$(printf '%s%s' $'\x1B' '[30m')";
+color_and_prefix() {
+	color="$1";
+	shift;
+	prefix="$1";
+	normal_bg="$(printf '%s%s' $'\x1B' '[49m')";
+	normal_fg="$(printf '%s%s' $'\x1B' '[39m')";
+	sed "s/^/${color}${black_fg}${prefix}: /" | sed "s/$/${normal_bg}${normal_fg}/";
+}
+
+ciab_dir="${GITHUB_WORKSPACE}/infrastructure/cdn-in-a-box";
+trafficvault=trafficvault;
+start_traffic_vault() {
+	<<-'/ETC/HOSTS' sudo tee --append /etc/hosts
+		172.17.0.1    trafficvault.infra.ciab.test
+	/ETC/HOSTS
+
+	<<-'BASH_LINES' cat >infrastructure/cdn-in-a-box/traffic_vault/prestart.d/00-0-standalone-config.sh;
+		TV_FQDN="${TV_HOST}.${INFRA_SUBDOMAIN}.${TLD_DOMAIN}" # Also used in 02-add-search-schema.sh
+		certs_dir=/etc/ssl/certs;
+		X509_INFRA_CERT_FILE="${certs_dir}/trafficvault.crt";
+		X509_INFRA_KEY_FILE="${certs_dir}/trafficvault.key";
+
+		# Generate x509 certificate
+		openssl req -new -x509 -nodes -newkey rsa:4096 -out "$X509_INFRA_CERT_FILE" -keyout "$X509_INFRA_KEY_FILE" -subj "/CN=${TV_FQDN}";
+
+		# Do not wait for CDN in a Box to generate SSL keys
+		sed -i '0,/^update-ca-certificates/d' /etc/riak/prestart.d/00-config.sh;
+
+		# Do not try to source to-access.sh
+		sed -i '/to-access\.sh/d' /etc/riak/{prestart.d,poststart.d}/*
+	BASH_LINES
+
+	DOCKER_BUILDKIT=1 docker build "$ciab_dir" -f "${ciab_dir}/traffic_vault/Dockerfile" -t "$trafficvault" 2>&1 |
+		color_and_prefix "$gray_bg" "building Traffic Vault";
+	if [[ "$INPUT_VERSION" -lt 3 ]]; then
+		echo 'Not starting Traffic Vault for API versions less than 3'
+		return;
+	fi;
+	echo 'Starting Traffic Vault...';
+	docker run \
+		--detach \
+		--env-file="${ciab_dir}/variables.env" \
+		--hostname="${trafficvault}.infra.ciab.test" \
+		--name="$trafficvault" \
+		--publish=8087:8087 \
+		--rm \
+		"$trafficvault" \
+		/usr/lib/riak/riak-cluster.sh;
+	docker logs -f "$trafficvault" 2>&1 |
+		color_and_prefix "$gray_bg" 'Traffic Vault';
+}
+start_traffic_vault &
+
+sudo apt-get install -y --no-install-recommends gettext
+
+GOROOT=/usr/local/go
+export GOROOT PATH="${PATH}:${GOROOT}/bin"
+download_go
 export GOPATH="$(mktemp -d)"
 srcdir="$GOPATH/src/github.com/apache"
 mkdir -p "$srcdir"
@@ -34,8 +109,8 @@ ln -s "$PWD" "$srcdir/trafficcontrol"
 cd "$srcdir/trafficcontrol/traffic_ops/traffic_ops_golang"
 
 
-/usr/local/go/bin/go get ./...
-/usr/local/go/bin/go build .
+go get ./...
+go build .
 
 echo "
 -----BEGIN CERTIFICATE-----
@@ -91,31 +166,33 @@ A22D22wvfs7CE3cUz/8UnvLM3kbTTu1WbbBbrHjAV47sAHjW/ckTqeo=
 -----END RSA PRIVATE KEY-----
 " > localhost.key
 
-envsubst </cdn.json >cdn.conf
-mv /database.json ./database.conf
+resources="$(dirname "$0")"
+envsubst <"${resources}/cdn.json" >cdn.conf
+cp "${resources}/database.json" database.conf
 
-./traffic_ops_golang --cfg ./cdn.conf --dbcfg ./database.conf >out.log 2>err.log &
+export $(<"${ciab_dir}/variables.env" sed '/^#/d') # defines TV_ADMIN_USER/PASSWORD
+envsubst <"${resources}/riak.json" >riak.conf
 
-if [ -z "$INPUT_VERSION" ]; then
-	INPUT_VERSION="3";
-fi
+truncate --size=0 warning.log error.log # Removes output from previous API versions and makes sure files exist
+./traffic_ops_golang --cfg ./cdn.conf --dbcfg ./database.conf -riakcfg riak.conf &
 
-cd "../testing/api/v$INPUT_VERSION"
+# TODO - Make these logs build artifacts
+# 2>&1 makes terminal output go faster, even though stderr will not contain anything
+tail -f warning.log 2>&1 | color_and_prefix "${yellow_bg}" 'Traffic Ops' &
+tail -f error.log 2>&1 | color_and_prefix "${red_bg}" 'Traffic Ops' &
 
-cp /traffic-ops-test.json ./traffic-ops-test.conf
-/usr/local/go/bin/go test -v --cfg ./traffic-ops-test.conf
-CODE="$?"
-rm traffic-ops-test.conf
 
-# TODO - make these build artifacts
-if [ -f ../../../traffic_ops_golang/out.log ]; then
-	cat ../../../traffic_ops_golang/out.log
-	rm ../.../../traffic_ops_golang/out.log
-fi
+cd "../testing/api/v$INPUT_VERSION"
 
-if [ -f ../../../traffic_ops_golang/err.log ]; then
-	cat ../../../traffic_ops_golang/err.log >&2
-	rm ../../../traffic_ops_golang/err.log
-fi
+cp "${resources}/traffic-ops-test.json" traffic-ops-test.conf
+go test -test.v --cfg traffic-ops-test.conf
+CODE=$?
+rm traffic-ops-test.conf
+if [[ "$INPUT_VERSION" -ge 3 ]]; then
+	echo 'Stopping Traffic Vault...'
+	docker kill "$trafficvault";
+fi;
+echo 'Killing background jobs...';
+kill -9 $(jobs -p);
 
 exit "$CODE"
diff --git a/.github/actions/to-integration-tests/package-lock.json b/.github/actions/to-integration-tests/package-lock.json
new file mode 100644
index 0000000..2184aeb
--- /dev/null
+++ b/.github/actions/to-integration-tests/package-lock.json
@@ -0,0 +1,1994 @@
+{
+    "name": "to-integration-tests",
+    "version": "0.0.0",
+    "lockfileVersion": 1,
+    "requires": true,
+    "dependencies": {
+        "@babel/code-frame": {
+            "version": "7.10.4",
+            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+            "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+            "dev": true,
+            "requires": {
+                "@babel/highlight": "^7.10.4"
+            }
+        },
+        "@babel/helper-validator-identifier": {
+            "version": "7.10.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+            "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+            "dev": true
+        },
+        "@babel/highlight": {
+            "version": "7.10.4",
+            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+            "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+            "dev": true,
+            "requires": {
+                "@babel/helper-validator-identifier": "^7.10.4",
+                "chalk": "^2.0.0",
+                "js-tokens": "^4.0.0"
+            },
+            "dependencies": {
+                "chalk": {
+                    "version": "2.4.2",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+                    "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
+                    }
+                }
+            }
+        },
+        "@eslint/eslintrc": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
+            "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.12.4",
+                "debug": "^4.1.1",
+                "espree": "^7.3.0",
+                "globals": "^12.1.0",
+                "ignore": "^4.0.6",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^3.13.1",
+                "lodash": "^4.17.19",
+                "minimatch": "^3.0.4",
+                "strip-json-comments": "^3.1.1"
+            },
+            "dependencies": {
+                "ignore": {
+                    "version": "4.0.6",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+                    "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+                    "dev": true
+                }
+            }
+        },
+        "@nodelib/fs.scandir": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+            "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+            "dev": true,
+            "requires": {
+                "@nodelib/fs.stat": "2.0.3",
+                "run-parallel": "^1.1.9"
+            }
+        },
+        "@nodelib/fs.stat": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+            "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+            "dev": true
+        },
+        "@nodelib/fs.walk": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+            "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+            "dev": true,
+            "requires": {
+                "@nodelib/fs.scandir": "2.1.3",
+                "fastq": "^1.6.0"
+            }
+        },
+        "@types/json-schema": {
+            "version": "7.0.6",
+            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
+            "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
+            "dev": true
+        },
+        "@types/json5": {
+            "version": "0.0.29",
+            "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+            "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+            "dev": true
+        },
+        "@types/node": {
+            "version": "14.14.0",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.0.tgz",
+            "integrity": "sha512-BfbIHP9IapdupGhq/hc+jT5dyiBVZ2DdeC5WwJWQWDb0GijQlzUFAeIQn/2GtvZcd2HVUU7An8felIICFTC2qg==",
+            "dev": true
+        },
+        "@typescript-eslint/eslint-plugin": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.5.0.tgz",
+            "integrity": "sha512-mjb/gwNcmDKNt+6mb7Aj/TjKzIJjOPcoCJpjBQC9ZnTRnBt1p4q5dJSSmIqAtsZ/Pff5N+hJlbiPc5bl6QN4OQ==",
+            "dev": true,
+            "requires": {
+                "@typescript-eslint/experimental-utils": "4.5.0",
+                "@typescript-eslint/scope-manager": "4.5.0",
+                "debug": "^4.1.1",
+                "functional-red-black-tree": "^1.0.1",
+                "regexpp": "^3.0.0",
+                "semver": "^7.3.2",
+                "tsutils": "^3.17.1"
+            }
+        },
+        "@typescript-eslint/eslint-plugin-tslint": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-4.6.0.tgz",
+            "integrity": "sha512-3ba/AAcufH6Gd2UB4Qzsx0PoxzALiad5sulv82kshNJju1bWcvFa3EKdleGwBjEkaK8pvo1bE3n4wZFP2p6Y3g==",
+            "dev": true,
+            "requires": {
+                "@typescript-eslint/experimental-utils": "4.6.0",
+                "lodash": "^4.17.15"
+            },
+            "dependencies": {
+                "@typescript-eslint/experimental-utils": {
+                    "version": "4.6.0",
+                    "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.6.0.tgz",
+                    "integrity": "sha512-pnh6Beh2/4xjJVNL+keP49DFHk3orDHHFylSp3WEjtgW3y1U+6l+jNnJrGlbs6qhAz5z96aFmmbUyKhunXKvKw==",
+                    "dev": true,
+                    "requires": {
+                        "@types/json-schema": "^7.0.3",
+                        "@typescript-eslint/scope-manager": "4.6.0",
+                        "@typescript-eslint/types": "4.6.0",
+                        "@typescript-eslint/typescript-estree": "4.6.0",
+                        "eslint-scope": "^5.0.0",
+                        "eslint-utils": "^2.0.0"
+                    }
+                },
+                "@typescript-eslint/scope-manager": {
+                    "version": "4.6.0",
+                    "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.6.0.tgz",
+                    "integrity": "sha512-uZx5KvStXP/lwrMrfQQwDNvh2ppiXzz5TmyTVHb+5TfZ3sUP7U1onlz3pjoWrK9konRyFe1czyxObWTly27Ang==",
+                    "dev": true,
+                    "requires": {
+                        "@typescript-eslint/types": "4.6.0",
+                        "@typescript-eslint/visitor-keys": "4.6.0"
+                    }
+                },
+                "@typescript-eslint/types": {
+                    "version": "4.6.0",
+                    "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.6.0.tgz",
+                    "integrity": "sha512-5FAgjqH68SfFG4UTtIFv+rqYJg0nLjfkjD0iv+5O27a0xEeNZ5rZNDvFGZDizlCD1Ifj7MAbSW2DPMrf0E9zjA==",
+                    "dev": true
+                },
+                "@typescript-eslint/typescript-estree": {
+                    "version": "4.6.0",
+                    "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.6.0.tgz",
+                    "integrity": "sha512-s4Z9qubMrAo/tw0CbN0IN4AtfwuehGXVZM0CHNMdfYMGBDhPdwTEpBrecwhP7dRJu6d9tT9ECYNaWDHvlFSngA==",
+                    "dev": true,
+                    "requires": {
+                        "@typescript-eslint/types": "4.6.0",
+                        "@typescript-eslint/visitor-keys": "4.6.0",
+                        "debug": "^4.1.1",
+                        "globby": "^11.0.1",
+                        "is-glob": "^4.0.1",
+                        "lodash": "^4.17.15",
+                        "semver": "^7.3.2",
+                        "tsutils": "^3.17.1"
+                    }
+                },
+                "@typescript-eslint/visitor-keys": {
+                    "version": "4.6.0",
+                    "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.6.0.tgz",
+                    "integrity": "sha512-38Aa9Ztl0XyFPVzmutHXqDMCu15Xx8yKvUo38Gu3GhsuckCh3StPI5t2WIO9LHEsOH7MLmlGfKUisU8eW1Sjhg==",
+                    "dev": true,
+                    "requires": {
+                        "@typescript-eslint/types": "4.6.0",
+                        "eslint-visitor-keys": "^2.0.0"
+                    }
+                }
+            }
+        },
+        "@typescript-eslint/experimental-utils": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.5.0.tgz",
+            "integrity": "sha512-bW9IpSAKYvkqDGRZzayBXIgPsj2xmmVHLJ+flGSoN0fF98pGoKFhbunIol0VF2Crka7z984EEhFi623Rl7e6gg==",
+            "dev": true,
+            "requires": {
+                "@types/json-schema": "^7.0.3",
+                "@typescript-eslint/scope-manager": "4.5.0",
+                "@typescript-eslint/types": "4.5.0",
+                "@typescript-eslint/typescript-estree": "4.5.0",
+                "eslint-scope": "^5.0.0",
+                "eslint-utils": "^2.0.0"
+            }
+        },
+        "@typescript-eslint/parser": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.5.0.tgz",
+            "integrity": "sha512-xb+gmyhQcnDWe+5+xxaQk5iCw6KqXd8VQxGiTeELTMoYeRjpocZYYRP1gFVM2C8Yl0SpUvLa1lhprwqZ00w3Iw==",
+            "dev": true,
+            "requires": {
+                "@typescript-eslint/scope-manager": "4.5.0",
+                "@typescript-eslint/types": "4.5.0",
+                "@typescript-eslint/typescript-estree": "4.5.0",
+                "debug": "^4.1.1"
+            }
+        },
+        "@typescript-eslint/scope-manager": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.5.0.tgz",
+            "integrity": "sha512-C0cEO0cTMPJ/w4RA/KVe4LFFkkSh9VHoFzKmyaaDWAnPYIEzVCtJ+Un8GZoJhcvq+mPFXEsXa01lcZDHDG6Www==",
+            "dev": true,
+            "requires": {
+                "@typescript-eslint/types": "4.5.0",
+                "@typescript-eslint/visitor-keys": "4.5.0"
+            }
+        },
+        "@typescript-eslint/types": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.5.0.tgz",
+            "integrity": "sha512-n2uQoXnyWNk0Les9MtF0gCK3JiWd987JQi97dMSxBOzVoLZXCNtxFckVqt1h8xuI1ix01t+iMY4h4rFMj/303g==",
+            "dev": true
+        },
+        "@typescript-eslint/typescript-estree": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.5.0.tgz",
+            "integrity": "sha512-gN1mffq3zwRAjlYWzb5DanarOPdajQwx5MEWkWCk0XvqC8JpafDTeioDoow2L4CA/RkYZu7xEsGZRhqrTsAG8w==",
+            "dev": true,
+            "requires": {
+                "@typescript-eslint/types": "4.5.0",
+                "@typescript-eslint/visitor-keys": "4.5.0",
+                "debug": "^4.1.1",
+                "globby": "^11.0.1",
+                "is-glob": "^4.0.1",
+                "lodash": "^4.17.15",
+                "semver": "^7.3.2",
+                "tsutils": "^3.17.1"
+            }
+        },
+        "@typescript-eslint/visitor-keys": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.5.0.tgz",
+            "integrity": "sha512-UHq4FSa55NDZqscRU//O5ROFhHa9Hqn9KWTEvJGTArtTQp5GKv9Zqf6d/Q3YXXcFv4woyBml7fJQlQ+OuqRcHA==",
+            "dev": true,
+            "requires": {
+                "@typescript-eslint/types": "4.5.0",
+                "eslint-visitor-keys": "^2.0.0"
+            }
+        },
+        "acorn": {
+            "version": "7.4.1",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+            "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+            "dev": true
+        },
+        "acorn-jsx": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+            "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+            "dev": true
+        },
+        "ajv": {
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dev": true,
+            "requires": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            }
+        },
+        "ansi-colors": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+            "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+            "dev": true
+        },
+        "ansi-regex": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+            "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+            "dev": true
+        },
+        "ansi-styles": {
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+            "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+            "dev": true,
+            "requires": {
+                "color-convert": "^1.9.0"
+            }
+        },
+        "argparse": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+            "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+            "dev": true,
+            "requires": {
+                "sprintf-js": "~1.0.2"
+            }
+        },
+        "array-includes": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
+            "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0",
+                "is-string": "^1.0.5"
+            }
+        },
+        "array-union": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+            "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+            "dev": true
+        },
+        "array.prototype.flat": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
+            "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1"
+            }
+        },
+        "astral-regex": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+            "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+            "dev": true
+        },
+        "balanced-match": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+            "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+            "dev": true
+        },
+        "brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dev": true,
+            "requires": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "braces": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+            "dev": true,
+            "requires": {
+                "fill-range": "^7.0.1"
+            }
+        },
+        "builtin-modules": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+            "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+            "dev": true
+        },
+        "callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "dev": true
+        },
+        "chalk": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+            "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^4.1.0",
+                "supports-color": "^7.1.0"
+            },
+            "dependencies": {
+                "ansi-styles": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+                    "dev": true,
+                    "requires": {
+                        "color-convert": "^2.0.1"
+                    }
+                },
+                "color-convert": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+                    "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+                    "dev": true,
+                    "requires": {
+                        "color-name": "~1.1.4"
+                    }
+                },
+                "color-name": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+                    "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+                    "dev": true
+                },
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+                    "dev": true
+                },
+                "supports-color": {
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+                    "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
+                }
+            }
+        },
+        "color-convert": {
+            "version": "1.9.3",
+            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+            "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+            "dev": true,
+            "requires": {
+                "color-name": "1.1.3"
+            }
+        },
+        "color-name": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+            "dev": true
+        },
+        "commander": {
+            "version": "2.20.3",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+            "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+            "dev": true
+        },
+        "comment-parser": {
+            "version": "0.7.6",
+            "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.6.tgz",
+            "integrity": "sha512-GKNxVA7/iuTnAqGADlTWX4tkhzxZKXp5fLJqKTlQLHkE65XDUKutZ3BHaJC5IGcper2tT3QRD1xr4o3jNpgXXg==",
+            "dev": true
+        },
+        "concat-map": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+            "dev": true
+        },
+        "contains-path": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+            "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+            "dev": true
+        },
+        "cross-spawn": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+            "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+            "dev": true,
+            "requires": {
+                "path-key": "^3.1.0",
+                "shebang-command": "^2.0.0",
+                "which": "^2.0.1"
+            }
+        },
+        "debug": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+            "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+            "dev": true,
+            "requires": {
+                "ms": "2.1.2"
+            }
+        },
+        "deep-is": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+            "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+            "dev": true
+        },
+        "define-properties": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+            "dev": true,
+            "requires": {
+                "object-keys": "^1.0.12"
+            }
+        },
+        "diff": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+            "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+            "dev": true
+        },
+        "dir-glob": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+            "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+            "dev": true,
+            "requires": {
+                "path-type": "^4.0.0"
+            }
+        },
+        "doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "requires": {
+                "esutils": "^2.0.2"
+            }
+        },
+        "emoji-regex": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+            "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+            "dev": true
+        },
+        "enquirer": {
+            "version": "2.3.6",
+            "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+            "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+            "dev": true,
+            "requires": {
+                "ansi-colors": "^4.1.1"
+            }
+        },
+        "error-ex": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+            "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+            "dev": true,
+            "requires": {
+                "is-arrayish": "^0.2.1"
+            }
+        },
+        "es-abstract": {
+            "version": "1.17.7",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+            "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+            "dev": true,
+            "requires": {
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1",
+                "is-callable": "^1.2.2",
+                "is-regex": "^1.1.1",
+                "object-inspect": "^1.8.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.1",
+                "string.prototype.trimend": "^1.0.1",
+                "string.prototype.trimstart": "^1.0.1"
+            }
+        },
+        "es-to-primitive": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+            "dev": true,
+            "requires": {
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
+            }
+        },
+        "escape-string-regexp": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+            "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+            "dev": true
+        },
+        "eslint": {
+            "version": "7.11.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.11.0.tgz",
+            "integrity": "sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw==",
+            "dev": true,
+            "requires": {
+                "@babel/code-frame": "^7.0.0",
+                "@eslint/eslintrc": "^0.1.3",
+                "ajv": "^6.10.0",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
+                "debug": "^4.0.1",
+                "doctrine": "^3.0.0",
+                "enquirer": "^2.3.5",
+                "eslint-scope": "^5.1.1",
+                "eslint-utils": "^2.1.0",
+                "eslint-visitor-keys": "^2.0.0",
+                "espree": "^7.3.0",
+                "esquery": "^1.2.0",
+                "esutils": "^2.0.2",
+                "file-entry-cache": "^5.0.1",
+                "functional-red-black-tree": "^1.0.1",
+                "glob-parent": "^5.0.0",
+                "globals": "^12.1.0",
+                "ignore": "^4.0.6",
+                "import-fresh": "^3.0.0",
+                "imurmurhash": "^0.1.4",
+                "is-glob": "^4.0.0",
+                "js-yaml": "^3.13.1",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.4.1",
+                "lodash": "^4.17.19",
+                "minimatch": "^3.0.4",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.9.1",
+                "progress": "^2.0.0",
+                "regexpp": "^3.1.0",
+                "semver": "^7.2.1",
+                "strip-ansi": "^6.0.0",
+                "strip-json-comments": "^3.1.0",
+                "table": "^5.2.3",
+                "text-table": "^0.2.0",
+                "v8-compile-cache": "^2.0.3"
+            },
+            "dependencies": {
+                "ignore": {
+                    "version": "4.0.6",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+                    "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-import-resolver-node": {
+            "version": "0.3.4",
+            "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+            "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+            "dev": true,
+            "requires": {
+                "debug": "^2.6.9",
+                "resolve": "^1.13.1"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "2.6.9",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-module-utils": {
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
+            "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+            "dev": true,
+            "requires": {
+                "debug": "^2.6.9",
+                "pkg-dir": "^2.0.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "2.6.9",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-import": {
+            "version": "2.22.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
+            "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.1",
+                "array.prototype.flat": "^1.2.3",
+                "contains-path": "^0.1.0",
+                "debug": "^2.6.9",
+                "doctrine": "1.5.0",
+                "eslint-import-resolver-node": "^0.3.4",
+                "eslint-module-utils": "^2.6.0",
+                "has": "^1.0.3",
+                "minimatch": "^3.0.4",
+                "object.values": "^1.1.1",
+                "read-pkg-up": "^2.0.0",
+                "resolve": "^1.17.0",
+                "tsconfig-paths": "^3.9.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "2.6.9",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "doctrine": {
+                    "version": "1.5.0",
+                    "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+                    "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+                    "dev": true,
+                    "requires": {
+                        "esutils": "^2.0.2",
+                        "isarray": "^1.0.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-jsdoc": {
+            "version": "30.7.3",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.7.3.tgz",
+            "integrity": "sha512-slzJeKrm3vXabRYcFBpbUgWgdnATN9rNOFAr7IhmkxmR9Vk/6qN1c/IdvTqVSEctDRQ08chR+Lvk8fRayXgYZg==",
+            "dev": true,
+            "requires": {
+                "comment-parser": "^0.7.6",
+                "debug": "^4.2.0",
+                "jsdoctypeparser": "^9.0.0",
+                "lodash": "^4.17.20",
+                "regextras": "^0.7.1",
+                "semver": "^7.3.2",
+                "spdx-expression-parse": "^3.0.1"
+            }
+        },
+        "eslint-scope": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+            "dev": true,
+            "requires": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^4.1.1"
+            }
+        },
+        "eslint-utils": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+            "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+            "dev": true,
+            "requires": {
+                "eslint-visitor-keys": "^1.1.0"
+            },
+            "dependencies": {
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-visitor-keys": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
+            "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
+            "dev": true
+        },
+        "espree": {
+            "version": "7.3.0",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
+            "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
+            "dev": true,
+            "requires": {
+                "acorn": "^7.4.0",
+                "acorn-jsx": "^5.2.0",
+                "eslint-visitor-keys": "^1.3.0"
+            },
+            "dependencies": {
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                }
+            }
+        },
+        "esprima": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+            "dev": true
+        },
+        "esquery": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+            "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^5.1.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+                    "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+                    "dev": true
+                }
+            }
+        },
+        "esrecurse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^5.2.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+                    "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+                    "dev": true
+                }
+            }
+        },
+        "estraverse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+            "dev": true
+        },
+        "esutils": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+            "dev": true
+        },
+        "fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+            "dev": true
+        },
+        "fast-glob": {
+            "version": "3.2.4",
+            "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
+            "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
+            "dev": true,
+            "requires": {
+                "@nodelib/fs.stat": "^2.0.2",
+                "@nodelib/fs.walk": "^1.2.3",
+                "glob-parent": "^5.1.0",
+                "merge2": "^1.3.0",
+                "micromatch": "^4.0.2",
+                "picomatch": "^2.2.1"
+            }
+        },
+        "fast-json-stable-stringify": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+            "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+            "dev": true
+        },
+        "fast-levenshtein": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+            "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+            "dev": true
+        },
+        "fastq": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
+            "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
+            "dev": true,
+            "requires": {
+                "reusify": "^1.0.4"
+            }
+        },
+        "file-entry-cache": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+            "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+            "dev": true,
+            "requires": {
+                "flat-cache": "^2.0.1"
+            }
+        },
+        "fill-range": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+            "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+            "dev": true,
+            "requires": {
+                "to-regex-range": "^5.0.1"
+            }
+        },
+        "find-up": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+            "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+            "dev": true,
+            "requires": {
+                "locate-path": "^2.0.0"
+            }
+        },
+        "flat-cache": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+            "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+            "dev": true,
+            "requires": {
+                "flatted": "^2.0.0",
+                "rimraf": "2.6.3",
+                "write": "1.0.3"
+            }
+        },
+        "flatted": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+            "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+            "dev": true
+        },
+        "fs.realpath": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+            "dev": true
+        },
+        "function-bind": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+            "dev": true
+        },
+        "functional-red-black-tree": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+            "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+            "dev": true
+        },
+        "glob": {
+            "version": "7.1.6",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+            "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+            "dev": true,
+            "requires": {
+                "fs.realpath": "^1.0.0",
+                "inflight": "^1.0.4",
+                "inherits": "2",
+                "minimatch": "^3.0.4",
+                "once": "^1.3.0",
+                "path-is-absolute": "^1.0.0"
+            }
+        },
+        "glob-parent": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+            "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+            "dev": true,
+            "requires": {
+                "is-glob": "^4.0.1"
+            }
+        },
+        "globals": {
+            "version": "12.4.0",
+            "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+            "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+            "dev": true,
+            "requires": {
+                "type-fest": "^0.8.1"
+            }
+        },
+        "globby": {
+            "version": "11.0.1",
+            "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
+            "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
+            "dev": true,
+            "requires": {
+                "array-union": "^2.1.0",
+                "dir-glob": "^3.0.1",
+                "fast-glob": "^3.1.1",
+                "ignore": "^5.1.4",
+                "merge2": "^1.3.0",
+                "slash": "^3.0.0"
+            }
+        },
+        "graceful-fs": {
+            "version": "4.2.4",
+            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+            "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+            "dev": true
+        },
+        "has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+            "dev": true,
+            "requires": {
+                "function-bind": "^1.1.1"
+            }
+        },
+        "has-flag": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+            "dev": true
+        },
+        "has-symbols": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+            "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+            "dev": true
+        },
+        "hosted-git-info": {
+            "version": "2.8.8",
+            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+            "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+            "dev": true
+        },
+        "ignore": {
+            "version": "5.1.8",
+            "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+            "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+            "dev": true
+        },
+        "import-fresh": {
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+            "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+            "dev": true,
+            "requires": {
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
+            }
+        },
+        "imurmurhash": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+            "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+            "dev": true
+        },
+        "inflight": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+            "dev": true,
+            "requires": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
+        "inherits": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+            "dev": true
+        },
+        "is-arrayish": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+            "dev": true
+        },
+        "is-callable": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+            "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
+            "dev": true
+        },
+        "is-core-module": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz",
+            "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==",
+            "dev": true,
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
+        "is-date-object": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+            "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+            "dev": true
+        },
+        "is-extglob": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+            "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+            "dev": true
+        },
+        "is-fullwidth-code-point": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+            "dev": true
+        },
+        "is-glob": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+            "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+            "dev": true,
+            "requires": {
+                "is-extglob": "^2.1.1"
+            }
+        },
+        "is-negative-zero": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+            "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+            "dev": true
+        },
+        "is-number": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+            "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+            "dev": true
+        },
+        "is-regex": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+            "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+            "dev": true,
+            "requires": {
+                "has-symbols": "^1.0.1"
+            }
+        },
+        "is-string": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+            "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+            "dev": true
+        },
+        "is-symbol": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+            "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+            "dev": true,
+            "requires": {
+                "has-symbols": "^1.0.1"
+            }
+        },
+        "isarray": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+            "dev": true
+        },
+        "isexe": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+            "dev": true
+        },
+        "js-tokens": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+            "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+            "dev": true
+        },
+        "js-yaml": {
+            "version": "3.14.0",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+            "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+            "dev": true,
+            "requires": {
+                "argparse": "^1.0.7",
+                "esprima": "^4.0.0"
+            }
+        },
+        "jsdoctypeparser": {
+            "version": "9.0.0",
+            "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz",
+            "integrity": "sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw==",
+            "dev": true
+        },
+        "json-schema-traverse": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+            "dev": true
+        },
+        "json-stable-stringify-without-jsonify": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+            "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+            "dev": true
+        },
+        "json5": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+            "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+            "dev": true,
+            "requires": {
+                "minimist": "^1.2.0"
+            }
+        },
+        "levn": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+            "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+            "dev": true,
+            "requires": {
+                "prelude-ls": "^1.2.1",
+                "type-check": "~0.4.0"
+            }
+        },
+        "load-json-file": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+            "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+            "dev": true,
+            "requires": {
+                "graceful-fs": "^4.1.2",
+                "parse-json": "^2.2.0",
+                "pify": "^2.0.0",
+                "strip-bom": "^3.0.0"
+            }
+        },
+        "locate-path": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+            "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+            "dev": true,
+            "requires": {
+                "p-locate": "^2.0.0",
+                "path-exists": "^3.0.0"
+            }
+        },
+        "lodash": {
+            "version": "4.17.20",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+            "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+            "dev": true
+        },
+        "merge2": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+            "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+            "dev": true
+        },
+        "micromatch": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+            "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+            "dev": true,
+            "requires": {
+                "braces": "^3.0.1",
+                "picomatch": "^2.0.5"
+            }
+        },
+        "minimatch": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+            "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+            "dev": true,
+            "requires": {
+                "brace-expansion": "^1.1.7"
+            }
+        },
+        "minimist": {
+            "version": "1.2.5",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+            "dev": true
+        },
+        "mkdirp": {
+            "version": "0.5.5",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+            "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+            "dev": true,
+            "requires": {
+                "minimist": "^1.2.5"
+            }
+        },
+        "ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+            "dev": true
+        },
+        "natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+            "dev": true
+        },
+        "normalize-package-data": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+            "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+            "dev": true,
+            "requires": {
+                "hosted-git-info": "^2.1.4",
+                "resolve": "^1.10.0",
+                "semver": "2 || 3 || 4 || 5",
+                "validate-npm-package-license": "^3.0.1"
+            },
+            "dependencies": {
+                "semver": {
+                    "version": "5.7.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                    "dev": true
+                }
+            }
+        },
+        "object-inspect": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+            "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+            "dev": true
+        },
+        "object-keys": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+            "dev": true
+        },
+        "object.assign": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+            "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.18.0-next.0",
+                "has-symbols": "^1.0.1",
+                "object-keys": "^1.1.1"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.18.0-next.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+                    "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+                    "dev": true,
+                    "requires": {
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.1",
+                        "is-callable": "^1.2.2",
+                        "is-negative-zero": "^2.0.0",
+                        "is-regex": "^1.1.1",
+                        "object-inspect": "^1.8.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.1",
+                        "string.prototype.trimend": "^1.0.1",
+                        "string.prototype.trimstart": "^1.0.1"
+                    }
+                }
+            }
+        },
+        "object.values": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
+            "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3"
+            }
+        },
+        "once": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+            "dev": true,
+            "requires": {
+                "wrappy": "1"
+            }
+        },
+        "optionator": {
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+            "dev": true,
+            "requires": {
+                "deep-is": "^0.1.3",
+                "fast-levenshtein": "^2.0.6",
+                "levn": "^0.4.1",
+                "prelude-ls": "^1.2.1",
+                "type-check": "^0.4.0",
+                "word-wrap": "^1.2.3"
+            }
+        },
+        "p-limit": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+            "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+            "dev": true,
+            "requires": {
+                "p-try": "^1.0.0"
+            }
+        },
+        "p-locate": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+            "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+            "dev": true,
+            "requires": {
+                "p-limit": "^1.1.0"
+            }
+        },
+        "p-try": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+            "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+            "dev": true
+        },
+        "parent-module": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+            "dev": true,
+            "requires": {
+                "callsites": "^3.0.0"
+            }
+        },
+        "parse-json": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+            "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+            "dev": true,
+            "requires": {
+                "error-ex": "^1.2.0"
+            }
+        },
+        "path-exists": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+            "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+            "dev": true
+        },
+        "path-is-absolute": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+            "dev": true
+        },
+        "path-key": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+            "dev": true
+        },
+        "path-parse": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+            "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+            "dev": true
+        },
+        "path-type": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+            "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+            "dev": true
+        },
+        "picomatch": {
+            "version": "2.2.2",
+            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+            "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+            "dev": true
+        },
+        "pify": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+            "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+            "dev": true
+        },
+        "pkg-dir": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+            "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+            "dev": true,
+            "requires": {
+                "find-up": "^2.1.0"
+            }
+        },
+        "prelude-ls": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+            "dev": true
+        },
+        "progress": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+            "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+            "dev": true
+        },
+        "punycode": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+            "dev": true
+        },
+        "read-pkg": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+            "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+            "dev": true,
+            "requires": {
+                "load-json-file": "^2.0.0",
+                "normalize-package-data": "^2.3.2",
+                "path-type": "^2.0.0"
+            },
+            "dependencies": {
+                "path-type": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+                    "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+                    "dev": true,
+                    "requires": {
+                        "pify": "^2.0.0"
+                    }
+                }
+            }
+        },
+        "read-pkg-up": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+            "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+            "dev": true,
+            "requires": {
+                "find-up": "^2.0.0",
+                "read-pkg": "^2.0.0"
+            }
+        },
+        "regexpp": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+            "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+            "dev": true
+        },
+        "regextras": {
+            "version": "0.7.1",
+            "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.1.tgz",
+            "integrity": "sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w==",
+            "dev": true
+        },
+        "resolve": {
+            "version": "1.18.1",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
+            "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
+            "dev": true,
+            "requires": {
+                "is-core-module": "^2.0.0",
+                "path-parse": "^1.0.6"
+            }
+        },
+        "resolve-from": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+            "dev": true
+        },
+        "reusify": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+            "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+            "dev": true
+        },
+        "rimraf": {
+            "version": "2.6.3",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+            "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+            "dev": true,
+            "requires": {
+                "glob": "^7.1.3"
+            }
+        },
+        "run-parallel": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
+            "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
+            "dev": true
+        },
+        "semver": {
+            "version": "7.3.2",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+            "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+            "dev": true
+        },
+        "shebang-command": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+            "dev": true,
+            "requires": {
+                "shebang-regex": "^3.0.0"
+            }
+        },
+        "shebang-regex": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+            "dev": true
+        },
+        "slash": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+            "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+            "dev": true
+        },
+        "slice-ansi": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+            "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^3.2.0",
+                "astral-regex": "^1.0.0",
+                "is-fullwidth-code-point": "^2.0.0"
+            }
+        },
+        "spdx-correct": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+            "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+            "dev": true,
+            "requires": {
+                "spdx-expression-parse": "^3.0.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "spdx-exceptions": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+            "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+            "dev": true
+        },
+        "spdx-expression-parse": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+            "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+            "dev": true,
+            "requires": {
+                "spdx-exceptions": "^2.1.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "spdx-license-ids": {
+            "version": "3.0.6",
+            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
+            "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
+            "dev": true
+        },
+        "sprintf-js": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+            "dev": true
+        },
+        "string-width": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+            "dev": true,
+            "requires": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                }
+            }
+        },
+        "string.prototype.trimend": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz",
+            "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.18.0-next.1"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.18.0-next.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+                    "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+                    "dev": true,
+                    "requires": {
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.1",
+                        "is-callable": "^1.2.2",
+                        "is-negative-zero": "^2.0.0",
+                        "is-regex": "^1.1.1",
+                        "object-inspect": "^1.8.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.1",
+                        "string.prototype.trimend": "^1.0.1",
+                        "string.prototype.trimstart": "^1.0.1"
+                    }
+                }
+            }
+        },
+        "string.prototype.trimstart": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz",
+            "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.18.0-next.1"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.18.0-next.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+                    "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+                    "dev": true,
+                    "requires": {
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.1",
+                        "is-callable": "^1.2.2",
+                        "is-negative-zero": "^2.0.0",
+                        "is-regex": "^1.1.1",
+                        "object-inspect": "^1.8.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.1",
+                        "string.prototype.trimend": "^1.0.1",
+                        "string.prototype.trimstart": "^1.0.1"
+                    }
+                }
+            }
+        },
+        "strip-ansi": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+            "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+            "dev": true,
+            "requires": {
+                "ansi-regex": "^5.0.0"
+            }
+        },
+        "strip-bom": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+            "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+            "dev": true
+        },
+        "strip-json-comments": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+            "dev": true
+        },
+        "supports-color": {
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+            "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+            "dev": true,
+            "requires": {
+                "has-flag": "^3.0.0"
+            }
+        },
+        "table": {
+            "version": "5.4.6",
+            "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+            "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.10.2",
+                "lodash": "^4.17.14",
+                "slice-ansi": "^2.1.0",
+                "string-width": "^3.0.0"
+            }
+        },
+        "text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+            "dev": true
+        },
+        "to-regex-range": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+            "dev": true,
+            "requires": {
+                "is-number": "^7.0.0"
+            }
+        },
+        "tsconfig-paths": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+            "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+            "dev": true,
+            "requires": {
+                "@types/json5": "^0.0.29",
+                "json5": "^1.0.1",
+                "minimist": "^1.2.0",
+                "strip-bom": "^3.0.0"
+            }
+        },
+        "tslib": {
+            "version": "1.14.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+            "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+            "dev": true
+        },
+        "tslint": {
+            "version": "6.1.3",
+            "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+            "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
+            "dev": true,
+            "requires": {
+                "@babel/code-frame": "^7.0.0",
+                "builtin-modules": "^1.1.1",
+                "chalk": "^2.3.0",
+                "commander": "^2.12.1",
+                "diff": "^4.0.1",
+                "glob": "^7.1.1",
+                "js-yaml": "^3.13.1",
+                "minimatch": "^3.0.4",
+                "mkdirp": "^0.5.3",
+                "resolve": "^1.3.2",
+                "semver": "^5.3.0",
+                "tslib": "^1.13.0",
+                "tsutils": "^2.29.0"
+            },
+            "dependencies": {
+                "chalk": {
+                    "version": "2.4.2",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+                    "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^3.2.1",
+                        "escape-string-regexp": "^1.0.5",
+                        "supports-color": "^5.3.0"
+                    }
+                },
+                "semver": {
+                    "version": "5.7.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                    "dev": true
+                },
+                "tsutils": {
+                    "version": "2.29.0",
+                    "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+                    "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+                    "dev": true,
+                    "requires": {
+                        "tslib": "^1.8.1"
+                    }
+                }
+            }
+        },
+        "tsutils": {
+            "version": "3.17.1",
+            "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+            "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+            "dev": true,
+            "requires": {
+                "tslib": "^1.8.1"
+            }
+        },
+        "type-check": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+            "dev": true,
+            "requires": {
+                "prelude-ls": "^1.2.1"
+            }
+        },
+        "type-fest": {
+            "version": "0.8.1",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+            "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+            "dev": true
+        },
+        "typescript": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
+            "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
+            "dev": true
+        },
+        "uri-js": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+            "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+            "dev": true,
+            "requires": {
+                "punycode": "^2.1.0"
+            }
+        },
+        "v8-compile-cache": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
+            "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
+            "dev": true
+        },
+        "validate-npm-package-license": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+            "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+            "dev": true,
+            "requires": {
+                "spdx-correct": "^3.0.0",
+                "spdx-expression-parse": "^3.0.0"
+            }
+        },
+        "which": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+            "dev": true,
+            "requires": {
+                "isexe": "^2.0.0"
+            }
+        },
+        "word-wrap": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+            "dev": true
+        },
+        "wrappy": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+            "dev": true
+        },
+        "write": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+            "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+            "dev": true,
+            "requires": {
+                "mkdirp": "^0.5.1"
+            }
+        }
+    }
+}
diff --git a/.github/actions/to-integration-tests/package.json b/.github/actions/to-integration-tests/package.json
new file mode 100644
index 0000000..bc27ff1
--- /dev/null
+++ b/.github/actions/to-integration-tests/package.json
@@ -0,0 +1,24 @@
+{
+	"author": "Apache Traffic Control",
+	"description": "Runs Traffic Ops Go client/API integration tests",
+	"license": "Apache-2.0",
+	"main": "dist/to-integration-tests.js",
+	"name": "to-integration-tests",
+	"version": "0.0.0",
+	"scripts": {
+		"build": "tsc",
+		"lint": "eslint to-integration-tests.ts",
+		"all": "npm run lint && npm run build"
+	},
+	"devDependencies": {
+		"@types/node": "^14.14.0",
+		"@typescript-eslint/eslint-plugin": "^4.5.0",
+		"@typescript-eslint/eslint-plugin-tslint": "^4.6.0",
+		"@typescript-eslint/parser": "^4.5.0",
+		"eslint": "^7.11.0",
+		"eslint-plugin-import": "^2.22.1",
+		"eslint-plugin-jsdoc": "^30.7.3",
+		"tslint": "^6.1.3",
+		"typescript": "^4.0.3"
+	}
+}
diff --git a/.github/actions/to-integration-tests/riak.json b/.github/actions/to-integration-tests/riak.json
new file mode 100644
index 0000000..b13cd05
--- /dev/null
+++ b/.github/actions/to-integration-tests/riak.json
@@ -0,0 +1,8 @@
+{
+	"user": "${TV_RIAK_USER}",
+	"password": "${TV_RIAK_PASSWORD}",
+	"MaxTLSVersion": "1.1",
+	"tlsConfig": {
+		"insecureSkipVerify": true
+	}
+}
diff --git a/.github/actions/to-integration-tests/to-integration-tests.ts b/.github/actions/to-integration-tests/to-integration-tests.ts
new file mode 100644
index 0000000..d0076e0
--- /dev/null
+++ b/.github/actions/to-integration-tests/to-integration-tests.ts
@@ -0,0 +1,31 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+"use strict";
+import child_process, { SpawnSyncOptions, SpawnSyncReturns } from "child_process";
+import path from "path";
+
+const spawnOptions: SpawnSyncOptions = {stdio: "inherit"};
+
+function runProcess(...commandArguments: string[]): void {
+	console.info(...commandArguments);
+	const {status}: SpawnSyncReturns<Buffer> = child_process.spawnSync(commandArguments[0], commandArguments.slice(1), spawnOptions);
+	if (status === 0) {
+		return;
+	}
+	console.error("Child process \"", ...commandArguments, "\" exited with status code", status, "!");
+	process.exit(status ?? 1);
+}
+
+runProcess(path.join(__dirname, "../entrypoint.sh"));
diff --git a/.github/actions/to-integration-tests/traffic-ops-test.json b/.github/actions/to-integration-tests/traffic-ops-test.json
index 78c96ea..1c7298e 100644
--- a/.github/actions/to-integration-tests/traffic-ops-test.json
+++ b/.github/actions/to-integration-tests/traffic-ops-test.json
@@ -9,10 +9,11 @@
 		},
 		"session": {
 			"timeoutInSecs": 60
-		}
+		},
+		"includeSystemTests": true
 	},
 	"trafficOps": {
-		"URL": "https://localhost:6443",
+		"URL": "https://127.0.0.1:6443",
 		"password": "twelve",
 		"users": {
 			"disallowed": "disallowed",
@@ -27,7 +28,7 @@
 	"trafficOpsDB": {
 		"dbname": "traffic_ops",
 		"description": "Test database to_test",
-		"hostname": "postgres",
+		"hostname": "172.17.0.1",
 		"password": "twelve",
 		"port": "5432",
 		"type": "Pg",
diff --git a/.github/actions/to-integration-tests/tsconfig.json b/.github/actions/to-integration-tests/tsconfig.json
new file mode 100644
index 0000000..78c8c92
--- /dev/null
+++ b/.github/actions/to-integration-tests/tsconfig.json
@@ -0,0 +1,14 @@
+{
+	"compilerOptions": {
+		"target": "es6",                /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
+		"module": "commonjs",           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+		"outDir": "./dist",             /* Redirect output structure to the directory. */
+		"rootDir": ".",                 /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+		"strict": true,                 /* Enable all strict type-checking options. */
+		"noImplicitAny": true,          /* Raise error on expressions and declarations with an implied 'any' type. */
+		"esModuleInterop": true         /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
+	},
+	"exclude": [
+		"node_modules"
+	]
+}
diff --git a/.github/workflows/traffic ops.yml b/.github/workflows/traffic ops.yml
index c568431..5aa60bd 100644
--- a/.github/workflows/traffic ops.yml	
+++ b/.github/workflows/traffic ops.yml	
@@ -49,6 +49,20 @@ jobs:
         - 5432:5432
         options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
 
+      smtp:
+        image: maildev/maildev:2.0.0-beta3
+        ports:
+          - 25:25
+        options: >-
+          --entrypoint=bin/maildev
+          --user=root
+          --health-cmd="sh -c \"[[ \$(wget -qO- http://smtp/healthz) == true ]]\""
+          --
+          maildev/maildev:2.0.0-beta3
+          --smtp=25
+          --hide-extensions=STARTTLS
+          --web=80
+
     steps:
     - name: Checkout
       uses: actions/checkout@master
@@ -64,8 +78,10 @@ jobs:
       uses: ./.github/actions/to-integration-tests
       with:
         version: 2
+        smtp_address: 172.17.0.1
     - name: Run API v3 tests
       if: ${{ steps.todb.outcome == 'success' && always() }}
       uses: ./.github/actions/to-integration-tests
       with:
         version: 3
+        smtp_address: 172.17.0.1
diff --git a/.gitignore b/.gitignore
index 033cdd8..d05407f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,7 @@ traffic_router/*.log
 *.pyc
 bin/docker-compose
 /.m2
+/.github/actions/**/node_modules/
 # local storage for cdn-in-a-box Carton packages
 local.tar.gz
 # Editor/IDE configuration files
diff --git a/traffic_ops/testing/api/v3/serverservercapability_test.go b/traffic_ops/testing/api/v3/serverservercapability_test.go
index c5112c0..06af56d 100644
--- a/traffic_ops/testing/api/v3/serverservercapability_test.go
+++ b/traffic_ops/testing/api/v3/serverservercapability_test.go
@@ -133,10 +133,10 @@ func CreateTestServerServerCapabilities(t *testing.T) {
 
 	// Attempt to assign a server capability to a non MID/EDGE server
 	// TODO: DON'T hard-code server hostnames!
-	params.Set("hostName", "riak")
+	params.Set("hostName", "trafficvault")
 	resp, _, err := TOSession.GetServersWithHdr(&params, nil)
 	if err != nil {
-		t.Fatalf("cannot GET Server by hostname 'riak': %v - %v", err, resp.Alerts)
+		t.Fatalf("cannot GET Server by hostname 'trafficvault': %v - %v", err, resp.Alerts)
 	}
 	servers := resp.Response
 	if len(servers) < 1 {
diff --git a/traffic_ops/testing/api/v3/tc-fixtures.json b/traffic_ops/testing/api/v3/tc-fixtures.json
index 19e31c8..703e250 100644
--- a/traffic_ops/testing/api/v3/tc-fixtures.json
+++ b/traffic_ops/testing/api/v3/tc-fixtures.json
@@ -2618,9 +2618,9 @@
         {
             "cachegroup": "cachegroup1",
             "cdnName": "cdn1",
-            "domainName": "local",
+            "domainName": "infra.ciab.test",
             "guid": null,
-            "hostName": "riak",
+            "hostName": "trafficvault",
             "httpsPort": 8088,
             "iloIpAddress": "",
             "iloIpGateway": "",
@@ -2658,7 +2658,7 @@
             "revalPending": false,
             "routerHostName": "",
             "routerPortName": "",
-            "status": "REPORTED",
+            "status": "ONLINE",
             "tcpPort": 8088,
             "type": "RIAK",
             "updPending": false,


[trafficcontrol] 01/06: Fix 403 forbidden in deliveryservices_test (#5214)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 3999693d289be179ce7b88d7fbd8a8154be455f9
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Wed Oct 28 12:10:30 2020 -0600

    Fix 403 forbidden in deliveryservices_test (#5214)
    
    (cherry picked from commit d7b817fa03e2fc3c4fc88dafe0d77a6f31c0e585)
---
 traffic_ops/testing/api/v3/deliveryservices_test.go | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/traffic_ops/testing/api/v3/deliveryservices_test.go b/traffic_ops/testing/api/v3/deliveryservices_test.go
index caad394..f21c50a 100644
--- a/traffic_ops/testing/api/v3/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v3/deliveryservices_test.go
@@ -259,10 +259,6 @@ func SSLDeliveryServiceCDNUpdateTest(t *testing.T) {
 	ds.CDNID = &newCdn.ID
 	ds.CDNName = &newCdn.Name
 	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	_, _, err = TOSession.UpdateDeliveryServiceV30WithHdr(*ds.ID, ds, nil)
 	if err == nil {
 		t.Fatal("should not be able to update delivery service (cdn) as it has ssl keys")
 	}


[trafficcontrol] 06/06: Fix compilation error in experimental tool (#5220)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit ab198b3974099c83c428fb44f4d43e6573028019
Author: ocket8888 <oc...@apache.org>
AuthorDate: Thu Oct 29 12:41:18 2020 -0600

    Fix compilation error in experimental tool (#5220)
    
    (cherry picked from commit 4a77b62e11dcb9b245e21a7dcd4182c8d4d1fba0)
---
 experimental/traffic_ops_auth/traffic_ops_auth.go | 25 +++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/experimental/traffic_ops_auth/traffic_ops_auth.go b/experimental/traffic_ops_auth/traffic_ops_auth.go
index a31427f..49b8f74 100644
--- a/experimental/traffic_ops_auth/traffic_ops_auth.go
+++ b/experimental/traffic_ops_auth/traffic_ops_auth.go
@@ -26,13 +26,14 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
-	jwt "github.com/dgrijalva/jwt-go"
-	_ "github.com/lib/pq"
 	"io/ioutil"
 	"log"
 	"net/http"
 	"strconv"
 	"strings"
+
+	jwt "github.com/dgrijalva/jwt-go"
+	_ "github.com/lib/pq"
 )
 
 const TrafficOpsDomain = "localhost"
@@ -148,8 +149,13 @@ func getTokenData(jwtSigningKey string, r *http.Request) (*TokenData, error) {
 		return nil, fmt.Errorf("invalid token")
 	}
 
-	userInterface, hasUser := token.Claims["user"]
-	roleInterface, hasRole := token.Claims["role"]
+	clms, ok := token.Claims.(jwt.MapClaims)
+	if !ok {
+		return nil, fmt.Errorf("expected MapClaims in token, got: %T", token.Claims)
+	}
+
+	userInterface, hasUser := clms["user"]
+	roleInterface, hasRole := clms["role"]
 	user, userIsStr := userInterface.(string)
 	role, roleIsStr := roleInterface.(string)
 	if !hasUser || !hasRole || !userIsStr || !roleIsStr {
@@ -182,8 +188,15 @@ func HandleLogin(db *sql.DB, jwtSigningKey string, w http.ResponseWriter, r *htt
 	}
 
 	token := jwt.New(jwt.SigningMethodHS256)
-	token.Claims["user"] = user
-	token.Claims["role"] = role
+
+	clms, ok := token.Claims.(jwt.MapClaims)
+	if !ok {
+		w.WriteHeader(http.StatusInternalServerError)
+		log.Printf("%s ERROR creating token for '%s': expected MapClaims in token, got %T", token.Claims)
+		return
+	}
+	clms["user"] = user
+	clms["role"] = role
 	tokenString, err := token.SignedString([]byte(jwtSigningKey))
 	if err != nil {
 		w.WriteHeader(http.StatusInternalServerError)


[trafficcontrol] 02/06: fixes broken context menus due to missing id (#5215)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 7f251037e1e7a72ea328bd09bbd1b4e5b07763aa
Author: Jeremy Mitchell <mi...@users.noreply.github.com>
AuthorDate: Thu Oct 29 09:33:01 2020 -0600

    fixes broken context menus due to missing id (#5215)
    
    (cherry picked from commit c8262dfcc5535a9e7f54dd03d348234de209c213)
---
 .../modules/table/cacheGroupServers/table.cacheGroupServers.tpl.html    | 2 +-
 .../app/src/common/modules/table/cdnServers/table.cdnServers.tpl.html   | 2 +-
 .../table/deliveryServiceJobs/table.deliveryServiceJobs.tpl.html        | 2 +-
 .../table/deliveryServiceServers/table.deliveryServiceServers.tpl.html  | 2 +-
 .../table/physLocationServers/table.physLocationServers.tpl.html        | 2 +-
 .../common/modules/table/profileServers/table.profileServers.tpl.html   | 2 +-
 .../src/common/modules/table/statusServers/table.statusServers.tpl.html | 2 +-
 .../common/modules/table/topologyServers/table.topologyServers.tpl.html | 2 +-
 .../app/src/common/modules/table/typeServers/table.typeServers.tpl.html | 2 +-
 9 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/traffic_portal/app/src/common/modules/table/cacheGroupServers/table.cacheGroupServers.tpl.html b/traffic_portal/app/src/common/modules/table/cacheGroupServers/table.cacheGroupServers.tpl.html
index d835370..db53bbd 100644
--- a/traffic_portal/app/src/common/modules/table/cacheGroupServers/table.cacheGroupServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/cacheGroupServers/table.cacheGroupServers.tpl.html
@@ -70,7 +70,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/cdnServers/table.cdnServers.tpl.html b/traffic_portal/app/src/common/modules/table/cdnServers/table.cdnServers.tpl.html
index 439791f..7b0a649 100644
--- a/traffic_portal/app/src/common/modules/table/cdnServers/table.cdnServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/cdnServers/table.cdnServers.tpl.html
@@ -70,7 +70,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/deliveryServiceJobs/table.deliveryServiceJobs.tpl.html b/traffic_portal/app/src/common/modules/table/deliveryServiceJobs/table.deliveryServiceJobs.tpl.html
index 64d3330..674f65b 100644
--- a/traffic_portal/app/src/common/modules/table/deliveryServiceJobs/table.deliveryServiceJobs.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/deliveryServiceJobs/table.deliveryServiceJobs.tpl.html
@@ -67,7 +67,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <button type="button" ng-click="confirmRemoveJob(job, $event)">Delete Invalidation Request</button>
diff --git a/traffic_portal/app/src/common/modules/table/deliveryServiceServers/table.deliveryServiceServers.tpl.html b/traffic_portal/app/src/common/modules/table/deliveryServiceServers/table.deliveryServiceServers.tpl.html
index c93a04b..3b83c1d 100644
--- a/traffic_portal/app/src/common/modules/table/deliveryServiceServers/table.deliveryServiceServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/deliveryServiceServers/table.deliveryServiceServers.tpl.html
@@ -69,7 +69,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/physLocationServers/table.physLocationServers.tpl.html b/traffic_portal/app/src/common/modules/table/physLocationServers/table.physLocationServers.tpl.html
index 47cdb41..5cc90c4 100644
--- a/traffic_portal/app/src/common/modules/table/physLocationServers/table.physLocationServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/physLocationServers/table.physLocationServers.tpl.html
@@ -67,7 +67,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/profileServers/table.profileServers.tpl.html b/traffic_portal/app/src/common/modules/table/profileServers/table.profileServers.tpl.html
index d4496da..2974d3b 100644
--- a/traffic_portal/app/src/common/modules/table/profileServers/table.profileServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/profileServers/table.profileServers.tpl.html
@@ -67,7 +67,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/statusServers/table.statusServers.tpl.html b/traffic_portal/app/src/common/modules/table/statusServers/table.statusServers.tpl.html
index ccb72c3..18bedbd 100644
--- a/traffic_portal/app/src/common/modules/table/statusServers/table.statusServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/statusServers/table.statusServers.tpl.html
@@ -68,7 +68,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/topologyServers/table.topologyServers.tpl.html b/traffic_portal/app/src/common/modules/table/topologyServers/table.topologyServers.tpl.html
index 1946a07..e8fa020 100644
--- a/traffic_portal/app/src/common/modules/table/topologyServers/table.topologyServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/topologyServers/table.topologyServers.tpl.html
@@ -68,7 +68,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>
diff --git a/traffic_portal/app/src/common/modules/table/typeServers/table.typeServers.tpl.html b/traffic_portal/app/src/common/modules/table/typeServers/table.typeServers.tpl.html
index 12b3604..2804526 100644
--- a/traffic_portal/app/src/common/modules/table/typeServers/table.typeServers.tpl.html
+++ b/traffic_portal/app/src/common/modules/table/typeServers/table.typeServers.tpl.html
@@ -67,7 +67,7 @@ under the License.
     </div>
 </div>
 
-<menu class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
+<menu id="context-menu" class="dropdown-menu" ng-style="menuStyle" type="contextmenu" ng-show="showMenu">
     <ul>
         <li role="menuitem">
             <a ng-href="#!/servers/{{server.id}}" target="_blank">Open in New Tab</a>