diff --git a/.github/workflows/config-tool.yaml b/.github/workflows/config-tool.yaml new file mode 100644 index 000000000..982960c1f --- /dev/null +++ b/.github/workflows/config-tool.yaml @@ -0,0 +1,63 @@ +name: Config Tool +on: + push: + branches: + - "!dependabot/*" + - "*" + paths: + - config-tool/** + pull_request: + branches: + - "*" + paths: + - config-tool/** +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v4 + with: + go-version: ^1.19 + + - name: Checkout + uses: actions/checkout@v3 + + - name: Build + run: cd ./config-tool && go build -v ./... + + tests: + name: Tests + runs-on: ubuntu-latest + container: docker.io/library/golang:1.19-buster + services: + postgres: + image: postgres:11.5 + env: + POSTGRES_USER: "user" + POSTGRES_PASSWORD: "password" + POSTGRES_DB: "quay" + redis: + image: redis:latest + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: quay + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 + jwt: + image: quay.io/coreos/jwt-auth-example:latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Workaround for dubious ownership issue + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Build + run: cd ./config-tool && go build -v ./... + - name: Tests + run: cd ./config-tool && go test ./pkg/lib/fieldgroups/... + env: + GODEBUG: x509ignoreCN=0 diff --git a/Dockerfile b/Dockerfile index 92840d66e..296bdad2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,11 +29,7 @@ RUN set -ex\ # Config-editor builds the javascript for the configtool. FROM registry.access.redhat.com/ubi8/nodejs-10 AS config-editor WORKDIR /opt/app-root/src -# This argument must be repeated, and should have the same default as -# the other CONFIGTOOL_VERSION argument. -ARG CONFIGTOOL_VERSION=v0.1.21 -RUN curl -fsSL "https://github.com/quay/config-tool/archive/${CONFIGTOOL_VERSION}.tar.gz"\ - | tar xz --strip-components=4 --exclude='*.go' +COPY --chown=1001:0 config-tool/pkg/lib/editor/ ./ RUN set -ex\ ; npm install --quiet --no-progress --ignore-engines \ ; npm run --quiet build\ @@ -137,9 +133,7 @@ RUN set -ex\ # Config-tool builds the go binary in the configtool. FROM registry.access.redhat.com/ubi8/go-toolset as config-tool WORKDIR /opt/app-root/src -ARG CONFIGTOOL_VERSION=v0.1.21 -RUN curl -fsSL "https://github.com/quay/config-tool/archive/${CONFIGTOOL_VERSION}.tar.gz"\ - | tar xz --strip-components=1 --exclude '*/pkg/lib/editor/static/build' +COPY config-tool/ ./ COPY --from=config-editor /opt/app-root/src/static/build /opt/app-root/src/pkg/lib/editor/static/build RUN go install -tags=fips ./cmd/config-tool diff --git a/config-tool/.air.telepresence.toml b/config-tool/.air.telepresence.toml new file mode 100644 index 000000000..bcabdac23 --- /dev/null +++ b/config-tool/.air.telepresence.toml @@ -0,0 +1,42 @@ +root = "." +tmp_dir = "tmp" + +[build] +# Just plain old shell command. You could use `make` as well. +cmd = "cd pkg/lib/editor && npm run build && rm -rf ../../../tmp/build/configapp-quay-editor.bundle.js && mv static/build/configapp-quay-editor.bundle.js ../../../tmp/build/configapp-quay-editor.bundle.js && cd ../../.. && docker run --rm -v /home/jonathan/Desktop/config-tool:/usr/src/app -w /usr/src/app golang:1.15 go build -o ./tmp/config-tool ./cmd/..." +# Binary file yields from `cmd`. +bin = 'tmp/main editor --config-dir=/conf --password=password' +# Watch these filename extensions. +include_ext = ["go", "js", "html", "toml", "json", "yaml"] +# Ignore these filename extensions or directories. +exclude_dir = ["pkg/lib/editor/static/build", "pkg/lib/editor/node_modules"] + +# Watch these directories if you specified. +include_dir = [] +# Exclude files. +exclude_file = [] +# This log file places in your tmp_dir. +log = "air.log" +# It's not necessary to trigger build each time file changes if it's too frequent. +delay = 1000 # ms +# Stop running old binary when build errors occur. +stop_on_error = true +# Send Interrupt signal before killing process (windows does not support this feature) +send_interrupt = false +# Delay after sending Interrupt signal +kill_delay = 500 # ms + +[log] +# Show log time +time = false + +[color] +# Customize each part's color. If no color found, use the raw app log. +main = "magenta" +watcher = "cyan" +build = "yellow" +runner = "green" + +[misc] +# Delete tmp directory on exit +clean_on_exit = true \ No newline at end of file diff --git a/config-tool/.air.toml b/config-tool/.air.toml new file mode 100755 index 000000000..84843052a --- /dev/null +++ b/config-tool/.air.toml @@ -0,0 +1,42 @@ +root = "." +tmp_dir = "tmp" + +[build] +# Just plain old shell command. You could use `make` as well. +cmd = "cd /jssrc && npm run build && rm -rf /go/src/config-tool/pkg/lib/editor/static/build && mv /jssrc/static/build /go/src/config-tool/pkg/lib/editor/static/build && cd /go/src/config-tool && go build -o ./tmp/main ./cmd/..." +# Binary file yields from `cmd`. +bin = 'tmp/main editor --config-dir=/conf --password=password' +# Watch these filename extensions. +include_ext = ["go", "js", "html", "toml", "json", "yaml"] +# Ignore these filename extensions or directories. +exclude_dir = ["pkg/lib/editor/static/build", "pkg/lib/editor/node_modules"] + +# Watch these directories if you specified. +include_dir = [] +# Exclude files. +exclude_file = [] +# This log file places in your tmp_dir. +log = "air.log" +# It's not necessary to trigger build each time file changes if it's too frequent. +delay = 1000 # ms +# Stop running old binary when build errors occur. +stop_on_error = true +# Send Interrupt signal before killing process (windows does not support this feature) +send_interrupt = false +# Delay after sending Interrupt signal +kill_delay = 500 # ms + +[log] +# Show log time +time = false + +[color] +# Customize each part's color. If no color found, use the raw app log. +main = "magenta" +watcher = "cyan" +build = "yellow" +runner = "green" + +[misc] +# Delete tmp directory on exit +clean_on_exit = true \ No newline at end of file diff --git a/config-tool/.dockerignore b/config-tool/.dockerignore new file mode 100644 index 000000000..fc96f965e --- /dev/null +++ b/config-tool/.dockerignore @@ -0,0 +1,5 @@ +node_modules +**/node_modules +.git +Dockerfile +Dockerfile.dev \ No newline at end of file diff --git a/config-tool/.env b/config-tool/.env new file mode 100644 index 000000000..e5dc4b578 --- /dev/null +++ b/config-tool/.env @@ -0,0 +1,8 @@ +# Path to config bundle directory (uses testdata by defaut, you can change this to any path to a config bundle) +CONFIG_MOUNT=${PWD}/testdata/config-bundle-private + +# TLS Config +CT_PUBLIC_KEY=${PWD}/testdata/tls/localhost.crt +CT_PRIVATE_KEY=${PWD}/testdata/tls/localhost.key + +# OPERATOR_ENDPOINT= diff --git a/config-tool/.gitignore b/config-tool/.gitignore new file mode 100644 index 000000000..462d3ff3a --- /dev/null +++ b/config-tool/.gitignore @@ -0,0 +1,28 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +/.old + +/TODO + +playground.go + +node_modules + +build + +/tmp + +config-bundle-private* \ No newline at end of file diff --git a/config-tool/Dockerfile b/config-tool/Dockerfile new file mode 100644 index 000000000..8fbe58cfc --- /dev/null +++ b/config-tool/Dockerfile @@ -0,0 +1,20 @@ +FROM registry.access.redhat.com/ubi8/ubi:latest as jsbuild + +WORKDIR /jssrc +COPY pkg/lib/editor . +RUN yum install -y nodejs && \ + npm install --ignore-engines && \ + npm run build + +FROM golang:1.19-alpine + +RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* && mkdir /usr/local/share/ca-certificates/extra +WORKDIR /go/src/config-tool +COPY . . +RUN rm -rf /go/src/config-tool/pkg/lib/editor/static/build +COPY --from=jsbuild /jssrc/static/build /go/src/config-tool/pkg/lib/editor/static/build + +RUN go get -d -v ./... +RUN go install -v ./... + +ENTRYPOINT [ "config-tool" ] diff --git a/config-tool/Dockerfile.dev b/config-tool/Dockerfile.dev new file mode 100644 index 000000000..ee7be1d27 --- /dev/null +++ b/config-tool/Dockerfile.dev @@ -0,0 +1,28 @@ +FROM registry.access.redhat.com/ubi8/ubi:latest as jsbuild + +ENV GOROOT /usr/local/go +ENV GOPATH /go +ENV PATH $PATH:$GOPATH/bin:$GOROOT/bin + +WORKDIR /jssrc +COPY pkg/lib/editor/package.json . +COPY pkg/lib/editor/tsconfig.json . +COPY pkg/lib/editor/webpack.config.js . +COPY pkg/lib/editor/package-lock.json . +COPY pkg/lib/editor/static . + +RUN yum install -y nodejs && \ + yum install -y git && \ + npm install --ignore-engines + +RUN yum install -y wget && \ + wget https://golang.org/dl/go1.19.linux-amd64.tar.gz && \ + tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz + +WORKDIR /go/src/config-tool +RUN go get github.com/cosmtrek/air +COPY . . +RUN go get -d -v ./... +RUN go install -v ./... + +ENTRYPOINT ["air", "-c", ".air.toml"] diff --git a/config-tool/LICENSE b/config-tool/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/config-tool/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/config-tool/Makefile b/config-tool/Makefile new file mode 100644 index 000000000..7650e498a --- /dev/null +++ b/config-tool/Makefile @@ -0,0 +1,52 @@ +include .env + +PID_FILE=/tmp/go_id.pid + +all: + +generate: + go run ./utils/generate/gen.go + +install: + + go install ./... + +test: + go test ./pkg/lib/fieldgroups/... + +# Used to bring up production container +run-local-prod: + docker build -t config-app:latest -f Dockerfile . && docker run -p 7070:8080 -v ${CONFIG_MOUNT}:/conf -ti config-app:latest editor --config-dir=/conf --password=password --operator-endpoint=${OPERATOR_ENDPOINT} + +# Used to bring up dev container +build-local-dev: + docker build -t config-app:dev -f Dockerfile.dev . + +run-local-dev: + docker run -p 7070:8080 \ + -v ${PWD}/pkg/lib/editor/js:/jssrc/js \ + -v ${PWD}/pkg/lib/editor/editor.go:/jssrc/editor.go \ + -v ${PWD}/:/go/src/config-tool \ + -v ${CONFIG_MOUNT}:/conf \ + -v ${CT_PRIVATE_KEY}:/tls/localhost.key \ + -v ${CT_PUBLIC_KEY}:/tls/localhost.crt \ + -e CONFIG_TOOL_PRIVATE_KEY=/tls/localhost.key \ + -e CONFIG_TOOL_PUBLIC_KEY=/tls/localhost.crt \ + -e DEBUGLOG=true \ + -ti config-app:dev + +run-local-dev-setup: + docker run -p 7070:8080 \ + -v ${PWD}/pkg/lib/editor/js:/jssrc/js \ + -v ${PWD}/pkg/lib/editor/editor.go:/jssrc/editor.go \ + -v ${PWD}/:/go/src/config-tool \ + -v ${CT_PRIVATE_KEY}:/tls/localhost.key \ + -v ${CT_PUBLIC_KEY}:/tls/localhost.crt \ + -e CONFIG_TOOL_PRIVATE_KEY=/tls/localhost.key \ + -e CONFIG_TOOL_PUBLIC_KEY=/tls/localhost.crt \ + -e DEBUGLOG=true \ + -ti config-app:dev + + +swagger: + swag init -g pkg/lib/editor/editor.go \ No newline at end of file diff --git a/config-tool/README.md b/config-tool/README.md new file mode 100644 index 000000000..74d0e73af --- /dev/null +++ b/config-tool/README.md @@ -0,0 +1,124 @@ +# Config Tool + +The Quay Config Tool implements several features to capture and validate configuration data based on a predefined schema. + +This tool includes the following features: + +- Validate Quay configuration using CLI tool +- Generate code for custom field group definitions (includes structs, constructors, defaults) +- Validation tag support from [Validator](https://github.com/go-playground/validator) +- Built-in validator tags for OAuth and JWT structs + +## Installation + +### Build from Source + +Install using the Go tool: + +``` +go get -u github.com/quay/quay/config-tool/... +``` + +This will generate files for the Quay validator executable and install the `config-tool` CLI tool. + +### Build from Dockerfile + +Clone this repo and build an image: + +``` +$ git clone https://github.com/quay/quay.git +$ cd quay/config-tool +$ sudo podman build -t config-tool . +``` + +Start the container and execute command: + +``` +$ sudo podman run -it -v ${CONFIG_MOUNT}:/conf config-tool ... +``` + +Note that you must mount in your config directory in order for the config-tool to see it. + +#### Note: By default, this tool will generate an executable from a pre-built Config definition. For usage on writing a custom Config definition see [here](https://github.com/quay/quay/tree/master/config-tool/utils/generate) + +## Usage + +The CLI tool contains two main commands: + +#### The `print` command is used to output the entire configuration with defaults specified + +``` +{ + "HostSettings": (*fieldgroups.HostSettingsFieldGroup)({ + ServerHostname: "quay:8081", + PreferredURLScheme: "https", + ExternalTLSTermination: false + }), + "TagExpiration": (*fieldgroups.TagExpirationFieldGroup)({ + FeatureChangeTagExpiration: false, + DefaultTagExpiration: "2w", + TagExpirationOptions: { + "0s", + "1d", + "1w", + "2w", + "4w" + } + }), + "UserVisibleSettings": (*fieldgroups.UserVisibleSettingsFieldGroup)({ + RegistryTitle: "Project Quay", + RegistryTitleShort: "Project Quay", + SearchResultsPerPage: 10, + SearchMaxResultPageCount: 10, + ContactInfo: { + }, + AvatarKind: "local", + Branding: (*fieldgroups.BrandingStruct)({ + Logo: "not_a_url", + FooterIMG: "also_not_a_url", + FooterURL: "" + }) + }) +} +``` + +#### The `validate` command is used to show while field groups have been validated succesully + +``` +$ config-tool validate -c ++---------------------+--------------------+-------------------------+--------+ +| FIELD GROUP | FIELD | ERROR | STATUS | ++---------------------+--------------------+-------------------------+--------+ +| HostSettings | - | - | 🟢 | +| TagExpiration | - | - | 🟢 | +| UserVisibleSettings | BRANDING.Logo | Field enforces tag: url | 🔴 | +| | BRANDING.FooterIMG | Field enforces tag: url | 🔴 | ++---------------------+--------------------+-------------------------+--------+ +``` + +#### The `editor` command will bring up an interactive UI to reconfigure and validate a config bundle. + +``` +$ config-tool editor -c -p -e +``` + +This command will bring up an interactive UI in which a user can modify, validate, and download a config. In addition, Swagger documentation can be reached by going to `{{host}}/swagger/index.html` + +### Using HTTPS + +You can deploy the config editor using TLS certificates by passing environment variables to the runtime. The public and private keys must contain valid SANs for the route that you wish to deploy the editor on. + +The paths can be specifed using `CONFIG_TOOL_PRIVATE_KEY` and `CONFIG_TOOL_PUBLIC_KEY`. + +NOTE: If running from a container, the `CONFIG_TOOL_PRIVATE_KEY` and `CONFIG_TOOL_PUBLIC_KEY` values are the locations of the certs INSIDE the container. This might look something like the following: + +``` +$ docker run -p 7070:8080 \ + +-v ${PRIVATE_KEY_PATH}:/tls/localhost.key \ +-v ${PUBLIC_KEY_PATH}:/tls/localhost.crt \ +-e CONFIG_TOOL_PRIVATE_KEY=/tls/localhost.key \ +-e CONFIG_TOOL_PUBLIC_KEY=/tls/localhost.crt \ +-e DEBUGLOG=true \ +-ti config-app:dev +``` diff --git a/config-tool/cmd/config-tool/main.go b/config-tool/cmd/config-tool/main.go new file mode 100644 index 000000000..462504d79 --- /dev/null +++ b/config-tool/cmd/config-tool/main.go @@ -0,0 +1,22 @@ +/* +Copyright © 2020 NAME HERE + +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. +*/ +package main + +import "github.com/quay/quay/config-tool/commands" + +func main() { + commands.Execute() +} diff --git a/config-tool/commands/editor.go b/config-tool/commands/editor.go new file mode 100644 index 000000000..13098cd46 --- /dev/null +++ b/config-tool/commands/editor.go @@ -0,0 +1,65 @@ +/* +Copyright © 2020 NAME HERE + +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. +*/ +package commands + +import ( + "os" + "strings" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/quay/quay/config-tool/pkg/lib/editor" +) + +var editorPassword string +var operatorEndpoint string +var readonlyFieldGroups string + +// editorCmd represents the validate command +var editorCmd = &cobra.Command{ + Use: "editor", + Short: "Runs a browser-based editor for your config.yaml", + + Run: func(cmd *cobra.Command, args []string) { + + log.SetOutput(os.Stdout) + + // Only log the warning severity or above. + log.SetLevel(log.DebugLevel) + + editor.RunConfigEditor(editorPassword, configDir, operatorEndpoint, strings.Split(readonlyFieldGroups, ",")) + }, +} + +func init() { + // Add editor command + rootCmd.AddCommand(editorCmd) + + // Add --config-dir flag + editorCmd.Flags().StringVarP(&configDir, "config-dir", "c", "", "The directory containing your config files") + editorCmd.MarkFlagRequired("config-dir") + + // Add --password flag + editorCmd.Flags().StringVarP(&editorPassword, "password", "p", "", "The password to enter the editor") + editorCmd.MarkFlagRequired("password") + + // Add --operator-endpoint flag + editorCmd.Flags().StringVarP(&operatorEndpoint, "operator-endpoint", "e", "", "The endpoint to commit a validated config bundle to") + + // Add --readonly-fieldgroups flag + editorCmd.Flags().StringVarP(&readonlyFieldGroups, "readonly-fieldgroups", "r", "", "Comma-separated list of fieldgroups that should be treated as read-only") +} diff --git a/config-tool/commands/print.go b/config-tool/commands/print.go new file mode 100644 index 000000000..2e70317a6 --- /dev/null +++ b/config-tool/commands/print.go @@ -0,0 +1,81 @@ +/* +Copyright © 2020 NAME HERE + +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. +*/ +package commands + +import ( + "fmt" + "io/ioutil" + "path" + + "github.com/jojomi/go-spew/spew" + "github.com/quay/quay/config-tool/pkg/lib/config" + "github.com/quay/quay/config-tool/pkg/lib/shared" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" +) + +// validateCmd represents the validate command +var printCmd = &cobra.Command{ + Use: "print", + Short: "Print your config.yaml to show all values", + + Run: func(cmd *cobra.Command, args []string) { + + // Read config file + configFilePath := path.Join(configDir, "config.yaml") + configBytes, err := ioutil.ReadFile(configFilePath) + if err != nil { + fmt.Println(err.Error()) + return + } + + // Load config into struct + var conf map[string]interface{} + if err = yaml.Unmarshal(configBytes, &conf); err != nil { + fmt.Println(err.Error()) + return + } + + // Clean config + conf = shared.FixNumbers(conf) + conf = shared.RemoveNullValues(conf) + + configFieldGroups, err := config.NewConfig(conf) + if err != nil { + fmt.Println(err.Error()) + return + } + + spew.Config.Indent = "\t" + spew.Config.DisableCapacities = true + spew.Config.DisablePointerAddresses = true + spew.Config.SortKeys = true + spew.Config.DisableMethods = true + spew.Dump(configFieldGroups) + + }, +} + +func init() { + + // Add validation command + rootCmd.AddCommand(printCmd) + + // Add --config flag + printCmd.Flags().StringVarP(&configDir, "configDir", "c", "", "The directory containing your config files") + printCmd.MarkFlagRequired("configDir") + +} diff --git a/config-tool/commands/root.go b/config-tool/commands/root.go new file mode 100644 index 000000000..eb76790c3 --- /dev/null +++ b/config-tool/commands/root.go @@ -0,0 +1,49 @@ +/* +Copyright © 2020 NAME HERE + +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. +*/ +package commands + +import ( + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var cfgFile string +var configDir string +var validationMode string + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "config-tool", + Short: "A Configuration Validation Tool for Quay", + Long: `This tool allows Quay users to validate their configuration bundles.`, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := rootCmd.Execute(); err != nil { + log.Fatalf(err.Error()) + } +} + +func init() { + + // Persistent Flags + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config-tool.yaml)") + + // Local Flags + rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/config-tool/commands/validate.go b/config-tool/commands/validate.go new file mode 100644 index 000000000..98a41b105 --- /dev/null +++ b/config-tool/commands/validate.go @@ -0,0 +1,159 @@ +/* +Copyright © 2020 NAME HERE + +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. +*/ +package commands + +import ( + "io/ioutil" + "os" + "path" + "sort" + "strings" + + "github.com/olekukonko/tablewriter" + "github.com/quay/quay/config-tool/pkg/lib/config" + "github.com/quay/quay/config-tool/pkg/lib/shared" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" +) + +// validateCmd represents the validate command +var validateCmd = &cobra.Command{ + Use: "validate", + Short: "Validate your config bundle", + + Run: func(cmd *cobra.Command, args []string) { + + log.SetOutput(os.Stdout) + + // Only log the warning severity or above. + if os.Getenv("DEBUGLOG") == "true" { + log.SetLevel(log.DebugLevel) + } else { + log.SetLevel(log.WarnLevel) + } + + isValid := true + + // Read config file + configFilePath := path.Join(configDir, "config.yaml") + configBytes, err := ioutil.ReadFile(configFilePath) + if err != nil { + log.Fatalf(err.Error()) + } + + // Unmarshal from json + var conf map[string]interface{} + if err = yaml.Unmarshal(configBytes, &conf); err != nil { + log.Fatalf(err.Error()) + } + + // Clean config + conf = shared.RemoveNullValues(conf) + + // Load into struct + configFieldGroups, err := config.NewConfig(conf) + if err != nil { + log.Fatalf("An error occurred during validation. Process could not marshal config.yaml. This is most likely due to an incorrect type. \nMore info: " + err.Error()) + } + + // Load certs + certs := shared.LoadCerts(configDir) + if err != nil { + log.Fatalf(err.Error()) + } + + // Sort keys + fgNames := []string{} + for fgName := range configFieldGroups { + fgNames = append(fgNames, fgName) + } + sort.Strings(fgNames) + + // Initialize validaiton status grid + validationStatus := [][]string{} + + for _, fgName := range fgNames { + + // Get field group for key + fieldGroup := configFieldGroups[fgName] + + // Set options + opts := shared.Options{ + Mode: validationMode, + Certificates: certs, + } + + // Validate + log.Debugf("Validating %s", fgName) + validationErrors := fieldGroup.Validate(opts) + + // If no errors, append row + if len(validationErrors) == 0 { + validationStatus = append(validationStatus, []string{fgName, "-", "🟢"}) + } + + // Append error messages + for _, err := range validationErrors { + + log.Debugf(err.FieldGroup, err.Message, err.Tags) + + // Append field group policy violation + validationStatus = append(validationStatus, []string{fgName, err.Message, "🔴"}) + isValid = false + } + + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Field Group", "Error", "Status"}) + table.SetBorder(true) + table.SetAutoFormatHeaders(false) + table.SetAutoWrapText(false) + table.AppendBulk(validationStatus) + table.SetColWidth(1000) + table.SetRowLine(true) + table.SetAutoMergeCellsByColumnIndex([]int{0}) + table.Render() + + if !isValid { + os.Exit(1) + } + + }, +} + +func init() { + + // Add validation command + rootCmd.AddCommand(validateCmd) + + // Add --config flag + validateCmd.Flags().StringVarP(&configDir, "configDir", "c", "", "A directory containing your config files") + validateCmd.MarkFlagRequired("configDir") + + validateCmd.Flags().StringVarP(&validationMode, "mode", "m", "", "The mode to validate the config. Must be either online, offline, or testing (for development only)") + validateCmd.MarkFlagRequired("mode") + +} + +// removeFieldGroup removes the FieldGroup. prefix to a tag name +func removeFieldGroupPrefix(input string) string { + if i := strings.Index(input, "."); i != -1 { + return input[i+1:] + } + return input +} diff --git a/config-tool/commands/version.go b/config-tool/commands/version.go new file mode 100644 index 000000000..b2f060aa5 --- /dev/null +++ b/config-tool/commands/version.go @@ -0,0 +1,20 @@ +package commands + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(versionCmd) +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print the version number of config-tool", + Long: `Print the version number of config-tool`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("config-tool v0.1.0") + }, +} diff --git a/config-tool/docs/docs.go b/config-tool/docs/docs.go new file mode 100644 index 000000000..96dead4fa --- /dev/null +++ b/config-tool/docs/docs.go @@ -0,0 +1,205 @@ +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag + +package docs + +import ( + "bytes" + "encoding/json" + "strings" + + "github.com/alecthomas/template" + "github.com/swaggo/swag" +) + +var doc = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{.Description}}", + "title": "{{.Title}}", + "contact": { + "name": "Jonathan King", + "email": "joking@redhat.com" + }, + "license": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/config": { + "get": { + "description": "This endpoint will load the config bundle mounted by the config-tool into memory. This state can then be modified, validated, downloaded, and optionally committed to a Quay operator instance.", + "produces": [ + "application/json" + ], + "summary": "Returns the mounted config bundle.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + } + } + }, + "/config/download": { + "post": { + "description": "This endpoint will download the config bundle in the request body as a tar.gz", + "consumes": [ + "application/json" + ], + "produces": [ + "multipart/form-data" + ], + "summary": "Downloads a config bundle as a tar.gz", + "parameters": [ + { + "description": "JSON Representing Config Bundle", + "name": "configBundle", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + ], + "responses": { + "200": {} + } + } + }, + "/config/operator": { + "post": { + "description": "Handles an HTTP POST request containing a new ` + "`" + `config.yaml` + "`" + `, adds any uploaded certs, and calls an API endpoint on the Quay Operator to create a new ` + "`" + `Secret` + "`" + `.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Commits a config bundle to a Quay operator instance.", + "parameters": [ + { + "description": "JSON Representing Config Bundle", + "name": "configBundle", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + ], + "responses": { + "200": {} + } + } + }, + "/config/validate": { + "post": { + "description": "This endpoint will validate the config bundle contained in the request body.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Validates a config bundle.", + "parameters": [ + { + "description": "JSON Representing Config Bundle", + "name": "configBundle", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + ], + "responses": { + "200": {} + } + } + } + }, + "definitions": { + "editor.ConfigBundle": { + "type": "object", + "properties": { + "certs": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "config.yaml": { + "type": "object", + "additionalProperties": true + }, + "managedFieldGroups": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "securityDefinitions": { + "BasicAuth": { + "type": "basic" + } + } +}` + +type swaggerInfo struct { + Version string + Host string + BasePath string + Schemes []string + Title string + Description string +} + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = swaggerInfo{ + Version: "0.0", + Host: "", + BasePath: "/api/v1", + Schemes: []string{"http"}, + Title: "Config Tool Editor API", + Description: "", +} + +type s struct{} + +func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + + t, err := template.New("swagger_info").Funcs(template.FuncMap{ + "marshal": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, + }).Parse(doc) + if err != nil { + return doc + } + + var tpl bytes.Buffer + if err := t.Execute(&tpl, sInfo); err != nil { + return doc + } + + return tpl.String() +} + +func init() { + swag.Register(swag.Name, &s{}) +} diff --git a/config-tool/docs/swagger.json b/config-tool/docs/swagger.json new file mode 100644 index 000000000..fecb7eb2a --- /dev/null +++ b/config-tool/docs/swagger.json @@ -0,0 +1,144 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "title": "Config Tool Editor API", + "contact": { + "name": "Jonathan King", + "email": "joking@redhat.com" + }, + "license": {}, + "version": "0.0" + }, + "basePath": "/api/v1", + "paths": { + "/config": { + "get": { + "description": "This endpoint will load the config bundle mounted by the config-tool into memory. This state can then be modified, validated, downloaded, and optionally committed to a Quay operator instance.", + "produces": [ + "application/json" + ], + "summary": "Returns the mounted config bundle.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + } + } + }, + "/config/download": { + "post": { + "description": "This endpoint will download the config bundle in the request body as a tar.gz", + "consumes": [ + "application/json" + ], + "produces": [ + "multipart/form-data" + ], + "summary": "Downloads a config bundle as a tar.gz", + "parameters": [ + { + "description": "JSON Representing Config Bundle", + "name": "configBundle", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + ], + "responses": { + "200": {} + } + } + }, + "/config/operator": { + "post": { + "description": "Handles an HTTP POST request containing a new `config.yaml`, adds any uploaded certs, and calls an API endpoint on the Quay Operator to create a new `Secret`.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Commits a config bundle to a Quay operator instance.", + "parameters": [ + { + "description": "JSON Representing Config Bundle", + "name": "configBundle", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + ], + "responses": { + "200": {} + } + } + }, + "/config/validate": { + "post": { + "description": "This endpoint will validate the config bundle contained in the request body.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Validates a config bundle.", + "parameters": [ + { + "description": "JSON Representing Config Bundle", + "name": "configBundle", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/editor.ConfigBundle" + } + } + ], + "responses": { + "200": {} + } + } + } + }, + "definitions": { + "editor.ConfigBundle": { + "type": "object", + "properties": { + "certs": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "config.yaml": { + "type": "object", + "additionalProperties": true + }, + "managedFieldGroups": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "securityDefinitions": { + "BasicAuth": { + "type": "basic" + } + } +} \ No newline at end of file diff --git a/config-tool/docs/swagger.yaml b/config-tool/docs/swagger.yaml new file mode 100644 index 000000000..5dff7cb9b --- /dev/null +++ b/config-tool/docs/swagger.yaml @@ -0,0 +1,94 @@ +basePath: /api/v1 +definitions: + editor.ConfigBundle: + properties: + certs: + additionalProperties: + items: + type: integer + type: array + type: object + config.yaml: + additionalProperties: true + type: object + managedFieldGroups: + items: + type: string + type: array + type: object +info: + contact: + email: joking@redhat.com + name: Jonathan King + license: {} + title: Config Tool Editor API + version: "0.0" +paths: + /config: + get: + description: This endpoint will load the config bundle mounted by the config-tool into memory. This state can then be modified, validated, downloaded, and optionally committed to a Quay operator instance. + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/editor.ConfigBundle' + summary: Returns the mounted config bundle. + /config/download: + post: + consumes: + - application/json + description: This endpoint will download the config bundle in the request body as a tar.gz + parameters: + - description: JSON Representing Config Bundle + in: body + name: configBundle + required: true + schema: + $ref: '#/definitions/editor.ConfigBundle' + produces: + - multipart/form-data + responses: + "200": {} + summary: Downloads a config bundle as a tar.gz + /config/operator: + post: + consumes: + - application/json + description: Handles an HTTP POST request containing a new `config.yaml`, adds any uploaded certs, and calls an API endpoint on the Quay Operator to create a new `Secret`. + parameters: + - description: JSON Representing Config Bundle + in: body + name: configBundle + required: true + schema: + $ref: '#/definitions/editor.ConfigBundle' + produces: + - application/json + responses: + "200": {} + summary: Commits a config bundle to a Quay operator instance. + /config/validate: + post: + consumes: + - application/json + description: This endpoint will validate the config bundle contained in the request body. + parameters: + - description: JSON Representing Config Bundle + in: body + name: configBundle + required: true + schema: + $ref: '#/definitions/editor.ConfigBundle' + produces: + - application/json + responses: + "200": {} + summary: Validates a config bundle. +schemes: +- http +securityDefinitions: + BasicAuth: + type: basic +swagger: "2.0" diff --git a/config-tool/go.mod b/config-tool/go.mod new file mode 100644 index 000000000..f96628efa --- /dev/null +++ b/config-tool/go.mod @@ -0,0 +1,87 @@ +module github.com/quay/quay/config-tool + +go 1.19 + +require ( + cuelang.org/go v0.2.1 + github.com/Azure/azure-storage-blob-go v0.11.0 + github.com/abbot/go-http-auth v0.4.0 + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 + github.com/aws/aws-sdk-go v1.44.282 + github.com/coreos/go-oidc v2.2.1+incompatible + github.com/creasty/defaults v1.4.0 + github.com/dave/jennifer v1.4.0 + github.com/go-chi/chi v4.1.2+incompatible + github.com/go-ldap/ldap/v3 v3.2.4 + github.com/go-redis/redis/v8 v8.0.0-beta.6 + github.com/go-sql-driver/mysql v1.5.0 + github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 + github.com/jackc/pgx/v4 v4.11.0 + github.com/jojomi/go-spew v1.1.1-0.20180323180114-b94c73b27dc1 + github.com/json-iterator/go v1.1.12 + github.com/lib/pq v1.7.0 + github.com/minio/minio-go/v7 v7.0.40 + github.com/ncw/swift v1.0.52 + github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6 + github.com/sirupsen/logrus v1.9.0 + github.com/spf13/cobra v1.0.0 + github.com/swaggo/http-swagger v1.3.3 + github.com/swaggo/swag v1.8.1 + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa + golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/Azure/azure-pipeline-go v0.2.3 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/spec v0.20.6 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/golang/protobuf v1.4.3 // indirect + github.com/google/go-cmp v0.5.4 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.8.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.0.6 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.7.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/compress v1.15.9 // indirect + github.com/klauspost/cpuid/v2 v2.1.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-ieproxy v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/onsi/ginkgo v1.14.2 // indirect + github.com/onsi/gomega v1.10.3 // indirect + github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect + github.com/rs/xid v1.4.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect + go.opentelemetry.io/otel v0.13.0 // indirect + golang.org/x/exp v0.0.0-20200513190911-00229845015e // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.1.12 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/ini.v1 v1.66.6 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/config-tool/go.sum b/config-tool/go.sum new file mode 100644 index 000000000..fbf45feed --- /dev/null +++ b/config-tool/go.sum @@ -0,0 +1,763 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cuelang.org/go v0.2.1 h1:/cUwlLv+XM31dUbAdv94kcO4VyBcpUZ9nnVU76y9I1s= +cuelang.org/go v0.2.1/go.mod h1:vppNUYfS1ft0358GnzUjtObSOhdxooZehmU3A2y43EQ= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= +github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= +github.com/Azure/azure-storage-blob-go v0.11.0 h1:WCTHKKNkHlzm7lzUNXRSD11784LwJqdrxnwWJxsJQHg= +github.com/Azure/azure-storage-blob-go v0.11.0/go.mod h1:A0u4VjtpgZJ7Y7um/+ix2DHBuEKFC6sEIlj0xc13a4Q= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest/adal v0.9.2 h1:Aze/GQeAN1RRbGmnUJvUj+tFGBzFdIg3293/A9rbxC4= +github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= +github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.282 h1:ZPB9QhwxmMIEC8ja0DdFowOl5fODWaZ6s2cZ40fx6r8= +github.com/aws/aws-sdk-go v1.44.282/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= +github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creasty/defaults v1.4.0 h1:Pz90duUjIzkmCznPtRSpamL+ET00QOxyA+kIgpRDp/E= +github.com/creasty/defaults v1.4.0/go.mod h1:9UWnPlI41ASz+YJswP5aK5S79d6QH60/Ioz52OXV9X8= +github.com/dave/jennifer v1.4.0 h1:tNJFJmLDVTLu+v05mVZ88RINa3vQqnyyWkTKWYz0CwE= +github.com/dave/jennifer v1.4.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9 h1:h2Ul3Ym2iVZWMQGYmulVUJ4LSkBm1erp9mUkPwtMoLg= +github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= +github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E= +github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-redis/redis/v8 v8.0.0-beta.6 h1:QeXAkG9L5cWJA+eJTBvhkftE7dwpJ0gbMYeBE2NxXS4= +github.com/go-redis/redis/v8 v8.0.0-beta.6/go.mod h1:g79Vpae8JMzg5qjk8BiwU9tK+HmU3iDVyS4UAJLFycI= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8= +github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.8.1 h1:ySBX7Q87vOMqKU2bbmKbUvtYhauDFclYbNDYIE1/h6s= +github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.7.0 h1:6f4kVsW01QftE38ufBYxKciO6gyioXSC0ABIRLcZrGs= +github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.11.0 h1:J86tSWd3Y7nKjwT/43xZBvpi04keQWx8gNC2YkdJhZI= +github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jojomi/go-spew v1.1.1-0.20180323180114-b94c73b27dc1 h1:tB9wM01caN00bZkWAo6r8sniOX59o7E0rHVdelMtCY0= +github.com/jojomi/go-spew v1.1.1-0.20180323180114-b94c73b27dc1/go.mod h1:/8AXJSDFeVKowsL5GiXhfUy9lT6JtLVxbY3H6qhBXtE= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= +github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= +github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= +github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.40 h1:dgyyRKelGW1B/7spyDyvHv9LI3RK5AJDJUrIRllyLk4= +github.com/minio/minio-go/v7 v7.0.40/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncw/swift v1.0.52 h1:ACF3JufDGgeKp/9mrDgQlEgS8kRYC4XKcuzj/8EJjQU= +github.com/ncw/swift v1.0.52/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6 h1:F721VBMijn0OBFZ5wUSuMVVLQj2IJiiupn6UNd7UbBE= +github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e h1:BLqxdwZ6j771IpSCRx7s/GJjXHUE00Hmu7/YegCGdzA= +github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc= +github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc= +github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo= +github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI= +github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo= +go.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA= +go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200513190911-00229845015e h1:rMqLP+9XLy+LdbCXHjJHAmTfXCr93W7oruWA6Hq1Alc= +golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= +gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/config-tool/pkg/lib/config/config.go b/config-tool/pkg/lib/config/config.go new file mode 100644 index 000000000..b0a45f24f --- /dev/null +++ b/config-tool/pkg/lib/config/config.go @@ -0,0 +1,162 @@ +package config + +import ( + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/accesssettings" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/actionlogarchiving" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/apptokenauthentication" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/buildmanager" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/database" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/distributedstorage" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/elasticsearch" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/email" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/githubbuildtrigger" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/githublogin" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/googlelogin" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/hostsettings" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/jwtauthentication" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/ldap" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/oidc" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/quaydocumentation" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/redis" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/repomirror" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/securityscanner" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/teamsyncing" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/timemachine" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/uservisiblesettings" + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Config is a struct that represents a configuration as a mapping of field groups +type Config map[string]shared.FieldGroup + +// NewConfig creates a Config struct from a map[string]interface{} +func NewConfig(fullConfig map[string]interface{}) (Config, error) { + + var err error + newConfig := Config{} + newBitbucketBuildTriggerFieldGroup, err := bitbucketbuildtrigger.NewBitbucketBuildTriggerFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["BitbucketBuildTrigger"] = newBitbucketBuildTriggerFieldGroup + + newElasticSearchFieldGroup, err := elasticsearch.NewElasticSearchFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["ElasticSearch"] = newElasticSearchFieldGroup + newSecurityScannerFieldGroup, err := securityscanner.NewSecurityScannerFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["SecurityScanner"] = newSecurityScannerFieldGroup + newActionLogArchivingFieldGroup, err := actionlogarchiving.NewActionLogArchivingFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["ActionLogArchiving"] = newActionLogArchivingFieldGroup + newUserVisibleSettingsFieldGroup, err := uservisiblesettings.NewUserVisibleSettingsFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["UserVisibleSettings"] = newUserVisibleSettingsFieldGroup + newHostSettingsFieldGroup, err := hostsettings.NewHostSettingsFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["HostSettings"] = newHostSettingsFieldGroup + newGoogleLoginFieldGroup, err := googlelogin.NewGoogleLoginFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["GoogleLogin"] = newGoogleLoginFieldGroup + newGitHubLoginFieldGroup, err := githublogin.NewGitHubLoginFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["GitHubLogin"] = newGitHubLoginFieldGroup + newRepoMirrorFieldGroup, err := repomirror.NewRepoMirrorFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["RepoMirror"] = newRepoMirrorFieldGroup + newAppTokenAuthenticationFieldGroup, err := apptokenauthentication.NewAppTokenAuthenticationFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["AppTokenAuthentication"] = newAppTokenAuthenticationFieldGroup + newQuayDocumentationFieldGroup, err := quaydocumentation.NewQuayDocumentationFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["QuayDocumentation"] = newQuayDocumentationFieldGroup + newGitHubBuildTriggerFieldGroup, err := githubbuildtrigger.NewGitHubBuildTriggerFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["GitHubBuildTrigger"] = newGitHubBuildTriggerFieldGroup + newGitLabBuildTriggerFieldGroup, err := gitlabbuildtrigger.NewGitLabBuildTriggerFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["GitLabBuildTrigger"] = newGitLabBuildTriggerFieldGroup + newDatabaseFieldGroup, err := database.NewDatabaseFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["Database"] = newDatabaseFieldGroup + newTimeMachineFieldGroup, err := timemachine.NewTimeMachineFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["TimeMachine"] = newTimeMachineFieldGroup + newTeamSyncingFieldGroup, err := teamsyncing.NewTeamSyncingFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["TeamSyncing"] = newTeamSyncingFieldGroup + newDistributedStorageFieldGroup, err := distributedstorage.NewDistributedStorageFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["DistributedStorage"] = newDistributedStorageFieldGroup + newAccessSettingsFieldGroup, err := accesssettings.NewAccessSettingsFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["AccessSettings"] = newAccessSettingsFieldGroup + newJWTAuthenticationFieldGroup, err := jwtauthentication.NewJWTAuthenticationFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["JWTAuthentication"] = newJWTAuthenticationFieldGroup + newEmailFieldGroup, err := email.NewEmailFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["Email"] = newEmailFieldGroup + newRedisFieldGroup, err := redis.NewRedisFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["Redis"] = newRedisFieldGroup + newLDAPFieldGroup, err := ldap.NewLDAPFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["LDAP"] = newLDAPFieldGroup + newOIDCFieldGroup, err := oidc.NewOIDCFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["OIDC"] = newOIDCFieldGroup + newBuildManagerFieldGroup, err := buildmanager.NewBuildManagerFieldGroup(fullConfig) + if err != nil { + return newConfig, err + } + newConfig["BuildManager"] = newBuildManagerFieldGroup + + return newConfig, nil +} diff --git a/config-tool/pkg/lib/config/config_validator.go b/config-tool/pkg/lib/config/config_validator.go new file mode 100644 index 000000000..2cc3a9de7 --- /dev/null +++ b/config-tool/pkg/lib/config/config_validator.go @@ -0,0 +1,30 @@ +package config + +import ( + "github.com/quay/quay/config-tool/pkg/lib/shared" + log "github.com/sirupsen/logrus" +) + +// Validate checks the configuration settings for this field group +func (c Config) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + configErrors := []shared.ValidationError{} + + // Iterate through field groups and add validator + for fgName, fg := range c { + + log.Debugf("Validating %s", fgName) + + // Validate specific field group + fgErrors := fg.Validate(opts) + + // If errors were present, append to config errors + if len(fgErrors) > 0 { + configErrors = append(configErrors, fgErrors...) + } + } + + // Return errors + return configErrors +} diff --git a/config-tool/pkg/lib/editor/API.md b/config-tool/pkg/lib/editor/API.md new file mode 100644 index 000000000..fe461b46a --- /dev/null +++ b/config-tool/pkg/lib/editor/API.md @@ -0,0 +1,146 @@ +# Quay Automated Deployment + +This guide will walk you through a Quay deployment through the exposed API. This can be used for automated deployments and reconfigurations. + +## Quay Install + +First, you must download the Quay image. This can be pulled from quay.io. + +```bash +$ docker pull quay.io/projectquay/quay:latest +``` + +Now that we have a Quay image, we can start building a configuration. This can be done through the config-tool API. If a config bundle isn't mounted, the config-tool will boot into a setup session. This will allow us to create a new config bundle from scratch. To start the config-tool from the Quay container, run the following command: + +```bash +$ docker run -p 8080:8080 quay.io/projectquay/quay:latest config secret +``` + +This will start a config-tool session with the credentials `username: quayconfig, password: secret` + +## Using the config-tool API + +There are 4 endpoints exposed in the config-tool API that can be used to build, validate, bundle, and deploy a configuration. Since the config-tool was loaded in a setup session, hitting the following endpoint will return a base config. The base config.yaml will contain default values and can later be changed with custom configurations. + +### GET - Get Mounted (or default) Config Bundle + +```bash +$ curl -u quayconfig:secret localhost:8080/api/v1/config | jq + + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 1885 100 1885 0 0 368k 0 --:--:-- --:--:-- --:--:-- 368k +{ + "config.yaml": { + "AUTHENTICATION_TYPE": "Database", + "AVATAR_KIND": "local", + "DB_CONNECTION_ARGS": { + "autorollback": true, + "threadlocals": true + }, + "DEFAULT_TAG_EXPIRATION": "2w", + "EXTERNAL_TLS_TERMINATION": false, + "FEATURE_ACTION_LOG_ROTATION": false, + "FEATURE_BITBUCKET_BUILD": false, + ... + ... + "TAG_EXPIRATION_OPTIONS": [ + "0s", + "1d", + "1w", + "2w", + "4w" + ], + "TEAM_RESYNC_STALE_TIME": "30m", + "USER_RECOVERY_TOKEN_LIFETIME": "30m" + } +} + +``` + +### POST - Validate Config Bundle + +```bash +$ curl --header "Content-Type: application/json" --request POST --data '{ + "config.yaml": { + "AUTHENTICATION_TYPE": "Database", + "AVATAR_KIND": "local", + "DB_CONNECTION_ARGS": { + "autorollback": true, + "threadlocals": true + }, + "DEFAULT_TAG_EXPIRATION": "2w", + "EXTERNAL_TLS_TERMINATION": false, + "FEATURE_ACTION_LOG_ROTATION": false, + "FEATURE_BITBUCKET_BUILD": false, + ... + ... + "TAG_EXPIRATION_OPTIONS": [ + "0s", + "1d", + "1w", + "2w", + "4w" + ], + "TEAM_RESYNC_STALE_TIME": "30m", + "USER_RECOVERY_TOKEN_LIFETIME": "30m" + } +}' -u quayconfig:password localhost:8080/api/v1/config/validate | jq + +% Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 2827 100 531 100 2296 103k 448k --:--:-- --:--:-- --:--:-- 552k +[ + { + "FieldGroup": "Redis", + "Tags": [ + "BUILDLOGS_REDIS" + ], + "Message": "BUILDLOGS_REDIS is required" + }, + { + "FieldGroup": "Database", + "Tags": [ + "DB_URI" + ], + "Message": "DB_URI is required." + }, + { + "FieldGroup": "DistributedStorage", + "Tags": [ + "DISTRIBUTED_STORAGE_CONFIG" + ], + "Message": "DISTRIBUTED_STORAGE_CONFIG must contain at least one storage location." + }, + { + "FieldGroup": "HostSettings", + "Tags": [ + "SERVER_HOSTNAME" + ], + "Message": "SERVER_HOSTNAME is required" + }, + { + "FieldGroup": "HostSettings", + "Tags": [ + "SERVER_HOSTNAME" + ], + "Message": "SERVER_HOSTNAME must be of type Hostname" + } +] +``` + +Notice that this endpoint returns any validation errors that exist in the config. Here, we see that our base config was still missing a few required fields. When a configuration is fully validated, this endpoint will return an empty list. Once a config bundle has been successfully validated, we can use a third endpoint to bundle our config into a tar.gz. + +## Create/Add Super Users + +In order to add a super user to Quay, the username must be included in the `SUPER_USERS` field in the `config.yaml`. This can be done using the instructions in the previous step. Once the username has been set as a super user, the user can be created inside of Quay. This can be done through the Quay API. To create a user, start the Quay container in registry mode and run the following command: + +```bash +$ curl --header "Content-Type: application/json" --request POST --data '{ + "username": , + "password": + }' localhost:8080/v1/superuser/users + +``` + +This command will create a user in the database. diff --git a/config-tool/pkg/lib/editor/controllers.go b/config-tool/pkg/lib/editor/controllers.go new file mode 100644 index 000000000..172e86bca --- /dev/null +++ b/config-tool/pkg/lib/editor/controllers.go @@ -0,0 +1,314 @@ +package editor + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/json" + "io/ioutil" + "net/http" + "path" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/quay/quay/config-tool/pkg/lib/config" + conf "github.com/quay/quay/config-tool/pkg/lib/config" + "github.com/quay/quay/config-tool/pkg/lib/shared" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" +) + +func rootHandler(opts *ServerOptions) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/" { + p := opts.staticContentPath + "/index.html" + + if len(opts.operatorEndpoint) > 0 { + http.SetCookie(w, &http.Cookie{Name: "QuayOperatorEndpoint", Value: opts.operatorEndpoint}) + } + + http.SetCookie(w, &http.Cookie{Name: "QuayReadOnlyFieldGroups", Value: strings.Join(opts.readOnlyFieldGroups, ",")}) + + http.ServeFile(w, r, p) + return + } + + w.WriteHeader(404) + } +} + +// @Summary Returns the mounted config bundle. +// @Description This endpoint will load the config bundle mounted by the config-tool into memory. This state can then be modified, validated, downloaded, and optionally committed to a Quay operator instance. +// @Produce json +// @Success 200 {object} ConfigBundle +// @Router /config [get] +func getMountedConfigBundle(opts *ServerOptions) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + + // Fill defaults + var config map[string]interface{} + defaultFieldGroups, err := conf.NewConfig(map[string]interface{}{}) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Fill defaults + for _, fg := range defaultFieldGroups { + fgBytes, err := yaml.Marshal(fg) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = yaml.Unmarshal(fgBytes, &config) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + // Read config file + configFilePath := path.Join(opts.configPath, "config.yaml") + configBytes, err := ioutil.ReadFile(configFilePath) + if err != nil { + // Mount not found, but will continue with defaults + w.WriteHeader(http.StatusAccepted) + } else { + w.WriteHeader(http.StatusOK) + if err = yaml.Unmarshal(configBytes, &config); err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + // Get all certs in directory + certs := shared.LoadCerts(opts.configPath) + + resp := ConfigBundle{ + Config: config, + Certificates: certs, + } + var json = jsoniter.ConfigCompatibleWithStandardLibrary + js, err := json.Marshal(resp) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Add("Content-Type", "application/json") + w.Write(js) + } +} + +// @Summary Downloads a config bundle as a tar.gz +// @Description This endpoint will download the config bundle in the request body as a tar.gz +// @Accept json +// @Param configBundle body ConfigBundle true "JSON Representing Config Bundle" +// @Produce multipart/form-data +// @Success 200 +// @Router /config/download [post] +func downloadConfigBundle(opts *ServerOptions) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var confBundle ConfigBundle + err := json.NewDecoder(r.Body).Decode(&confBundle) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + files := make(map[string][]byte) + files["config.yaml"], err = yaml.Marshal(confBundle.Config) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + for certName, contents := range confBundle.Certificates { + files[certName] = contents + } + + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + tw := tar.NewWriter(gw) + hdr := &tar.Header{ + Name: "extra_ca_certs/", + Typeflag: tar.TypeDir, + Mode: 0777, + } + if err := tw.WriteHeader(hdr); err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + for name, contents := range files { + hdr := &tar.Header{ + Name: name, + Mode: 0777, + Size: int64(len(contents)), + } + if err := tw.WriteHeader(hdr); err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if _, err := tw.Write(contents); err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + tw.Close() + gw.Close() + + w.Header().Set("Content-type", "application/zip") + w.Header().Set("Content-Disposition", "attachment; filename=quay-config.tar.gz") + w.Write(buf.Bytes()) + } +} + +// @Summary Validates a config bundle. +// @Description This endpoint will validate the config bundle contained in the request body. +// @Accept json +// @Param configBundle body ConfigBundle true "JSON Representing Config Bundle" +// @Produce json +// @Success 200 +// @Router /config/validate [post] +func validateConfigBundle(opts *ServerOptions) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + log.Debug("Received config bundle. Decoding into ConfigBundle struct") + + var configBundle ConfigBundle + err := json.NewDecoder(r.Body).Decode(&configBundle) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + configBundle.Config = shared.FixNumbers(configBundle.Config) + configBundle.Config = shared.RemoveNullValues(configBundle.Config) + + loaded, err := config.NewConfig(configBundle.Config) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + mode := r.URL.Query().Get("mode") + if mode == "" { + mode = "online" + } + + opts := shared.Options{ + Mode: mode, + Certificates: configBundle.Certificates, + } + + errors := loaded.Validate(opts) + + var json = jsoniter.ConfigCompatibleWithStandardLibrary + js, err := json.Marshal(errors) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Add("Content-Type", "application/json") + w.Write(js) + } +} + +// @Summary Commits a config bundle to a Quay operator instance. +// @Description Handles an HTTP POST request containing a new `config.yaml`, adds any uploaded certs, and calls an API endpoint on the Quay Operator to create a new `Secret`. +// @Accept json +// @Param configBundle body ConfigBundle true "JSON Representing Config Bundle" +// @Produce json +// @Success 200 +// @Router /config/operator [post] +func commitToOperator(opts *ServerOptions) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var configBundle ConfigBundle + err := json.NewDecoder(r.Body).Decode(&configBundle) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + configBundle.Config = shared.FixNumbers(configBundle.Config) + configBundle.Config = shared.RemoveNullValues(configBundle.Config) + + // TODO(alecmerdler): For each managed component fieldgroup, remove its fields from `config.yaml` using `Fields()` function... + newConfig, err := config.NewConfig(configBundle.Config) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + for _, fieldGroup := range configBundle.ManagedFieldGroups { + // NOTE: Skip `HostSettings` because it is a special case where we allow setting the fields. + if fieldGroup == "HostSettings" { + continue + } + + if fg, ok := newConfig[fieldGroup]; ok { + fields := fg.Fields() + for _, field := range fields { + delete(configBundle.Config, field) + } + } + } + + // TODO: Define struct type for this with correct `yaml` tags + preSecret := map[string]interface{}{ + "quayRegistryName": strings.Split(opts.podName, "-quay-config-editor")[0], + "namespace": opts.podNamespace, + "config.yaml": configBundle.Config, + "certs": configBundle.Certificates, + } + + var json = jsoniter.ConfigCompatibleWithStandardLibrary + js, err := json.Marshal(preSecret) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // FIXME: Currently hardcoding + req, err := http.NewRequest("POST", opts.operatorEndpoint+"/reconfigure", bytes.NewBuffer(js)) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Errorf(err.Error()) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(resp.StatusCode) + w.Header().Add("Content-Type", "application/json") + w.Write(body) + } +} diff --git a/config-tool/pkg/lib/editor/editor.go b/config-tool/pkg/lib/editor/editor.go new file mode 100644 index 000000000..016d2facd --- /dev/null +++ b/config-tool/pkg/lib/editor/editor.go @@ -0,0 +1,170 @@ +// @title Config Tool Editor API +// @version 0.0 +// @contact.name Jonathan King +// @contact.email joking@redhat.com +// @BasePath /api/v1 +// @securityDefinitions.basic BasicAuth +// @schemes http + +package editor + +import ( + "crypto/tls" + "errors" + "io/ioutil" + "mime" + "net/http" + "os" + + log "github.com/sirupsen/logrus" + + auth "github.com/abbot/go-http-auth" + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + _ "github.com/quay/quay/config-tool/docs" + httpSwagger "github.com/swaggo/http-swagger" + "golang.org/x/crypto/bcrypt" +) + +// ServerOptions holds information regarding the set up of the config-tool server +type ServerOptions struct { + username string + password string + port string + configPath string + staticContentPath string + operatorEndpoint string + readOnlyFieldGroups []string + podNamespace string // Optional + podName string // Optional + publicKeyPath string + privateKeyPath string +} + +// ConfigBundle is the current state of the config bundle on the server. It may read from a path on disk and then edited through the API. +type ConfigBundle struct { + Config map[string]interface{} `json:"config.yaml" yaml:"config.yaml"` + Certificates map[string][]byte `json:"certs,omitempty" yaml:"certs,omitempty"` + ManagedFieldGroups []string `json:"managedFieldGroups,omitempty" yaml:"managedFieldGroups,omitempty"` +} + +// RunConfigEditor runs the configuration editor server. +func RunConfigEditor(password, configPath, operatorEndpoint string, readOnlyFieldGroups []string) { + // FIX THIS + publicKeyPath := os.Getenv("CONFIG_TOOL_PUBLIC_KEY") + privateKeyPath := os.Getenv("CONFIG_TOOL_PRIVATE_KEY") + + staticContentPath, exists := os.LookupEnv("CONFIG_EDITOR_STATIC_CONTENT_PATH") + if !exists { + staticContentPath = "pkg/lib/editor/static" + } + podNamespace := os.Getenv("MY_POD_NAMESPACE") + podName := os.Getenv("MY_POD_NAME") + if operatorEndpoint != "" && (podNamespace == "" || podName == "") { + panic("If you would like to use operator reconfiguration features you must specify your namespace and pod name") // FIXME (jonathan) - come up with better error message + } + if readOnlyFieldGroups == nil { + readOnlyFieldGroups = []string{} + } + + opts := &ServerOptions{ + username: "quayconfig", // FIXME (jonathan) - add option to change username + password: password, + port: "8080", // FIXME (jonathan) - add option to change port + configPath: configPath, + staticContentPath: staticContentPath, + operatorEndpoint: operatorEndpoint, + readOnlyFieldGroups: readOnlyFieldGroups, + podNamespace: podNamespace, + podName: podName, + publicKeyPath: publicKeyPath, + privateKeyPath: privateKeyPath, + } + + hashed, _ := bcrypt.GenerateFromPassword([]byte(opts.password), 5) + authenticator := auth.NewBasicAuthenticator(opts.username, func(user, realm string) string { + if user == opts.username { + return string(hashed) + } + return "" + }) + + mime.AddExtensionType(".css", "text/css; charset=utf-8") + mime.AddExtensionType(".js", "application/javascript; charset=utf-8") + + if opts.operatorEndpoint != "" { + log.Printf("Using Operator Endpoint: " + opts.operatorEndpoint) + } + + r := chi.NewRouter() + if debug := os.Getenv("DEBUGLOG"); debug != "" { + r.Use(middleware.RequestID) + r.Use(middleware.RealIP) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + } + + // Function handlers + r.Get("/", rootHandler(opts)) + r.Get("/api/v1/config", auth.JustCheck(authenticator, getMountedConfigBundle(opts))) + r.Post("/api/v1/config/validate", auth.JustCheck(authenticator, validateConfigBundle(opts))) + r.Post("/api/v1/config/download", auth.JustCheck(authenticator, downloadConfigBundle(opts))) + r.Post("/api/v1/config/operator", auth.JustCheck(authenticator, commitToOperator(opts))) + + r.Get("/swagger/*", httpSwagger.Handler( + httpSwagger.URL("/docs/swagger.json"), // FIXME(jonathan) - This can eventually be changed to the github link to this file. + )) + + // File handlers + r.Get("/static/*", func(w http.ResponseWriter, r *http.Request) { + fs := http.StripPrefix("/static/", http.FileServer(http.Dir(opts.staticContentPath))) + fs.ServeHTTP(w, r) + }) + r.Get("/docs/*", func(w http.ResponseWriter, r *http.Request) { + fs := http.StripPrefix("/docs/", http.FileServer(http.Dir("docs"))) + fs.ServeHTTP(w, r) + }) + + // Create server base + s := &http.Server{ + Addr: ":" + opts.port, + Handler: r, + } + + // Try to load TLS + tlsConfig, err := loadTLS(opts.publicKeyPath, opts.privateKeyPath) + if err != nil { + log.Warningf("An error occurred loading TLS: " + err.Error() + ". Server falling back to HTTP.") + log.Infof("Running the configuration editor with HTTP on port %v with username %s", opts.port, opts.username) + log.Fatal(s.ListenAndServe()) + } else { + s.TLSConfig = tlsConfig + log.Infof("Running the configuration editor with HTTPS on port %v with username %s", opts.port, opts.username) + log.Fatal(s.ListenAndServeTLS("", "")) + } +} + +// tlsConfig will attempt to create a tls config given a public and private key. It returns an error if it fails to create a Config. +func loadTLS(publicKeyPath, privateKeyPath string) (*tls.Config, error) { + if publicKeyPath == "" { + return nil, errors.New("No public key provided for HTTPS") + } + if privateKeyPath == "" { + return nil, errors.New("No private key provided for HTTPS") + } + crt, err := ioutil.ReadFile(publicKeyPath) + if err != nil { + return nil, errors.New("Could not open public key: " + publicKeyPath) + } + key, err := ioutil.ReadFile(privateKeyPath) + if err != nil { + return nil, errors.New("Could not open private key: " + privateKeyPath) + } + cert, err := tls.X509KeyPair(crt, key) + if err != nil { + return nil, errors.New("Could not load X509 key pair: " + err.Error()) + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}}, nil +} diff --git a/config-tool/pkg/lib/editor/js/components/config-setup-app/config-setup-app.component.html b/config-tool/pkg/lib/editor/js/components/config-setup-app/config-setup-app.component.html new file mode 100644 index 000000000..6a7dab20d --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/config-setup-app/config-setup-app.component.html @@ -0,0 +1,8 @@ + +
+
+
\ No newline at end of file diff --git a/config-tool/pkg/lib/editor/js/components/config-setup-app/config-setup-app.component.ts b/config-tool/pkg/lib/editor/js/components/config-setup-app/config-setup-app.component.ts new file mode 100644 index 000000000..43e9431c5 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/config-setup-app/config-setup-app.component.ts @@ -0,0 +1,15 @@ +import { Component, Inject } from 'ng-metadata/core'; +const templateUrl = require('./config-setup-app.component.html'); + +declare var window: any; + +/** + * Initial Screen and Choice in the Config App + */ +@Component({ + selector: 'config-setup-app', + templateUrl: templateUrl, +}) +export class ConfigSetupAppComponent { + +} diff --git a/config-tool/pkg/lib/editor/js/components/cor-floating-bottom-bar/cor-floating-bottom-bar.html b/config-tool/pkg/lib/editor/js/components/cor-floating-bottom-bar/cor-floating-bottom-bar.html new file mode 100644 index 000000000..11615e6a8 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-floating-bottom-bar/cor-floating-bottom-bar.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/config-tool/pkg/lib/editor/js/components/cor-floating-bottom-bar/cor-floating-bottom-bar.js b/config-tool/pkg/lib/editor/js/components/cor-floating-bottom-bar/cor-floating-bottom-bar.js new file mode 100644 index 000000000..ae18dcae7 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-floating-bottom-bar/cor-floating-bottom-bar.js @@ -0,0 +1,44 @@ +const templateUrl = require('./cor-floating-bottom-bar.html'); + +angular.module('quay-config') + .directive('corFloatingBottomBar', function() { + var directiveDefinitionObject = { + priority: 3, + templateUrl, + replace: true, + transclude: true, + restrict: 'C', + scope: {}, + controller: function($rootScope, $scope, $element, $timeout, $interval) { + var handler = function() { + $element.removeClass('floating'); + $element.css('width', $element[0].parentNode.clientWidth + 'px'); + + var windowHeight = $(window).height(); + var rect = $element[0].getBoundingClientRect(); + if (rect.bottom > windowHeight) { + $element.addClass('floating'); + } + }; + + $(window).on("scroll", handler); + $(window).on("resize", handler); + + var previousHeight = $element[0].parentNode.clientHeight; + var stop = $interval(function() { + var currentHeight = $element[0].parentNode.clientWidth; + if (previousHeight != currentHeight) { + currentHeight = previousHeight; + handler(); + } + }, 100); + + $scope.$on('$destroy', function() { + $(window).off("resize", handler); + $(window).off("scroll", handler); + $interval.cancel(stop); + }); + } + }; + return directiveDefinitionObject; + }); diff --git a/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader-inline.html b/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader-inline.html new file mode 100644 index 000000000..3a2c42c1d --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader-inline.html @@ -0,0 +1,5 @@ +
+
+
+
+
diff --git a/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader.html b/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader.html new file mode 100644 index 000000000..f0aab7afc --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader.html @@ -0,0 +1,5 @@ +
+
+
+
+
diff --git a/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader.js b/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader.js new file mode 100644 index 000000000..17c090666 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-loader/cor-loader.js @@ -0,0 +1,28 @@ +const loaderUrl = require('./cor-loader.html'); +const inlineUrl = require('./cor-loader-inline.html'); + +angular.module('quay-config') + .directive('corLoader', function() { + var directiveDefinitionObject = { + templateUrl: loaderUrl, + replace: true, + restrict: 'C', + scope: { + }, + controller: function($rootScope, $scope, $element) { + } + }; + return directiveDefinitionObject; + }) + .directive('corLoaderInline', function() { + var directiveDefinitionObject = { + templateUrl: inlineUrl, + replace: true, + restrict: 'C', + scope: { + }, + controller: function($rootScope, $scope, $element) { + } + }; + return directiveDefinitionObject; + }); diff --git a/config-tool/pkg/lib/editor/js/components/cor-option/cor-option.html b/config-tool/pkg/lib/editor/js/components/cor-option/cor-option.html new file mode 100644 index 000000000..8482a9050 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-option/cor-option.html @@ -0,0 +1,3 @@ +
  • + +
  • diff --git a/config-tool/pkg/lib/editor/js/components/cor-option/cor-option.js b/config-tool/pkg/lib/editor/js/components/cor-option/cor-option.js new file mode 100644 index 000000000..880d83df3 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-option/cor-option.js @@ -0,0 +1,32 @@ +const corOption = require('./cor-option.html'); +const corOptionsMenu = require('./cor-options-menu.html'); + +angular.module('quay-config') + .directive('corOptionsMenu', function() { + var directiveDefinitionObject = { + priority: 1, + templateUrl: corOptionsMenu, + replace: true, + transclude: true, + restrict: 'C', + scope: {}, + controller: function($rootScope, $scope, $element) { + } + }; + return directiveDefinitionObject; + }) + .directive('corOption', function() { + var directiveDefinitionObject = { + priority: 1, + templateUrl: corOption, + replace: true, + transclude: true, + restrict: 'C', + scope: { + 'optionClick': '&optionClick' + }, + controller: function($rootScope, $scope, $element) { + } + }; + return directiveDefinitionObject; + }); diff --git a/config-tool/pkg/lib/editor/js/components/cor-option/cor-options-menu.html b/config-tool/pkg/lib/editor/js/components/cor-option/cor-options-menu.html new file mode 100644 index 000000000..a234590a3 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-option/cor-options-menu.html @@ -0,0 +1,6 @@ + + + diff --git a/config-tool/pkg/lib/editor/js/components/cor-title/cor-title-content.html b/config-tool/pkg/lib/editor/js/components/cor-title/cor-title-content.html new file mode 100644 index 000000000..0d3e13ddd --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-title/cor-title-content.html @@ -0,0 +1,3 @@ +
    +

    +
    diff --git a/config-tool/pkg/lib/editor/js/components/cor-title/cor-title.html b/config-tool/pkg/lib/editor/js/components/cor-title/cor-title.html new file mode 100644 index 000000000..63cfd322c --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-title/cor-title.html @@ -0,0 +1,2 @@ +
    + diff --git a/config-tool/pkg/lib/editor/js/components/cor-title/cor-title.js b/config-tool/pkg/lib/editor/js/components/cor-title/cor-title.js new file mode 100644 index 000000000..033112f23 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/cor-title/cor-title.js @@ -0,0 +1,31 @@ + +const titleUrl = require('./cor-title.html'); +const titleContentUrl = require('./cor-title-content.html'); + +angular.module('quay-config') + .directive('corTitleContent', function() { + var directiveDefinitionObject = { + priority: 1, + templateUrl: titleContentUrl, + replace: true, + transclude: true, + restrict: 'C', + scope: {}, + controller: function($rootScope, $scope, $element) { + } + }; + return directiveDefinitionObject; + }) + .directive('corTitle', function() { + var directiveDefinitionObject = { + priority: 1, + templateUrl: titleUrl, + replace: true, + transclude: true, + restrict: 'C', + scope: {}, + controller: function($rootScope, $scope, $element) { + } + }; + return directiveDefinitionObject; + }); diff --git a/config-tool/pkg/lib/editor/js/components/datetime-picker/datetime-picker.html b/config-tool/pkg/lib/editor/js/components/datetime-picker/datetime-picker.html new file mode 100644 index 000000000..653c3869a --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/datetime-picker/datetime-picker.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/js/components/datetime-picker/datetime-picker.js b/config-tool/pkg/lib/editor/js/components/datetime-picker/datetime-picker.js new file mode 100644 index 000000000..cb1463e57 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/datetime-picker/datetime-picker.js @@ -0,0 +1,60 @@ +const templateUrl = require('./datetime-picker.html'); +/** + * An element which displays a datetime picker. + */ +angular.module('quay-config').directive('datetimePicker', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'datetime': '=datetime', + }, + controller: function($scope, $element) { + var datetimeSet = false; + + $(function() { + $element.find('input').datetimepicker({ + 'format': 'LLL', + 'sideBySide': true, + 'showClear': true, + 'minDate': new Date(), + 'debug': false + }); + + $element.find('input').on("dp.change", function (e) { + $scope.$apply(function() { + $scope.datetime = e.date ? e.date.unix() : null; + }); + }); + }); + + $scope.$watch('selected_datetime', function(value) { + if (!datetimeSet) { return; } + + if (!value) { + if ($scope.datetime) { + $scope.datetime = null; + } + return; + } + + $scope.datetime = (new Date(value)).getTime()/1000; + }); + + $scope.$watch('datetime', function(value) { + if (!value) { + $scope.selected_datetime = null; + datetimeSet = true; + return; + } + + $scope.selected_datetime = moment.unix(value).format('LLL'); + datetimeSet = true; + }); + } + }; + return directiveDefinitionObject; +}); \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/js/components/file-upload-box.html b/config-tool/pkg/lib/editor/js/components/file-upload-box.html new file mode 100644 index 000000000..65cdf9d6c --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/file-upload-box.html @@ -0,0 +1,46 @@ +
    +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    +
    + + Uploading file {{ currentlyUploadingFile.name }}... +
    + +
    {{ selectMessage }}
    +
    + + {{ message }} +
    +
    + + {{ message }} +
    +
    +
    \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/js/components/file-upload-box.js b/config-tool/pkg/lib/editor/js/components/file-upload-box.js new file mode 100644 index 000000000..b8175cc70 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/file-upload-box.js @@ -0,0 +1,112 @@ +const templateUrl = require('./file-upload-box.html'); +/** + * An element which adds a stylize box for uploading a file. + */ +angular.module('quay-config').directive('fileUploadBox', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'selectMessage': '@selectMessage', + 'certs': '=certs', + 'filesSelected': '&filesSelected', + 'filesCleared': '&filesCleared', + 'filesValidated': '&filesValidated', + + 'extensions': ' MAX_FILE_SIZE) { + $scope.state = 'error'; + $scope.message = 'File ' + selectedFiles[i].name + ' is larger than the maximum file ' + + 'size of ' + MAX_FILE_SIZE_MB + ' MB'; + return; + } + } + + $scope.state = 'checking'; + $scope.filesSelected(); + + for (var i = 0; i < selectedFiles.length; ++i) { + conductUpload(selectedFiles[i]) + } + } + + $scope.state = "clear" + $scope.selectedFiles = [] + }; + + + var conductUpload = function(file) { + + var reader = new FileReader(); + reader.readAsText(file) + + reader.onprogress = function(e) { + $scope.$apply(function() { + if (e.lengthComputable) { + $scope.uploadProgress = (e.loaded / e.total) * 100 + } + }); + } + + reader.onload = function(e){ + $scope.$apply(function(){ + $scope.certs["extra_ca_certs/"+file.name] = btoa(e.target.result) + $scope.uploadProgress = 100 + $scope.state = 'clear' + }) + } + + reader.onerror = function(e){ + $scope.$apply(function () { + doneCb(false, 'Error when uploading'); + $scope.state = "clear"; + }); + } + + }; + + $scope.getAccepts = function(extensions) { + if (!extensions || !extensions.length) { + return '*'; + } + + return extensions.join(','); + }; + + $scope.$watch('reset', function(reset) { + if (reset) { + $scope.state = 'clear'; + $element.find('#file-drop-' + $scope.boxId).parent().trigger('reset'); + } + }); + } + }; + return directiveDefinitionObject; +}); \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/js/components/files-changed.js b/config-tool/pkg/lib/editor/js/components/files-changed.js new file mode 100644 index 000000000..997dd9144 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/files-changed.js @@ -0,0 +1,18 @@ +/** + * Raises the 'filesChanged' event on the scope if a file on the marked exists. + */ +angular.module('quay-config').directive("filesChanged", [function () { + return { + restrict: 'A', + scope: { + 'filesChanged': "&" + }, + link: function (scope, element, attributes) { + element.bind("change", function (changeEvent) { + scope.$apply(function() { + scope.filesChanged({'files': changeEvent.target.files}); + }); + }); + } + } +}]); diff --git a/config-tool/pkg/lib/editor/js/components/registry-name/registry-name.html b/config-tool/pkg/lib/editor/js/components/registry-name/registry-name.html new file mode 100644 index 000000000..d68e63ddb --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/registry-name/registry-name.html @@ -0,0 +1 @@ +{{ name }} diff --git a/config-tool/pkg/lib/editor/js/components/registry-name/registry-name.js b/config-tool/pkg/lib/editor/js/components/registry-name/registry-name.js new file mode 100644 index 000000000..ade72a7d8 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/components/registry-name/registry-name.js @@ -0,0 +1,23 @@ +const templateUrl = require('./registry-name.html'); + +/** + * An element which displays the name of the registry (optionally the short name). + */ +angular.module('quay-config').directive('registryName', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'isShort': '=isShort' + }, + controller: function($scope, $element) { + // FIXME: Do we want to encode the name from the context somehow? + $scope.name = $scope.isShort ? 'Quay' : 'Quay'; + } + }; + return directiveDefinitionObject; +}); + diff --git a/config-tool/pkg/lib/editor/js/config-app.module.ts b/config-tool/pkg/lib/editor/js/config-app.module.ts new file mode 100644 index 000000000..5342fc4a3 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-app.module.ts @@ -0,0 +1,47 @@ +import { NgModule } from 'ng-metadata/core'; + +import { ConfigSetupAppComponent } from './components/config-setup-app/config-setup-app.component'; + +import * as restangular from 'restangular'; + +const quayDependencies: any[] = [ + restangular, +]; + +@NgModule(({ + imports: quayDependencies, + declarations: [], + providers: [ + provideConfig, + ] +})) +class DependencyConfig { } + + +provideConfig.$inject = [ + '$provide', + '$injector', + '$compileProvider', + 'RestangularProvider', +]; + +function provideConfig($provide: ng.auto.IProvideService, + $injector: ng.auto.IInjectorService, + $compileProvider: ng.ICompileProvider, + RestangularProvider: any): void { + + // Configure the API provider. + RestangularProvider.setBaseUrl('/api/v1/'); +} + + +@NgModule({ + imports: [ + DependencyConfig, + ], + declarations: [ + ConfigSetupAppComponent, + ], + providers: [] +}) +export class ConfigAppModule { } diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-bool-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-bool-field.html new file mode 100644 index 000000000..4e7f012b3 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-bool-field.html @@ -0,0 +1,8 @@ +
    +
    + +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-certificates-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-certificates-field.html new file mode 100644 index 000000000..31e0773da --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-certificates-field.html @@ -0,0 +1,77 @@ +
    +
    + +
    + extra_ca_certs is a single file and cannot be processed by this tool. If a valid and appended list of certificates, they will be installed on container startup. +
    + +
    +
    +

    This section lists any custom or self-signed SSL certificates that are installed in the container on startup after being read from the extra_ca_certs directory in the configuration volume. +

    +

    + Custom certificates are typically used in place of publicly signed certificates for corporate-internal services. +

    +

    Please make sure that all custom names used for downstream services (such as Clair) are listed in the certificates below.

    +
    + + + + + + +
    Upload certificates: +
    +
    + + + + + + + + + + + + + +
    Certificate FilenameStatusNames Handled
    {{ certificate.path }} +
    + + {{ certificate.error }} +
    +
    + + Certificate is expired +
    +
    + + Certificate is valid +
    +
    +
    (None)
    + {{ name }} +
    + + + Delete Certificate + + +
    +
    +
    + Uploading, validating and updating certificate(s) +
    +
    +
    No custom certificates found.
    +
    +
    +
    +
    \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-contact-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-contact-field.html new file mode 100644 index 000000000..df2b9de53 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-contact-field.html @@ -0,0 +1,46 @@ +
    + + + + + +
    + + +
    + +
    +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-contacts-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-contacts-field.html new file mode 100644 index 000000000..40762934c --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-contacts-field.html @@ -0,0 +1,4 @@ +
    +
    +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-file-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-file-field.html new file mode 100644 index 000000000..1208199b2 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-file-field.html @@ -0,0 +1,13 @@ +
    + + + /conf/stack/{{ filename }} + Select a replacement file: + + Please select a file to upload as {{ filename }}: + + + + Uploading file as {{ filename }}... {{ uploadProgress }}% + +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-list-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-list-field.html new file mode 100644 index 000000000..9918e9a07 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-list-field.html @@ -0,0 +1,17 @@ +
    +
      +
    • + {{ item }} + + Remove + +
    • +
    + No {{ itemTitle }}s defined +
    + + +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-map-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-map-field.html new file mode 100644 index 000000000..84f086052 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-map-field.html @@ -0,0 +1,20 @@ +
    + + + + + + +
    {{ key }}{{ value }} + Remove +
    + No entries defined +
    + Add Key-Value: + + + +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-numeric-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-numeric-field.html new file mode 100644 index 000000000..e77f4cd0f --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-numeric-field.html @@ -0,0 +1,7 @@ +
    +
    + +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-parsed-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-parsed-field.html new file mode 100644 index 000000000..766b0a8a2 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-parsed-field.html @@ -0,0 +1 @@ +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-password-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-password-field.html new file mode 100644 index 000000000..111f16606 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-password-field.html @@ -0,0 +1,9 @@ +
    +
    + +
    + {{ errorMessage }} +
    +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-service-key-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-service-key-field.html new file mode 100644 index 000000000..25c66a980 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-service-key-field.html @@ -0,0 +1,30 @@ +
    + +
    + + +
    + Could not load service keys +
    + + +
    +
    + + Valid key for service {{ serviceName }} exists + Assign New Key +
    +
    + No valid key found for service {{ serviceName }} + Create Key +
    +
    + + + + +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-string-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-string-field.html new file mode 100644 index 000000000..102c0cc81 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-string-field.html @@ -0,0 +1,11 @@ +
    +
    + +
    + {{ errorMessage }} +
    +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-string-list-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-string-list-field.html new file mode 100644 index 000000000..33e61c0a7 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-string-list-field.html @@ -0,0 +1,6 @@ +
    +
    + +
    +
    diff --git a/config-tool/pkg/lib/editor/js/config-field-templates/config-variable-field.html b/config-tool/pkg/lib/editor/js/config-field-templates/config-variable-field.html new file mode 100644 index 000000000..9236469cd --- /dev/null +++ b/config-tool/pkg/lib/editor/js/config-field-templates/config-variable-field.html @@ -0,0 +1,10 @@ +
    +
    + +
    + + +
    diff --git a/config-tool/pkg/lib/editor/js/core-config-setup/config-setup-tool.html b/config-tool/pkg/lib/editor/js/core-config-setup/config-setup-tool.html new file mode 100644 index 000000000..ce4b0a2bd --- /dev/null +++ b/config-tool/pkg/lib/editor/js/core-config-setup/config-setup-tool.html @@ -0,0 +1,2068 @@ +
    +
    +
    + + Red Hat Quay Setup +
    +
    +
    +
    +
    + You are in a setup session! +
    +
    + No configuration bundle was mounted: default values will be used. +
    +
    +
    +
    +
    +
    + +
    +
    + Custom SSL Certificates +
    +
    +
    +
    +
    + + +
    +
    + Basic Configuration +
    +
    + + + + + + + + + + + + + + + + + + +
    Registry Title: + +
    + Name of registry to be displayed in the Contact Page. +
    +
    Registry Title Short: + +
    Enterprise Logo URL: + +
    + Enter the full URL to your company's logo. +
    +
    + +
    Contact Information: + +
    + Information to show in the Contact Page. If none specified, CoreOS contact information + is displayed. +
    +
    +
    +
    + + +
    +
    + Server Configuration +
    +
    + + +
    + The Hostname configuration is currently managed externally. +
    + + + + + + + + + +
    Server Hostname: +
    + The domain name quay.io is reserved for legacy reasons and cannot be used in + installations. +
    + +
    + The HTTP host (and optionally the port number if a non-standard HTTP/HTTPS port) of the location + where the registry will be accessible on the network. +
    +
    TLS: + + + + + + + + + + + +
    Certificate: + +
    + The certificate must be in PEM format. +
    +
    Private key: + +
    +
    + +
    +
    + + +
    +
    + Database +
    +
    +
    +

    Quay uses a database as its primary metadata storage.

    +
    + + +
    + The database configuration is currently managed externally. +
    + + + + + +
    + Note: MySQL (and MariaDB) support is deprecated and support will be removed in a future version of Quay. If starting a new Quay installation please consider using PostgreSQL +
    + + + + + + + + + + + + + + + + + + + + +
    Database Type: + +
    Database Server: + > +
    + The server (and optionally, custom port) where the database lives +
    +
    Username: + +
    This user must have full access to the database
    +
    Password: + +
    Database Name: + +
    SSL Certificate: + +
    Optional SSL certicate (in PEM format) to use to connect to the database
    +
    +
    +
    + + + +
    +
    + Data Consistency Settings +
    +
    +
    +

    Relax constraints on consistency guarantees for specific operations + to enable higher performance and availability. +

    +
    + + + + +
    +
    + Allow repository pulls even if audit logging fails. +
    + If enabled, failures to write to the audit log will fallback from + the database to the standard logger for registry pulls. +
    +
    +
    +
    +
    + + +
    +
    + Time Machine +
    +
    +
    +

    Time machine keeps older copies of tags within a repository for the configured period + of time, after which they are garbage collected. This allows users to + revert tags to older images in case they accidentally pushed a broken image. It is + highly recommended to have time machine enabled, but it does take a bit more space + in storage. +

    +
    + + + + + + + + + + + + + + +
    Allowed expiration periods: + +
    + The expiration periods allowed for configuration. The default tag expiration *must* be in this list. +
    +
    Default expiration period: + +
    + The default tag expiration period for all namespaces (users and organizations). Must be expressed in a + duration string form: 30m, 1h, 1d, 2w. +
    +
    Allow users to select expiration: +
    + Enable Expiration Configuration +
    + If enabled, users will be able to select the tag expiration duration for the namespace(s) they + administrate, from the configured list of options. +
    +
    +
    +
    +
    + + +
    +
    + Redis +
    +
    +
    +

    A redis key-value store is required for real-time events and + build logs.

    +
    + + +
    + The Redis configuration is currently managed externally. +
    + + + + + + + + + + + + + +
    Redis Hostname: + +
    Redis port: + +
    + Access to this port and hostname must be allowed from all hosts running + the enterprise registry +
    +
    Redis password: + +
    +
    +
    + + +
    +
    + Repository Mirroring +
    +
    +
    +

    If enabled, scheduled mirroring of repositories from registries will be available. +

    + +
    + The repository mirroring configuration is currently managed externally. +
    + +
    + Enable Repository Mirroring +
    +
    + A repository mirror service must be running to use this feature. Documentation + on setting up and running this service can be found at Running Repository Mirroring Service. +
    + +
    + Require HTTPS and verify certificates of Quay registry during mirror. +
    +
    +
    + + +
    +
    + Registry Storage +
    +
    +
    +

    + Registry images can be stored either locally or in a remote storage system. +

    + Do not use "Locally mounted directory" Storage Engine for any production configurations. Mounted NFS volumes are not supported. Local storage is meant for test-only installations. +
    +
    + The registry storage configuration is currently managed externally. +
    +

    + +
    + Proxy storage via +
    + If enabled, all requests to storage engine(s) will be proxied through + . Should only be + enabled if storage cannot be directly accessed by external nodes talking to the registry. +
    +
    + +
    + Enable Storage Replication +
    + If enabled, replicates storage to other regions. See + documentation for more information. +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Location ID: + +
    + {{ sc.location }} +
    +
    + {{ storageConfigError[$index].location }} +
    + +
    Set Default: +
    + Replicate to storage engine by default +
    +
    Storage Engine: + + +
    + {{ storageConfigError[$index].engine }} +
    +
    {{ field.title }}: + + + + + {{ field.placeholder }} + + +
    + +
    +
    + {{ field.help_text }} +
    +
    + See Documentation for more information +
    +
    +
    + + +
    +
    +
    + + +
    +
    + Action Log Storage Configuration +
    +
    +
    +

    + Action logs can be stored in the database or Elasticsearch. + In the latter case, the actions logs can (optionally) be sent to a data stream first. +

    +
    + + + + +
    Logs storage: + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Elasticsearch config
    Elasticsearch hostname: + +
    Elasticsearch port: + +
    + Access to this port and hostname must be allowed from all hosts running + the enterprise registry +
    +
    Elasticsearch access key: + +
    Elasticsearch secret key: + +
    AWS region: + +
    Index prefix: + +
    Logs Producer: + +
    + + + + + + + + + + + + + + + + + + + + +
    Logs producer options
    Stream name: + +
    AWS access key: + +
    AWS secret key: + +
    AWS region: + +
    +
    +
    + + +
    +
    + Action Log Rotation and Archiving +
    +
    +
    +

    + All actions performed in are automatically logged. These logs are + stored in a database table, which can become quite large. + Enabling log rotation and archiving will move all logs older than 30 days into storage. +

    +
    +
    + Enable Action Log Rotation +
    + + + + + + + + + + + + + + +
    Storage location: + +
    + The storage location in which to place archived action logs. Logs will only be archived to this single + location. +
    +
    Storage path: + +
    + The path under the configured storage engine in which to place the archived logs in JSON form. +
    +
    Log Rotation Threshold: + +
    + The number of days after which to archive action logs to storage. Must be expressed in a duration + string form: 30m, 1h, 1d, 2w. +
    +
    + Note: The rotation threshold should be considered a balancing act between user history and size of + database. +
    +
    + Larger time windows will result in more logs, but give users more history to view. + Anything less than 2w is not recommended. +
    +
    +
    +
    + + +
    +
    + Security Scanner +
    +
    +
    +

    If enabled, all images pushed to will be scanned via the external + security scanning service, with vulnerability information available in the UI and API, as well + as async notification support. +

    +
    + +
    + The security scanner configuration is currently managed externally. +
    + +
    + Enable Security Scanning +
    +
    + + A scanner compliant with the Quay Security Scanning API must be running to use this feature. Documentation + on running Clair can be found at Running Clair Security + Scanner. +
    + + + + + + + + + + + +
    Security Scanner Endpoint: + +
    + The HTTP URL at which the security scanner is running. +
    +
    + Is the security scanner behind a domain signed with a self-signed TLS certificate? + If so, please make sure to register your SSL CA in the custom certificates + panel above. +
    +
    Security Scanner PSK: + +
    + Clair Pre-Shared Key. Make sure to include this value in your Clair config. +
    +
    + +
    +
    +
    + + +
    +
    + Application Registry +
    +
    +
    +

    If enabled, an additional registry API will be available for managing applications (Kubernetes + manifests, Helm charts) via the App Registry + specification. A great place to get started is to install the + Helm Registry Plugin. +

    + +
    + Enable App Registry +
    +
    +
    + + +
    +
    + E-mail +
    +
    +
    +

    Valid e-mail server configuration is required for notification e-mails and the ability of + users to reset their passwords.

    +
    + +
    + Enable E-mails +
    + + + + + + + + + + + + + + + + + + + + + + + +
    SMTP Server: + > +
    SMTP Server Port: + +
    TLS: +
    + Require TLS +
    +
    Mail Sender: + +
    + E-mail address from which all e-mails are sent. If not specified, + support@quay.io will be used. +
    +
    Authentication: +
    + Requires Authentication +
    + + + + + + + + + + +
    Username: + +
    Password: + +
    +
    +
    +
    + + +
    +
    + Internal Authentication +
    +
    +
    +

    + Authentication for the registry can be handled by either the registry itself, LDAP, Keystone, or + external JWT endpoint. +

    +

    + Additional external authentication providers (such as GitHub) can be used in addition + for login into the UI. +

    +
    + +
    +
    + It is highly recommended to require encrypted client passwords. External passwords used + in the Docker client will be stored in plaintext! + Enable this requirement now. +
    + +
    + Note: The "Require Encrypted Client Passwords" feature is currently enabled which will + prevent passwords from being saved as plaintext by the Docker client. +
    +
    + + + + + + + + + + + + + + + + + + + +
    Authentication: + +
    Team synchronization: +
    + Enable Team Synchronization Support +
    +
    + If enabled, organization administrators who are also superusers can set teams to have their + membership synchronized with a backing group in {{ config.AUTHENTICATION_TYPE }}. +
    +
    Resynchronization duration: + +
    + The duration before a team must be re-synchronized. Must be expressed in a duration string form: + 30m, 1h, 1d. +
    +
    Self-service team syncing setup: +
    If enabled, this feature will + allow *any organization administrator* to read the membership of any + {{ config.AUTHENTICATION_TYPE }} group.
    +
    + Allow non-superusers to enable and manage team syncing +
    +
    + If enabled, non-superusers will be able to enable and manage team sycning on teams under + organizations in which they are administrators. +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    Keystone API Version: + +
    Keystone Authentication URL: + +
    + The URL (starting with http or https) of the Keystone Server endpoint for auth. +
    +
    Keystone Administrator Username: + +
    + The username for the Keystone admin. +
    +
    Keystone Administrator Password: + +
    + The password for the Keystone admin. +
    +
    Keystone Administrator Tenant: + +
    + The tenant (project/group) that contains the administrator user. +
    +
    + + +
    + JSON Web Token authentication allows your organization to provide an HTTP endpoint that + verifies user credentials on behalf of . +
    + Documentation + on the API required can be found here: https://github.com/coreos/jwt-auth-example. +
    + + + + + + + + + + + + + + + + + + + + + + +
    Authentication Issuer: + +
    + The id of the issuer signing the JWT token. Must be unique to your organization. +
    +
    Public Key: + +
    + A certificate containing the public key portion of the key pair used to sign + the JSON Web Tokens. This file must be in PEM format. +
    +
    User Verification Endpoint: + +
    + The URL (starting with http or https) on the JWT authentication server for verifying username and + password credentials. +
    + +
    + Credentials will be sent in the Authorization header as Basic Auth, and this endpoint + should return 200 OK on success (or a 4** otherwise). +
    +
    User Query Endpoint: + +
    + The URL (starting with http or https) on the JWT authentication server for looking up + users based on a prefix query. This is optional. +
    + +
    + The prefix query will be sent as a query parameter with name query. +
    +
    User Lookup Endpoint: + +
    + The URL (starting with http or https) on the JWT authentication server for looking up + a user by username or email address. +
    + +
    + The username or email address will be sent as a query parameter with name username. +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    LDAP URI: + +
    + The full LDAP URI, including the ldap:// or ldaps:// prefix. +
    +
    Base DN: + +
    + A Distinguished Name path which forms the base path for looking up all LDAP records. +
    +
    + Example: dc=my,dc=domain,dc=com +
    +
    User Relative DN: + +
    + A Distinguished Name path which forms the base path for looking up all user LDAP records, + relative to the Base DN defined above. +
    +
    + Example: ou=employees +
    +
    Secondary User Relative DNs: + +
    + A list of Distinguished Name path(s) which forms the secondary base path(s) for + looking up all user LDAP records, relative to the Base DN defined above. These path(s) + will be tried if the user is not found via the primary relative DN. +
    +
    + Example: [ou=employees] +
    +
    Additional User Filter Expression: +
    + NOTE: This query is added unescaped to user + lookups, so be VERY careful with the query you specify. +
    + +
    + If specified, the additional filter used for all user lookup queries. Note that all + Distinguished Names used in the filter must be full paths; the base_dn + is not added automatically here. Must be wrapped in parens. +
    +
    + Example: (someOtherField=someOtherValue) +
    +
    + Example: (memberOf=some.full.path.to.a.group) +
    +
    + Example: (|(someFirstField=someValue)(someOtherField=someOtherValue)) +
    +
    + Example: (&(someFirstField=someValue)(someOtherField=someOtherValue)) +
    +
    Administrator DN: +
    + The Distinguished Name for the Administrator account. This account must be able to login and view + the records for all user accounts. +
    +
    + Example: uid=admin,ou=employees,dc=my,dc=domain,dc=com +
    +
    Administrator DN Password: +
    + Note: This will be stored in + plaintext inside the config.yaml, so setting up a dedicated account or using + a + password hash is highly recommended. +
    + +
    + The password for the Administrator DN. +
    +
    UID Attribute: + +
    + The name of the property field in your LDAP user records that stores your + users' username. Typically "uid". +
    +
    Mail Attribute: + +
    + The name of the property field in your LDAP user records that stores your + users' e-mail address(es). Typically "mail". +
    +
    Custom TLS Certificate: + +
    + If specified, the certificate (in PEM format) for the LDAP TLS connection. +
    +
    Allow insecure: +
    + Allow fallback to non-TLS connections +
    +
    + If enabled, LDAP will fallback to insecure non-TLS connections if TLS does not + succeed. +
    +
    +
    +
    + + +
    +
    + External Authorization (OAuth) +
    +
    + +
    +
    + GitHub (Enterprise) Authentication +
    +
    +
    +

    + If enabled, users can use GitHub or GitHub Enterprise to authenticate to the registry. +

    +

    + Note: A registered GitHub (Enterprise) OAuth application is required. + View instructions on how to + + Create an OAuth Application in GitHub + +

    +
    + +
    + Enable GitHub Authentication +
    + +
    + Warning: This provider is not bound to your {{ config.AUTHENTICATION_TYPE }} + authentication. Logging in via this provider will create a -only user, which is not the recommended approach. It is + highly recommended to choose a "Binding Field" below. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    GitHub: + +
    GitHub Endpoint: + + +
    + The GitHub Enterprise endpoint. Must start with http:// or https://. +
    +
    OAuth Client ID: + + +
    OAuth Client Secret: + + +
    Organization Filtering: +
    + Restrict By Organization Membership +
    + +
    + If enabled, only members of specified GitHub + Enterprise organizations will be + allowed to login via GitHub + Enterprise. +
    + + + +
    Binding Field: + +
    + If selected, when a user logs in via this provider, they will be automatically bound to their + user in {{ config.AUTHENTICATION_TYPE }} by matching the selected field from + the provider to the associated user in {{ config.AUTHENTICATION_TYPE }}. +
    +
    + For example, selecting Subject here with a backing authentication system of LDAP + means that a user logging in via this provider will also be bound to their user in LDAP by + username. +
    +
    + If none selected, a user unique to will be + created on initial login with this provider. This is not the recommended setup. +
    +
    +
    +
    + + +
    +
    + Google Authentication +
    +
    +
    +

    + If enabled, users can use Google to authenticate to the registry. +

    +

    + Note: A registered Google OAuth application is required. + Visit the + + Google Developer Console + + to register an application. +

    +
    + +
    + Enable Google Authentication +
    + +
    + Warning: This provider is not bound to your {{ config.AUTHENTICATION_TYPE }} + authentication. Logging in via this provider will create a -only user, which is not the recommended approach. It is + highly recommended to choose a "Binding Field" below. +
    + + + + + + + + + + + + + + +
    OAuth Client ID: + + +
    OAuth Client Secret: + + +
    Binding Field: + +
    + If selected, when a user logs in via this provider, they will be automatically bound to their + user in {{ config.AUTHENTICATION_TYPE }} by matching the selected field from + the provider to the associated user in {{ config.AUTHENTICATION_TYPE }}. +
    +
    + For example, selecting Subject here with a backing authentication system of LDAP + means that a user logging in via this provider will also be bound to their user in LDAP by + username. +
    +
    + If none selected, a user unique to will be + created on initial login with this provider. This is not the recommended setup. +
    +
    +
    +
    + + +
    +
    + + {{ config[provider]['SERVICE_NAME'] || (getOIDCProviderId(provider) + ' Authentication') }} + (Delete) +
    +
    +
    + Warning: This OIDC provider is not bound to your {{ config.AUTHENTICATION_TYPE }} + authentication. Logging in via this provider will create a -only user, which is not the recommended approach. It is + highly recommended to choose a "Binding Field" below. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Service ID: + {{ getOIDCProviderId(provider) }} +
    OIDC Server: + + +
    + The URL of an OIDC-compliant server. +
    +
    Client ID: + +
    Client Secret: + +
    Service Name: + + +
    + The user friendly name to display for the service on the login page. +
    +
    Service Icon (optional): + + +
    + If specified, the icon to display for this login service on the login page. Can be either a URL + to an icon or a CSS class name from Font + Awesome +
    +
    Verified E-mail Address Claim (optional): + + +
    + If specified, the claim in the User Information JWT that contains the verified e-mail address + for the user. +
    +
    Preferred Username Claim (optional): + + +
    + If specified, the claim in the User Information JWT that contains the preferred username for the + user. +
    +
    Binding Field: + +
    + If selected, when a user logs in via this OIDC provider, they will be automatically bound to + their user in {{ config.AUTHENTICATION_TYPE }} by matching the selected field + from the OIDC provider to the associated user in {{ config.AUTHENTICATION_TYPE }}. +
    +
    + For example, selecting Subject here with a backing authentication system of LDAP + means that a user logging in via this OIDC provider will also be bound to their user in LDAP by + username. +
    +
    + If none selected, a user unique to will be + created on initial login with this OIDC provider. This is not the recommended + setup. +
    +
    Login Scopes: + +
    + If specified, the scopes to send to the OIDC provider when performing the login flow. Note that, + if specified, these scopes will + override those set by default, so this list must include a + scope for OpenID Connect + (typically the openid scope) or this provider will fail. +
    +
    +
    +

    Callback URLs for this service:

    +
      +
    • + {{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback +
    • +
    • + {{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback/attach +
    • +
    • + {{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback/cli +
    • +
    +
    +
    +
    + + + Add OIDC Provider + What is OIDC? +
    +
    + + +
    +
    + Access Settings +
    +
    +
    +

    Various settings around access and authentication to the registry.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Basic Credentials Login: +
    + Login to User Interface via credentials +
    +
    +
    + Login to User Interface via credentials must be enabled. Click here to enable. +
    +
    + Login to User Interface via credentials is enabled (requires at least one OIDC + provider to disable) +
    +
    +
    + If enabled, users will be able to login to the user interface via their username + and password credentials. +
    +
    + If disabled, users will only be able to login to the user + interface via one of the configured External Authentication providers. +
    +
    External Application tokens +
    + Allow external application tokens +
    +
    + If enabled, users will be able to generate external application tokens for use on the Docker and rkt + CLI. Note + that these tokens will not be required unless "App Token" is chosen as the Internal + Authentication method above. +
    +
    External application token expiration + +
    + The expiration time for user generated external application tokens. If none, tokens will never + expire. +
    +
    Anonymous Access: +
    + Enable Anonymous Access +
    +
    + If enabled, public repositories and search can be accessed by anyone that can + reach the registry, even if they are not authenticated. Disable to only allow + authenticated users to view and pull "public" resources. +
    +
    User Creation: +
    + Enable Non-Superuser User Creation +
    +
    + If enabled, user accounts can be created by anyone (unless restricted below to invited users). + Users can always be created in the users panel in this superuser tool, even if this feature is + disabled. If disabled, users can ONLY be created in the superuser tool or + via team sync. +
    +
    Invite-only User Creation: +
    + Enable Invite-only User Creation +
    +
    + If enabled, user accounts can only be created when a user has been invited, by e-mail address, to + join a team. + Users can always be created in the users panel in this superuser tool, even if this feature is + enabled. +
    +
    Encrypted Client Password: +
    + Require Encrypted Client Passwords +
    +
    + If enabled, users will not be able to login from the Docker command + line with a non-encrypted password and must generate an encrypted + password to use. +
    +
    + This feature is highly recommended for setups with external authentication, as + Docker currently stores passwords in plaintext on user's machines. +
    +
    Prefix username autocompletion: +
    + Allow prefix username autocompletion +
    +
    + If disabled, autocompletion for users will only match on exact usernames. +
    +
    Team Invitations: +
    + Require Team Invitations +
    +
    + If enabled, when adding a new user to a team, they will receive an invitation to join the team, with + the option to decline. + Otherwise, users will be immediately part of a team when added by a team administrator. +
    +
    Super Users: + +
    + Users included in this list will be given elevated access to Quay. +
    +
    +
    +
    + + +
    +
    + Dockerfile Build Support +
    +
    +
    + If enabled, users can submit Dockerfile's to be built and pushed by . +
    + +
    + Enable Dockerfile Build +
    + +
    + Note: Build workers are required for this feature. + See Adding Build + Workers for instructions on how to setup build workers. +
    +
    +
    + + +
    +
    + GitHub (Enterprise) Build Triggers +
    +
    +
    +

    + If enabled, users can setup GitHub or GitHub Enterprise triggers to invoke Registry builds. +

    +

    + Note: A registered GitHub (Enterprise) OAuth application (separate from GitHub + Authentication) is required. + View instructions on how to + + Create an OAuth Application in GitHub + +

    +
    + +
    + Enable GitHub Triggers +
    + + + + + + + + + + + + + + + + + + +
    GitHub: + +
    GitHub Endpoint: + + +
    + The GitHub Enterprise endpoint. Must start with http:// or https://. +
    +
    OAuth Client ID: + + +
    OAuth Client Secret: + + +
    +
    +
    + + +
    +
    + BitBucket Build Triggers +
    +
    +
    +

    + If enabled, users can setup BitBucket triggers to invoke Registry builds. +

    +

    + Note: A registered BitBucket OAuth application is required. +

    +
    + +
    + Enable BitBucket Triggers +
    + + + + + + + + + + +
    OAuth Consumer Key: + + +
    OAuth Consumer Secret: + + +
    +
    +
    + + +
    +
    + GitLab Build Triggers +
    +
    +
    +

    + If enabled, users can setup GitLab triggers to invoke Registry builds. +

    +

    + Note: A registered GitLab OAuth application is required. + Visit the + + GitLab applications admin panel + + to create a new application. +

    +

    The callback URL to use is:   + {{ config.PREFERRED_URL_SCHEME || 'http' }}://{{ config.SERVER_HOSTNAME || 'localhost' }}/oauth2/gitlab/callback/trigger +

    +
    + +
    + Enable GitLab Triggers +
    + + + + + + + + + + + + + + + + + + +
    GitLab: + +
    GitLab Endpoint: + + +
    + The GitLab Enterprise endpoint. Must start with http:// or https://. +
    +
    Application Id: + + +
    Secret: + + +
    +
    +
    +
    + + +
    + + +
    + + + + +
    +
    +
    +
    diff --git a/config-tool/pkg/lib/editor/js/core-config-setup/core-config-setup.js b/config-tool/pkg/lib/editor/js/core-config-setup/core-config-setup.js new file mode 100644 index 000000000..6f3c8c633 --- /dev/null +++ b/config-tool/pkg/lib/editor/js/core-config-setup/core-config-setup.js @@ -0,0 +1,1600 @@ +import * as URI from 'urijs'; +import * as angular from 'angular'; +const forge = require('node-forge') +import { X509 } from "jsrsasign" +const JSZip = require('jszip') +const yaml = require('js-yaml') +const uuid = require('uuid') +const FileSaver = require('file-saver') +const templateUrl = require('./config-setup-tool.html'); +const urlParsedField = require('../config-field-templates/config-parsed-field.html'); +const urlVarField = require('../config-field-templates/config-variable-field.html'); +const urlListField = require('../config-field-templates/config-list-field.html'); +const urlFileField = require('../config-field-templates/config-file-field.html'); +const urlBoolField = require('../config-field-templates/config-bool-field.html'); +const urlNumericField = require('../config-field-templates/config-numeric-field.html'); +const urlContactField = require('../config-field-templates/config-contact-field.html'); +const urlContactsField = require('../config-field-templates/config-contacts-field.html'); +const urlMapField = require('../config-field-templates/config-map-field.html'); +const urlServiceKeyField = require('../config-field-templates/config-service-key-field.html'); +const urlStringField = require('../config-field-templates/config-string-field.html'); +const urlPasswordField = require('../config-field-templates/config-password-field.html'); + +const urlStringListField = require('../config-field-templates/config-string-list-field.html'); +const urlCertField = require('../config-field-templates/config-certificates-field.html'); + + +angular.module("quay-config") + .directive('configSetupTool', () => { + var directiveDefinitionObject = { + priority: 1, + templateUrl, + replace: true, + transclude: true, + restrict: 'C', + scope: { + 'isActive': '=isActive', + 'configurationSaved': '&configurationSaved', + 'setupCompleted': '&setupCompleted', + }, + controller: function($rootScope, $scope, $element, $timeout, ApiService) { + $scope.HOSTNAME_REGEX = '^[a-zA-Z-0-9\.]+(:[0-9]+)?$'; + $scope.GITHOST_REGEX = '^https?://([a-zA-Z0-9]+\.?\/?)+$'; + + const readOnlyFieldGroupsCookie = document.cookie.split('; ').find(row => row.startsWith('QuayReadOnlyFieldGroups')); + $scope.readOnlyFieldGroups = new Set(); + if (readOnlyFieldGroupsCookie !== undefined) { + readOnlyFieldGroupsCookie + .split('=')[1] + .split(',') + .forEach(fieldGroup => { + if (fieldGroup) { + $scope.readOnlyFieldGroups.add(fieldGroup.replace(/"/, "")); + } + }); + } + + + $scope.validationMode = "online" + + $scope.fieldGroupReadonly = function(fieldGroup) { + return $scope.readOnlyFieldGroups.has(fieldGroup); + }; + + $scope.changeFieldGroupReadonly = function(fieldGroup, readonly) { + if (readonly) { + $scope.readOnlyFieldGroups.add(fieldGroup); + } else { + $scope.readOnlyFieldGroups.delete(fieldGroup); + } + // FIXME(alecmerdler): Debugging + console.log($scope.readOnlyFieldGroups); + }; + + $scope.STORAGE_CONFIG_FIELDS = { + 'LocalStorage': [ + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/some/directory', 'kind': 'text'} + ], + + 'S3Storage': [ + {'name': 's3_bucket', 'title': 'S3 Bucket', 'placeholder': 'my-cool-bucket', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'}, + {'name': 's3_access_key', 'title': 'AWS Access Key (optional if using IAM)', 'placeholder': 'accesskeyhere', 'kind': 'text', 'optional': true}, + {'name': 's3_secret_key', 'title': 'AWS Secret Key (optional if using IAM)', 'placeholder': 'secretkeyhere', 'kind': 'password', 'optional': true}, + {'name': 'host', 'title': 'S3 Host', 'placeholder': 's3.amazonaws.com', 'kind': 'text', 'optional': true}, + {'name': 'port', 'title': 'S3 Port', 'placeholder': '443', 'kind': 'text', 'pattern': '^[0-9]+$', 'optional': true} + ], + + 'AzureStorage': [ + {'name': 'azure_container', 'title': 'Azure Storage Container', 'placeholder': 'container', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/container', 'kind': 'text'}, + {'name': 'azure_account_name', 'title': 'Azure Account Name', 'placeholder': 'accountnamehere', 'kind': 'text'}, + {'name': 'azure_account_key', 'title': 'Azure Account Key', 'placeholder': 'accountkeyhere', 'kind': 'text', 'optional': true}, + {'name': 'sas_token', 'title': 'Azure SAS Token', 'placeholder': 'sastokenhere', 'kind': 'text', 'optional': true}, + {'name': 'endpoint_url', 'title': 'Azure Storage Endpoint URL', 'placeholder': 'Optional, must include http(s)://', 'kind': 'text', 'optional': true}, + ], + + 'GoogleCloudStorage': [ + {'name': 'access_key', 'title': 'Cloud Access Key', 'placeholder': 'accesskeyhere', 'kind': 'text'}, + {'name': 'secret_key', 'title': 'Cloud Secret Key', 'placeholder': 'secretkeyhere', 'kind': 'text'}, + {'name': 'bucket_name', 'title': 'GCS Bucket', 'placeholder': 'my-cool-bucket', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'} + ], + + 'RHOCSStorage': [ + {'name': 'hostname', 'title': 'NooBaa Server Hostname', 'placeholder': 'my.noobaa.hostname', 'kind': 'text'}, + {'name': 'port', 'title': 'Custom Port (optional)', 'placeholder': '443', 'kind': 'text', 'pattern': '^[0-9]+$', 'optional': true}, + {'name': 'is_secure', 'title': 'Is Secure', 'placeholder': 'Require SSL', 'kind': 'bool'}, + {'name': 'access_key', 'title': 'Access Key', 'placeholder': 'accesskeyhere', 'kind': 'text'}, + {'name': 'secret_key', 'title': 'Secret Key', 'placeholder': 'secretkeyhere', 'kind': 'text'}, + {'name': 'bucket_name', 'title': 'Bucket Name', 'placeholder': 'my-cool-bucket', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'} + ], + + 'RadosGWStorage': [ + {'name': 'hostname', 'title': 'Rados Server Hostname', 'placeholder': 'my.rados.hostname', 'kind': 'text'}, + {'name': 'port', 'title': 'Custom Port (optional)', 'placeholder': '443', 'kind': 'text', 'pattern': '^[0-9]+$', 'optional': true}, + {'name': 'is_secure', 'title': 'Is Secure', 'placeholder': 'Require SSL', 'kind': 'bool'}, + {'name': 'access_key', 'title': 'Access Key', 'placeholder': 'accesskeyhere', 'kind': 'text', 'help_url': 'http://ceph.com/docs/master/radosgw/admin/'}, + {'name': 'secret_key', 'title': 'Secret Key', 'placeholder': 'secretkeyhere', 'kind': 'text'}, + {'name': 'bucket_name', 'title': 'Bucket Name', 'placeholder': 'my-cool-bucket', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'} + ], + + 'SwiftStorage': [ + {'name': 'auth_version', 'title': 'Swift Auth Version', 'kind': 'option', 'values': [1, 2, 3]}, + {'name': 'auth_url', 'title': 'Swift Auth URL', 'placeholder': 'http://swiftdomain/auth/v1.0', 'kind': 'text'}, + {'name': 'swift_container', 'title': 'Swift Container Name', 'placeholder': 'mycontainer', 'kind': 'text', + 'help_text': 'The swift container for all objects. Must already exist inside Swift.'}, + + {'name': 'storage_path', 'title': 'Storage Path', 'placeholder': '/path/inside/container', 'kind': 'text'}, + + {'name': 'swift_user', 'title': 'Username', 'placeholder': 'accesskeyhere', 'kind': 'text', + 'help_text': 'Note: For Swift V1, this is "username:password" (-U on the CLI).'}, + {'name': 'swift_password', 'title': 'Key/Password', 'placeholder': 'secretkeyhere', 'kind': 'text', + 'help_text': 'Note: For Swift V1, this is the API token (-K on the CLI).'}, + + {'name': 'ca_cert_path', 'title': 'CA Cert Filename', 'placeholder': 'conf/stack/swift.cert', 'kind': 'text', 'optional': true}, + + {'name': 'temp_url_key', 'title': 'Temp URL Key (optional)', 'placholder': 'key-here', 'kind': 'text', 'optional': true, + 'help_url': 'https://coreos.com/products/enterprise-registry/docs/latest/swift-temp-url.html', + 'help_text': 'If enabled, will allow for faster pulls directly from Swift.'}, + + {'name': 'os_options', 'title': 'OS Options', 'kind': 'map', + 'keys': ['tenant_id', 'auth_token', 'service_type', 'endpoint_type', 'tenant_name', 'object_storage_url', 'region_name', + 'project_id', 'project_name', 'project_domain_name', 'user_domain_name', 'user_domain_id']} + ], + + 'CloudFrontedS3Storage': [ + {'name': 's3_bucket', 'title': 'S3 Bucket', 'placeholder': 'my-cool-bucket', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'}, + {'name': 's3_access_key', 'title': 'AWS Access Key (optional if using IAM)', 'placeholder': 'accesskeyhere', 'kind': 'text', 'optional': true}, + {'name': 's3_secret_key', 'title': 'AWS Secret Key (optional if using IAM)', 'placeholder': 'secretkeyhere', 'kind': 'text', 'optional': true}, + {'name': 'host', 'title': 'S3 Host', 'placeholder': 's3.amazonaws.com', 'kind': 'text', 'optional': true}, + {'name': 'port', 'title': 'S3 Port', 'placeholder': '443', 'kind': 'text', 'pattern': '^[0-9]+$', 'optional': true}, + + {'name': 'cloudfront_distribution_domain', 'title': 'CloudFront Distribution Domain Name', 'placeholder': 'somesubdomain.cloudfront.net', 'pattern': '^([0-9a-zA-Z]+\\.)+[0-9a-zA-Z]+$', 'kind': 'text'}, + {'name': 'cloudfront_key_id', 'title': 'CloudFront Key ID', 'placeholder': 'APKATHISISAKEYID', 'kind': 'text'}, + {'name': 'cloudfront_privatekey_filename', 'title': 'CloudFront Private Key', 'filesuffix': 'cloudfront-signing-key.pem', 'kind': 'file'}, + ], + 'CloudFlareStorage': [ + {'name': 's3_bucket', 'title': 'S3 Bucket', 'placeholder': 'my-cool-bucket', 'kind': 'text'}, + {'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'}, + {'name': 's3_access_key', 'title': 'AWS Access Key (optional if using IAM)', 'placeholder': 'accesskeyhere', 'kind': 'text', 'optional': true}, + {'name': 's3_secret_key', 'title': 'AWS Secret Key (optional if using IAM)', 'placeholder': 'secretkeyhere', 'kind': 'text', 'optional': true}, + {'name': 'cloudflare_domain', 'title': 'CloudFlare Domain Name', 'placeholder': 'somesubdomain.cloudflare.net', 'pattern': '^([0-9a-zA-Z]+\\.)+[0-9a-zA-Z]+$', 'kind': 'text'}, + {'name': 'cloudflare_privatekey_filename', 'title': 'CloudFlare Private Key', 'filesuffix': 'cloudflare-signing-key.pem', 'kind': 'file'}, + ], + "MultiCDNStorage": [ + {'name': 'providers', 'title': 'sub Providers for multiple CDNs', 'kind': 'map', 'keys': []}, + {'name': 'storage_config', 'title': 'Storage config for the common storage', 'kind': 'map', 'keys': []}, + {'name': 'default_provider', 'title': 'Default provider for storage', 'kind': 'text'}, + {'name': 'rules', 'title': 'rules for routing CDNs', 'kind': 'list', 'optional': true}, + ] + }; + + $scope.enableFeature = function(config, feature) { + config[feature] = true; + }; + + $scope.validateHostname = function(hostname) { + if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) { + return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.'; + } + + return null; + }; + + $scope.config = null; + $scope.originalConfig = null; + $scope.mapped = { + '$hasChanges': false + }; + + $scope.certs = {}; + $scope.savingConfiguration = false; + + $scope.validationStatus = 'none'; + $scope.validationResult = null; + + $scope.operatorEndpoint = document.cookie.includes('QuayOperatorEndpoint'); + + $scope.removeOIDCProvider = function(provider) { + delete $scope.config[provider]; + }; + + $scope.addOIDCProvider = () => { + var result = prompt('Enter an ID for the OIDC provider'); + if (!result) { + return; + } + + result = result.toUpperCase(); + + if (!result.match(/^[A-Z0-9]+$/)) { + alert('Invalid ID for OIDC provider: must be alphanumeric'); + return; + } + + if (result == 'GITHUB' || result == 'GOOGLE') { + alert('Invalid ID for OIDC provider: cannot be a reserved name'); + return; + } + + var key = result + '_LOGIN_CONFIG'; + if ($scope.config[key]) { + alert('Invalid ID for OIDC provider: already exists'); + return; + } + + $scope.config[key] = {}; + }; + + $scope.getOIDCProviderId = function(key) { + var index = key.indexOf('_LOGIN_CONFIG'); + if (index <= 0) { + return null; + } + + return key.substr(0, index).toLowerCase(); + }; + + $scope.getOIDCProviders = function(config) { + var keys = Object.keys(config || {}); + return keys.filter(function(key) { + if (key == 'GITHUB_LOGIN_CONFIG' || key == 'GOOGLE_LOGIN_CONFIG') { + // Has custom UI and config. + return false; + } + + return !!$scope.getOIDCProviderId(key); + }); + }; + + $scope.cancelValidation = function() { + $('#validateAndSaveModal').modal('hide'); + $scope.validationStatus = 'none'; + $scope.savingConfiguration = false; + }; + + var generateDatabaseSecretKey = () => uuid.v4() + + const mergeValidationErrors = (errors) => { + let output = [] + errors.forEach(function (err) { + let existing = output.filter(function (v, i) { + return v.FieldGroup == err.FieldGroup + }) + if (existing.length) { + var existingIndex = output.indexOf(existing[0]); + output[existingIndex].Message = output[existingIndex].Message.concat( + err.Message + ); + } else { + if (typeof err.Message == "string") err.Message = [err.Message]; + output.push(err); + } + }) + return output + } + + const addSetupConfig = function (config) { + config["SETUP_COMPLETE"] = true; + config["SECRET_KEY"] = generateDatabaseSecretKey(); + config["DATABASE_SECRET_KEY"] = generateDatabaseSecretKey(); + config["FEATURE_ACI_CONVERSION"] = false; + config["USE_CDN"] = false; + config["USERFILES_LOCATION"] = config["DISTRIBUTED_STORAGE_PREFERENCE"][0] || Object.keys(config["DISTRIBUTED_STORAGE_CONFIG"])[0] || "default"; + config["TESTING"] = false; + if (!("FEATURE_REQUIRE_TEAM_INVITE" in config)) { + config["FEATURE_REQUIRE_TEAM_INVITE"] = true; + } + if (!("FEATURE_APP_REGISTRY" in config)) { + config["FEATURE_APP_REGISTRY"] = false; + } + if (!("FEATURE_RESTRICTED_V1_PUSH" in config)) { + config["FEATURE_RESTRICTED_V1_PUSH"] = true; + } + if (!("FEATURE_SECURITY_NOTIFICATIONS" in config)) { + config["FEATURE_SECURITY_NOTIFICATIONS"] = true; + } + if (!("LOG_ARCHIVE_LOCATION" in config)) { + config["LOG_ARCHIVE_LOCATION"] = "default"; + } + if (!("ALLOWED_OCI_ARTIFACT_TYPES" in config)){ + config["ALLOWED_OCI_ARTIFACT_TYPES"] = {} + config["ALLOWED_OCI_ARTIFACT_TYPES"][ + "application/vnd.oci.image.config.v1+json" + ] = ["application/vnd.oci.image.layer.v1.tar+zstd"]; + config["ALLOWED_OCI_ARTIFACT_TYPES"][ + "application/vnd.sylabs.sif.config.v1+json" + ] = ["application/vnd.sylabs.sif.layer.v1+tar"]; + } + return config; + } + $scope.validateConfig = function() { + $scope.validationStatus = 'validating'; + + if($scope.certs["database.pem"] && $scope.config["DB_URI"].startsWith("postgres")){ + delete $scope.config["DB_CONNECTION_ARGS"]["ssl"]; + $scope.config["DB_CONNECTION_ARGS"]["sslrootcert"] = "conf/stack/database.pem"; + $scope.config["DB_CONNECTION_ARGS"]["sslmode"] = "verify-full" + } else if + ($scope.certs["database.pem"] && $scope.config["DB_URI"].startsWith("mysql")){ + delete $scope.config["DB_CONNECTION_ARGS"]["sslrootcert"]; + delete $scope.config["DB_CONNECTION_ARGS"]["sslmode"]; + $scope.config["DB_CONNECTION_ARGS"]["ssl"] = {} + $scope.config["DB_CONNECTION_ARGS"]["ssl"]["ca"] = "conf/stack/database.pem"; + } + + var errorDisplay = ApiService.errorDisplay( + 'Could not validate configuration. Please report this error.'); + + ApiService.validateConfigBundle({"config.yaml": $scope.config, "certs": $scope.certs, readOnlyFieldGroups: $scope.readOnlyFieldGroups}, $scope.validationMode).then(function(resp) { + $scope.validationStatus = resp.data.length == 0 ? 'success' : 'error'; + $scope.validationResult = mergeValidationErrors(resp.data); + if($scope.validationStatus == 'success' && $scope.validationMode == 'setup'){ + $scope.config = addSetupConfig($scope.config) + } + }, errorDisplay); + }; + + + + $scope.operatorCommitStatus = 'none' + $scope.commitToOperator = function() { + + $scope.operatorCommitStatus = "inProgress" + + ApiService.commitToOperator({ + "config.yaml": $scope.config, + certs: $scope.certs, + managedFieldGroups: $scope.readOnlyFieldGroups, + }).then(function (resp) { + $scope.operatorCommitStatus = "success"; + }, (resp) => { + console.log(resp) + $scope.operatorCommitStatus = "error" + }); + } + + $scope.downloadConfigBundle = function() { + ApiService.downloadConfigBundle({"config.yaml": $scope.config, "certs": $scope.certs, readOnlyFieldGroups: $scope.readOnlyFieldGroups}).then(function(resp) { + FileSaver.saveAs(resp.data, "quay-config.tar.gz") + }, console.log("failed error")) + } + + $scope.checkValidateAndSave = function() { + + if ($scope.configform.$valid) { + saveStorageConfig(); + $scope.validateAndSave(); + return; + } + + var query = $.find(".ng-invalid"); + + console.log(query) + if (query && query.length) { + query[1].scrollIntoView(); + query[1].focus(); + } + }; + + $scope.validateAndSave = function() { + $scope.savingConfiguration = false; + + $('#validateAndSaveModal').modal({ + keyboard: false, + backdrop: 'static' + }); + + $scope.validateConfig(); + }; + + $scope.saveConfiguration = function() { + $scope.savingConfiguration = true; + + // Make sure to note that fully verified setup is completed. We use this as a signal + // in the setup tool. + $scope.config['SETUP_COMPLETE'] = true; + + var data = { + 'config': $scope.config, + 'hostname': window.location.host, + }; + + var errorDisplay = ApiService.errorDisplay( + 'Could not save configuration. Please report this error.'); + + ApiService.scUpdateConfig(data).then(function(resp) { + $scope.savingConfiguration = false; + $scope.mapped.$hasChanges = false; + + $('#validateAndSaveModal').modal('hide'); + + $scope.setupCompleted(); + }, errorDisplay); + }; + + // Convert storage config to an array + var initializeStorageConfig = function($scope) { + var config = $scope.config.DISTRIBUTED_STORAGE_CONFIG || {}; + var defaultLocations = $scope.config.DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS || []; + var preference = $scope.config.DISTRIBUTED_STORAGE_PREFERENCE || []; + + $scope.serverStorageConfig = angular.copy(config); + $scope.storageConfig = []; + + Object.keys(config).forEach(function(location) { + $scope.storageConfig.push({ + location: location, + defaultLocation: defaultLocations.indexOf(location) >= 0, + data: angular.copy(config[location]), + error: {}, + }); + }); + + if (!$scope.storageConfig.length) { + $scope.addStorageConfig('default'); + return; + } + + // match DISTRIBUTED_STORAGE_PREFERENCE order first, remaining are + // ordered by unicode point value + $scope.storageConfig.sort(function(a, b) { + var indexA = preference.indexOf(a.location); + var indexB = preference.indexOf(b.location); + + if (indexA > -1 && indexB > -1) return indexA < indexB ? -1 : 1; + if (indexA > -1) return -1; + if (indexB > -1) return 1; + + return a.location < b.location ? -1 : 1; + }); + }; + + var parseDbUri = function(value) { + if (!value) { return null; } + + // Format: mysql+pymysql://:@/ + var uri = URI(value); + return { + 'kind': uri.protocol(), + 'username': uri.username(), + 'password': uri.password(), + 'server': uri.host(), + 'database': uri.path() ? uri.path().substr(1) : '' + }; + }; + + $scope.allowChangeLocationStorageConfig = function(location) { + if (!$scope.serverStorageConfig[location]) { return true }; + + // allow user to change location ID if another exists with the same ID + return $scope.storageConfig.filter(function(sc) { + return sc.location === location; + }).length >= 2; + }; + + $scope.allowRemoveStorageConfig = function(location) { + return $scope.storageConfig.length > 1 && $scope.allowChangeLocationStorageConfig(location); + }; + + $scope.canAddStorageConfig = function() { + return $scope.config && + $scope.config.FEATURE_STORAGE_REPLICATION && + $scope.storageConfig && + (!$scope.storageConfig.length || $scope.storageConfig.length < 10); + }; + + $scope.addStorageConfig = function(location) { + var storageType = 'LocalStorage'; + + // Use last storage type by default + if ($scope.storageConfig.length) { + storageType = $scope.storageConfig[$scope.storageConfig.length-1].data[0]; + } + + $scope.storageConfig.push({ + location: location || '', + defaultLocation: false, + data: [storageType, {storage_path: "/datastorage/registry"}], + error: {}, + }); + }; + + $scope.removeStorageConfig = function(sc) { + $scope.storageConfig.splice($scope.storageConfig.indexOf(sc), 1); + }; + + var saveStorageConfig = function() { + var config = {}; + var defaultLocations = []; + var preference = []; + + $scope.storageConfig.forEach(function(sc) { + config[sc.location] = sc.data; + if (sc.defaultLocation) defaultLocations.push(sc.location); + preference.push(sc.location); + }); + + $scope.config.DISTRIBUTED_STORAGE_CONFIG = config; + $scope.config.DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS = defaultLocations; + $scope.config.DISTRIBUTED_STORAGE_PREFERENCE = preference; + }; + + var gitlabSelector = function(key) { + return function(value) { + if (!value || !$scope.config) { return; } + + if (!$scope.config[key]) { + $scope.config[key] = {}; + } + + if (value == 'enterprise') { + if ($scope.config[key]['GITLAB_ENDPOINT'] == 'https://gitlab.com/') { + $scope.config[key]['GITLAB_ENDPOINT'] = ''; + } + } else if (value == 'hosted') { + $scope.config[key]['GITLAB_ENDPOINT'] = 'https://gitlab.com/'; + } + }; + }; + + var githubSelector = function(key) { + return function(value) { + if (!value || !$scope.config) { return; } + + if (!$scope.config[key]) { + $scope.config[key] = {}; + } + + if (value == 'enterprise') { + if ($scope.config[key]['GITHUB_ENDPOINT'] == 'https://github.com/') { + $scope.config[key]['GITHUB_ENDPOINT'] = ''; + } + delete $scope.config[key]['API_ENDPOINT']; + } else if (value == 'hosted') { + $scope.config[key]['GITHUB_ENDPOINT'] = 'https://github.com/'; + $scope.config[key]['API_ENDPOINT'] = 'https://api.github.com/'; + } + }; + }; + + var getKey = function(config, path) { + if (!config) { + return null; + } + + var parts = path.split('.'); + var current = config; + for (var i = 0; i < parts.length; ++i) { + var part = parts[i]; + if (!current[part]) { return null; } + current = current[part]; + } + return current; + }; + + var initializeMappedLogic = function(config) { + var gle = getKey(config, 'GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT'); + var gte = getKey(config, 'GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT'); + + $scope.mapped['GITHUB_LOGIN_KIND'] = gle == 'https://github.com/' ? 'hosted' : 'enterprise'; + $scope.mapped['GITHUB_TRIGGER_KIND'] = gte == 'https://github.com/' ? 'hosted' : 'enterprise'; + + var glabe = getKey(config, 'GITLAB_TRIGGER_KIND.GITHUB_ENDPOINT'); + $scope.mapped['GITLAB_TRIGGER_KIND'] = glabe == 'https://gitlab.com/' ? 'hosted' : 'enterprise'; + + $scope.mapped['redis'] = {}; + $scope.mapped['redis']['host'] = getKey(config, 'BUILDLOGS_REDIS.host') || getKey(config, 'USER_EVENTS_REDIS.host'); + $scope.mapped['redis']['port'] = getKey(config, 'BUILDLOGS_REDIS.port') || getKey(config, 'USER_EVENTS_REDIS.port'); + $scope.mapped['redis']['password'] = getKey(config, 'BUILDLOGS_REDIS.password') || getKey(config, 'USER_EVENTS_REDIS.password'); + + $scope.mapped['TLS_SETTING'] = 'none'; + if (config['PREFERRED_URL_SCHEME'] == 'https') { + if (config['EXTERNAL_TLS_TERMINATION'] === true) { + $scope.mapped['TLS_SETTING'] = 'external-tls'; + } else { + $scope.mapped['TLS_SETTING'] = 'internal-tls'; + } + } + + $scope.mapped['LOGS_MODEL_CONFIG'] = {}; + $scope.mapped['LOGS_MODEL'] = config['LOGS_MODEL'] || 'database'; + if (config['LOGS_MODEL'] == 'elasticsearch') { + $scope.mapped['LOGS_MODEL_CONFIG']['producer'] = config['LOGS_MODEL_CONFIG']['producer'] || 'elasticsearch'; + + if (config['LOGS_MODEL_CONFIG']['kinesis_stream_config']) { + $scope.mapped['LOGS_MODEL_CONFIG']['kinesis_stream_config'] = config['LOGS_MODEL_CONFIG']['kinesis_stream_config']; + } + + if (config['LOGS_MODEL_CONFIG']['elasticsearch_config']) { + $scope.mapped['LOGS_MODEL_CONFIG']['elasticsearch_config'] = config['LOGS_MODEL_CONFIG']['elasticsearch_config']; + } + } + + $scope.mapped['database'] = {} + $scope.mapped['database'] = parseDbUri(getKey(config, "DB_URI")) + console.log($scope.mapped['database']) + + }; + + var tlsSetter = function(value) { + if (value == null || !$scope.config) { return; } + + switch (value) { + case 'none': + $scope.config['PREFERRED_URL_SCHEME'] = 'http'; + delete $scope.certs["ssl.key"] + delete $scope.certs["ssl.cert"] + delete $scope.config['EXTERNAL_TLS_TERMINATION']; + return; + + case 'external-tls': + $scope.config['PREFERRED_URL_SCHEME'] = 'https'; + $scope.config['EXTERNAL_TLS_TERMINATION'] = true; + delete $scope.certs["ssl.key"]; + delete $scope.certs["ssl.cert"]; + return; + + case 'internal-tls': + $scope.config['PREFERRED_URL_SCHEME'] = 'https'; + $scope.config['EXTERNAL_TLS_TERMINATION'] = false; + return; + } + }; + + var redisSetter = function(keyname) { + + return function(value) { + + console.log("changing redis ") + + + if (value == null || !$scope.config) { return; } + + if (!$scope.config['BUILDLOGS_REDIS']) { + $scope.config['BUILDLOGS_REDIS'] = {}; + } + + if (!$scope.config['USER_EVENTS_REDIS']) { + $scope.config['USER_EVENTS_REDIS'] = {}; + } + + if (!value) { + delete $scope.config['BUILDLOGS_REDIS'][keyname]; + delete $scope.config['USER_EVENTS_REDIS'][keyname]; + return; + } + + $scope.config['BUILDLOGS_REDIS'][keyname] = value; + $scope.config['USER_EVENTS_REDIS'][keyname] = value; + }; + }; + + var databaseSetter = function(fields) { + if (fields == null || !$scope.config) { return; } + + if (!fields['server']) { return ''; } + if (!fields['database']) { return ''; } + + var uri = URI(); + try { + uri = uri && uri.host(fields['server']); + uri = uri && uri.protocol(fields['kind']); + uri = uri && uri.username(fields['username']); + uri = uri && uri.password(fields['password']); + uri = uri && uri.path('/' + (fields['database'] || '')); + uri = uri && uri.toString(); + } catch (ex) { + return ''; + } + + $scope.config['DB_URI'] = uri + + }; + + var logsModelSelector = function(keyname) { + return function(value) { + if (!$scope.config) { return; } + + if (!value) { $scope.config['LOGS_MODEL'] = 'database'; }; + + if (value == 'elasticsearch') { + $scope.config['LOGS_MODEL'] = 'elasticsearch'; + if (!$scope.config['LOGS_MODEL_CONFIG']) { + $scope.config['LOGS_MODEL_CONFIG'] = {}; + } + if (!$scope.config['LOGS_MODEL_CONFIG']['elasticsearch_config']) { + $scope.config['LOGS_MODEL_CONFIG']['elasticsearch_config'] = {}; + } + if (!$scope.config['LOGS_MODEL_CONFIG']['producer']) { + $scope.mapped['LOGS_MODEL_CONFIG']['producer'] = 'elasticsearch'; + $scope.config['LOGS_MODEL_CONFIG']['producer'] = 'elasticsearch'; + } + } else if (value == 'database') { + $scope.config['LOGS_MODEL'] = 'database'; + $scope.mapped['LOGS_MODEL_CONFIG'] = {}; + $scope.config['LOGS_MODEL_CONFIG'] = {}; + } + }; + }; + + var logsProducerSetter = function(value) { + if (value == null || !$scope.config ) { return; } + + if (value == 'kinesis_stream') { + if (!$scope.config['LOGS_MODEL_CONFIG']['kinesis_stream_config']) { + $scope.config['LOGS_MODEL_CONFIG']['kinesis_stream_config'] = {}; + } + } else { + delete $scope.mapped['LOGS_MODEL_CONFIG']['kinesis_stream_config']; + delete $scope.config['LOGS_MODEL_CONFIG']['kinesis_stream_config']; + } + + $scope.config['LOGS_MODEL_CONFIG']['producer'] = value; + }; + + var logsModelConfigSetter = function(keyname, configName) { + return function(value) { + if (value == null || !$scope.config ) { return; } + + if (!$scope.config['LOGS_MODEL_CONFIG'][configName]) { + $scope.config['LOGS_MODEL_CONFIG'][configName] = {}; + } + + if (!value) { + delete $scope.config['LOGS_MODEL_CONFIG'][configName][keyname]; + } + + $scope.config['LOGS_MODEL_CONFIG'][configName][keyname] = value; + }; + }; + + // Add mapped logic. + $scope.$watch('mapped.GITHUB_LOGIN_KIND', githubSelector('GITHUB_LOGIN_CONFIG')); + $scope.$watch('mapped.GITHUB_TRIGGER_KIND', githubSelector('GITHUB_TRIGGER_CONFIG')); + $scope.$watch('mapped.GITLAB_TRIGGER_KIND', gitlabSelector('GITLAB_TRIGGER_KIND')); + $scope.$watch('mapped.TLS_SETTING', tlsSetter); + + $scope.$watch('mapped.redis.host', redisSetter('host')); + $scope.$watch('mapped.redis.port', redisSetter('port')); + $scope.$watch('mapped.redis.password', redisSetter('password')); + + $scope.$watch('mapped.database', databaseSetter, true); + + $scope.$watch('mapped.LOGS_MODEL', logsModelSelector('LOGS_MODEL')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.producer', logsProducerSetter); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.elasticsearch_config.host', logsModelConfigSetter('host', 'elasticsearch_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.elasticsearch_config.port', logsModelConfigSetter('port', 'elasticsearch_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.elasticsearch_config.access_key', logsModelConfigSetter('access_key', 'elasticsearch_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.elasticsearch_config.secret_key', logsModelConfigSetter('secret_key', 'elasticsearch_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.elasticsearch_config.aws_region', logsModelConfigSetter('aws_region', 'elasticsearch_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.elasticsearch_config.index_prefix', logsModelConfigSetter('index_prefix', 'elasticsearch_config')); + + $scope.$watch('mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.aws_access_key', logsModelConfigSetter('aws_access_key', 'kinesis_stream_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.aws_secret_key', logsModelConfigSetter('aws_secret_key', 'kinesis_stream_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.aws_region', logsModelConfigSetter('aws_region', 'kinesis_stream_config')); + $scope.$watch('mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.stream_name', logsModelConfigSetter('stream_name', 'kinesis_stream_config')); + + // Remove extra extra fields (which are not allowed) from storage config. + var updateFields = function(sc) { + var type = sc.data[0]; + var configObject = sc.data[1]; + var allowedFields = $scope.STORAGE_CONFIG_FIELDS[type]; + + // Remove any fields not allowed. + for (var fieldName in configObject) { + if (!configObject.hasOwnProperty(fieldName)) { + continue; + } + + var isValidField = $.grep(allowedFields, function(field) { + return field.name == fieldName; + }).length > 0; + + if (!isValidField) { + delete configObject[fieldName]; + } + } + + // Set any missing boolean fields to false. + for (var i = 0; i < allowedFields.length; ++i) { + if (allowedFields[i].kind == 'bool') { + configObject[allowedFields[i].name] = configObject[allowedFields[i].name] || false; + } + } + }; + + var generateRandomString = () => Math.random().toString(20).substr(2, 2048) + + $scope.generateClairPSK = function() { + $scope.config['SECURITY_SCANNER_V4_PSK'] = btoa(generateRandomString()) + } + + // Validate and update storage config on update. + var refreshStorageConfig = function() { + if (!$scope.config || !$scope.storageConfig) return; + + var locationCounts = {}; + var errors = []; + var valid = true; + + $scope.storageConfig.forEach(function(sc) { + // remove extra fields from storage config + updateFields(sc); + + if (!locationCounts[sc.location]) locationCounts[sc.location] = 0; + locationCounts[sc.location]++; + }); + + // validate storage config + $scope.storageConfig.forEach(function(sc) { + var error = {}; + + if ($scope.config.FEATURE_STORAGE_REPLICATION && sc.data[0] === 'LocalStorage') { + error.engine = 'Replication to a locally mounted directory is unsupported as it is only accessible on a single machine.'; + valid = false; + } + + if (locationCounts[sc.location] > 1) { + error.location = 'Location ID must be unique.'; + valid = false; + } + + errors.push(error); + }); + + $scope.storageConfigError = errors; + $scope.configform.$setValidity('storageConfig', valid); + }; + + $scope.$watch('config.INTERNAL_OIDC_SERVICE_ID', function(service_id) { + if (service_id) { + $scope.config['FEATURE_DIRECT_LOGIN'] = false; + } + }); + + $scope.$watch('config.FEATURE_STORAGE_REPLICATION', function() { + refreshStorageConfig(); + }); + + $scope.$watch('config.FEATURE_USER_CREATION', function(value) { + if (!value && $scope.config) { + $scope.config['FEATURE_INVITE_ONLY_USER_CREATION'] = false; + } + }); + + $scope.$watch('config.LOGS_MODEL', function(value) { + if (!value && $scope.config) { + $scope.config['LOGS_MODEL'] = 'database'; + } + }); + + $scope.$watch('storageConfig', function() { + refreshStorageConfig(); + }, true); + + $scope.$watch('config', function(value) { + $scope.mapped['$hasChanges'] = true; + }, true); + + $scope.$watch('isActive', function(value) { + if (!value) { return; } + + ApiService.getMountedConfigBundle().then(function(resp) { + console.log("resp",resp) + $scope.config = resp.data["config.yaml"] || {}; + $scope.certs = resp.data["certs"] || {}; + $scope.originalConfig = Object.assign({}, resp.data["config.yaml"] || {});; + initializeMappedLogic($scope.config); + initializeStorageConfig($scope); + $scope.mapped['$hasChanges'] = false; + if(resp.status == 202){ + $scope.validationMode = "setup" + } + }, ApiService.errorDisplay('Could not load config')); + }); + } + }; + + return directiveDefinitionObject; + }) + + .directive('configParsedField', function ($timeout) { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlParsedField, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'binding': '=binding', + 'parser': '&parser', + 'serializer': '&serializer' + }, + controller: function($scope, $element, $transclude) { + $scope.childScope = null; + + $transclude(function(clone, scope) { + $scope.childScope = scope; + $scope.childScope['fields'] = {}; + $element.append(clone); + }); + + $scope.childScope.$watch('fields', function(value) { + // Note: We need the timeout here because Angular starts the digest of the + // parent scope AFTER the child scope, which means it can end up one action + // behind. The timeout ensures that the parent scope will be fully digest-ed + // and then we update the binding. Yes, this is a hack :-/. + $timeout(function() { + $scope.binding = $scope.serializer({'fields': value}); + }); + }, true); + + $scope.$watch('binding', function(value) { + var parsed = $scope.parser({'value': value}); + for (var key in parsed) { + if (parsed.hasOwnProperty(key)) { + $scope.childScope['fields'][key] = parsed[key]; + } + } + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configVariableField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlVarField, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'binding': '=binding' + }, + controller: function($scope, $element) { + $scope.sections = {}; + $scope.currentSection = null; + + $scope.setSection = function(section) { + $scope.binding = section.value; + }; + + this.addSection = function(section, element) { + $scope.sections[section.value] = { + 'title': section.valueTitle, + 'value': section.value, + 'element': element + }; + + element.hide(); + + if (!$scope.binding) { + $scope.binding = section.value; + } + }; + + $scope.$watch('binding', function(binding) { + if (!binding) { return; } + + if ($scope.currentSection) { + $scope.currentSection.element.hide(); + } + + if ($scope.sections[binding]) { + $scope.sections[binding].element.show(); + $scope.currentSection = $scope.sections[binding]; + } + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('variableSection', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlVarField, + priority: 1, + require: '^configVariableField', + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'value': '@value', + 'valueTitle': '@valueTitle' + }, + controller: function($scope, $element) { + var parentCtrl = $element.parent().controller('configVariableField'); + parentCtrl.addSection($scope, $element); + } + }; + return directiveDefinitionObject; + }) + + .directive('configListField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlListField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding', + 'placeholder': '@placeholder', + 'defaultValue': '@defaultValue', + 'itemTitle': '@itemTitle', + 'itemPattern': '@itemPattern' + }, + controller: function($scope, $element) { + $scope.removeItem = function(item) { + var index = $scope.binding.indexOf(item); + if (index >= 0) { + $scope.binding.splice(index, 1); + } + }; + + $scope.addItem = function() { + if (!$scope.newItemName) { + return; + } + + if (!$scope.binding) { + $scope.binding = []; + } + + if ($scope.binding.indexOf($scope.newItemName) >= 0) { + return; + } + + $scope.binding.push($scope.newItemName); + $scope.newItemName = null; + }; + + $scope.patternMap = {}; + + $scope.getRegexp = function(pattern) { + if (!pattern) { + pattern = '.*'; + } + + if ($scope.patternMap[pattern]) { + return $scope.patternMap[pattern]; + } + + return $scope.patternMap[pattern] = new RegExp(pattern); + }; + + $scope.$watch('binding', function(binding) { + if (!binding && $scope.defaultValue) { + $scope.binding = eval($scope.defaultValue); + } + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configFileField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlFileField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'filename': '@filename', + 'skipCheckFile': '@skipCheckFile', + 'binding': '=?binding', + 'isReadonly': '=isReadonly', + 'certs': '=certs' + }, + controller: function($scope, $element, Restangular) { + $scope.hasFile = false; + $scope.uploadProgress = null; + + const checkHasFile = () => { + if ($scope.filename in $scope.certs) { + $scope.hasFile = true; + } + } + $scope.$watch("certs", checkHasFile, true); + + $scope.onFileSelect = function(files) { + if (files.length < 1) { + $scope.hasFile = false; + return; + } + conductUpload(files[0]) + }; + + var conductUpload = function(file) { + var reader = new FileReader(); + reader.readAsText(file); + + reader.onprogress = function (e) { + $scope.$apply(function () { + if (e.lengthComputable) { + $scope.uploadProgress = (e.loaded / e.total) * 100; + } + }); + }; + + reader.onload = function (e) { + $scope.$apply(function () { + try { + $scope.certs[$scope.filename] = btoa(e.target.result); + $scope.hasFile = true; + $scope.uploadProgress = null; + $scope.binding = $scope.filename; + } catch (err) { + $scope.hasFile = false; + $scope.uploadProgress = null; + } + }); + }; + + reader.onerror = function (e) { + $scope.$apply(function () { + doneCb(false, "Error when uploading"); + $scope.hasFile = false; + $scope.uploadProgress = null; + }); + }; + + + + }; + + } + }; + return directiveDefinitionObject; + }) + + .directive('configBoolField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlBoolField, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'binding': '=binding', + 'isReadonly': '=?isReadonly', + }, + controller: function($scope, $element) { + } + }; + return directiveDefinitionObject; + }) + + .directive('configNumericField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlNumericField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding', + 'placeholder': '@placeholder', + 'defaultValue': '@defaultValue', + 'isReadonly': '=?isReadonly', + }, + controller: function($scope, $element) { + $scope.bindinginternal = 0; + + $scope.$watch('binding', function(binding) { + if ($scope.binding == 0 && $scope.defaultValue) { + $scope.binding = $scope.defaultValue * 1; + } + + $scope.bindinginternal = $scope.binding; + }); + + $scope.$watch('bindinginternal', function(binding) { + var newValue = $scope.bindinginternal * 1; + if (isNaN(newValue)) { + newValue = 0; + } + $scope.binding = newValue; + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configContactsField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlContactsField, + priority: 1, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding' + }, + controller: function($scope, $element) { + var padItems = function(items) { + // Remove the last item if both it and the second to last items are empty. + if (items.length > 1 && !items[items.length - 2].value && !items[items.length - 1].value) { + items.splice(items.length - 1, 1); + return; + } + + // If the last item is non-empty, add a new item. + if (items.length == 0 || items[items.length - 1].value) { + items.push({'value': ''}); + return; + } + }; + + $scope.itemHash = null; + $scope.$watch('items', function(items) { + if (!items) { return; } + padItems(items); + + var itemHash = ''; + var binding = []; + for (var i = 0; i < items.length; ++i) { + var item = items[i]; + if (item.value && (URI(item.value).host() || URI(item.value).path())) { + binding.push(item.value); + itemHash += item.value; + } + } + + $scope.itemHash = itemHash; + $scope.binding = binding; + }, true); + + $scope.$watch('binding', function(binding) { + var current = binding || []; + var items = []; + var itemHash = ''; + for (var i = 0; i < current.length; ++i) { + items.push({'value': current[i]}) + itemHash += current[i]; + } + + if ($scope.itemHash != itemHash) { + $scope.items = items; + } + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configContactField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlContactField, + priority: 1, + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'binding': '=binding' + }, + controller: function($scope, $element) { + $scope.kind = null; + $scope.value = null; + + var updateBinding = function() { + if ($scope.value == null) { return; } + var value = $scope.value || ''; + + switch ($scope.kind) { + case 'mailto': + $scope.binding = 'mailto:' + value; + return; + + case 'tel': + $scope.binding = 'tel:' + value; + return; + + case 'irc': + $scope.binding = 'irc://' + value; + return; + + default: + $scope.binding = value; + return; + } + }; + + $scope.$watch('kind', updateBinding); + $scope.$watch('value', updateBinding); + + $scope.$watch('binding', function(value) { + if (!value) { + $scope.kind = null; + $scope.value = null; + return; + } + + var uri = URI(value); + $scope.kind = uri.scheme(); + + switch ($scope.kind) { + case 'mailto': + case 'tel': + $scope.value = uri.path(); + break; + + case 'irc': + $scope.value = value.substr('irc://'.length); + break; + + default: + $scope.kind = 'http'; + $scope.value = value; + break; + } + }); + + $scope.getPlaceholder = function(kind) { + switch (kind) { + case 'mailto': + return 'some@example.com'; + + case 'tel': + return '555-555-5555'; + + case 'irc': + return 'myserver:port/somechannel'; + + default: + return 'http://some/url'; + } + }; + } + }; + return directiveDefinitionObject; + }) + + .directive('configMapField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlMapField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding', + 'keys': '=keys' + }, + controller: function($scope, $element) { + $scope.newKey = null; + $scope.newValue = null; + + $scope.hasValues = function(binding) { + return binding && Object.keys(binding).length; + }; + + $scope.removeKey = function(key) { + delete $scope.binding[key]; + }; + + $scope.addEntry = function() { + if (!$scope.newKey || !$scope.newValue) { return; } + + $scope.binding = $scope.binding || {}; + $scope.binding[$scope.newKey] = $scope.newValue; + $scope.newKey = null; + $scope.newValue = null; + } + } + }; + return directiveDefinitionObject; + }) + + .directive('configStringField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlStringField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding', + 'placeholder': '@placeholder', + 'pattern': '@pattern', + 'defaultValue': '@defaultValue', + 'validator': '&validator', + 'isOptional': '=isOptional', + 'isReadonly': '=?isReadonly' + }, + controller: function($scope, $element) { + var firstSet = true; + + $scope.patternMap = {}; + + $scope.getRegexp = function(pattern) { + if (!pattern) { + pattern = '.*'; + } + + if ($scope.patternMap[pattern]) { + return $scope.patternMap[pattern]; + } + + return $scope.patternMap[pattern] = new RegExp(pattern); + }; + + $scope.$watch('binding', function(binding) { + if (firstSet && !binding && $scope.defaultValue) { + $scope.binding = $scope.defaultValue; + firstSet = false; + } + + $scope.errorMessage = $scope.validator({'value': binding || ''}); + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configPasswordField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlPasswordField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding', + 'placeholder': '@placeholder', + 'defaultValue': '@defaultValue', + 'validator': '&validator', + 'isOptional': '=isOptional', + 'isReadonly': '=?isReadonly', + }, + controller: function($scope, $element) { + var firstSet = true; + + $scope.$watch('binding', function(binding) { + if (firstSet && !binding && $scope.defaultValue) { + $scope.binding = $scope.defaultValue; + firstSet = false; + } + $scope.errorMessage = $scope.validator({'value': binding || ''}); + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configStringListField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlStringListField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'binding': '=binding', + 'itemTitle': '@itemTitle', + 'itemDelimiter': '@itemDelimiter', + 'placeholder': '@placeholder', + 'isOptional': '=isOptional', + 'isReadonly': '=?isReadonly', + }, + controller: function($scope, $element) { + $scope.$watch('internalBinding', function(value) { + if (value) { + $scope.binding = value.split($scope.itemDelimiter); + } + }); + + $scope.$watch('binding', function(value) { + if (value) { + $scope.internalBinding = value.join($scope.itemDelimiter); + } + }); + } + }; + return directiveDefinitionObject; + }) + + .directive('configCertificatesField', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: urlCertField, + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'certs': '=certs', + }, + controller: function($scope, $element, ApiService) { + $scope.certsUploading = false; + $scope.certMeta = [] + + // Reads the certs stored in scope and creates a new object with metadata to render table + var loadCertificateMeta = function () { + $scope.certsUploading = true; + $scope.certMeta = Object.entries($scope.certs) + .filter(([filename, contents]) => + filename.startsWith("extra_ca_cert") + ) + .map(([filename, contents]) => { + try { + const cert = forge.pki.certificateFromPem(atob(contents)); + const current = new Date(); + const expired = current > cert.validity.notAfter; + return { + path: filename, + names: getCertNames(cert), + expired: expired, + error: null, + }; + } catch (err) { + // Retry with new library + try { + const c = new X509(); + c.readCertPEM(atob(contents)); + const current = new Date(); + // This function returns a bad string, so we have to do some extra formatting to normalize it. + let originalDateString = "20"+c.getNotAfter() + let formattedDateString = originalDateString.replace( + /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, + "$1-$2-$3T$4:$5:$6" + ); + const expired = current > Date.parse(formattedDateString); + let names = c + .getSubjectString() + .split("/") + .filter((attr) => attr.startsWith("CN")) + .map((cn) => cn.split("=")[1]); + if (c.getExtSubjectAltName2() != undefined) { + names.concat(c.getExtSubjectAltName2().map((v) => v[1])); + } + return { + path: filename, + names: names, + expired: expired, + error: null, + }; + } catch(_) { + return { + path: filename, + names: [], + expired: null, + error: err, + }; + } + } + }); + $scope.certsUploading = false; + + } + + // Gets the common names for a given cert + var getCertNames = function(cert) { + let cn = [] + cert.issuer.attributes.forEach(function(attr){ + if(attr.shortName == "CN"){ + cn.push(attr.value) + } + }) + return cn + } + + $scope.deleteCert = function (certPath) { + delete $scope.certs[certPath] + } + + $scope.$watch('certs', loadCertificateMeta, true) + + } + }; + return directiveDefinitionObject; + }); diff --git a/config-tool/pkg/lib/editor/js/main.ts b/config-tool/pkg/lib/editor/js/main.ts new file mode 100644 index 000000000..eaf5ad51e --- /dev/null +++ b/config-tool/pkg/lib/editor/js/main.ts @@ -0,0 +1,28 @@ +// imports shims, etc +import "core-js"; +import * as $ from 'jquery'; +import * as angular from "angular"; +import { ConfigAppModule } from "./config-app.module"; +import { bundle } from "ng-metadata/core"; + +import "bootstrap"; + +window.$ = $; +window.jQuery = jQuery; + +const ng1QuayModule: string = bundle(ConfigAppModule, []).name; +angular.module("quay-config", [ng1QuayModule]).run(() => {}); + +declare var require: any; +function requireAll(r) { + r.keys().forEach(r); +} + +// load all services +requireAll(require.context("./services", true, /\.js$/)); + +// load all the components after services +requireAll(require.context("./core-config-setup", true, /\.js$/)); +requireAll(require.context("./components", true, /\.js$/)); + +//requireAll(require.context('../static/css', true, /\.css$/)); diff --git a/config-tool/pkg/lib/editor/js/services/api-service.js b/config-tool/pkg/lib/editor/js/services/api-service.js new file mode 100644 index 000000000..b980cb93d --- /dev/null +++ b/config-tool/pkg/lib/editor/js/services/api-service.js @@ -0,0 +1,61 @@ +/** + * Service which exposes the server-defined API as a nice set of helper methods and automatic + * callbacks. + */ +angular.module("quay-config").factory("ApiService", [ + "Restangular", + "$q", + "UtilService", + function (Restangular, $q, UtilService) { + var apiService = {}; + Restangular.setFullResponse(true) + + apiService.getMountedConfigBundle = function () { + return Restangular.one("config").get(); + }; + + apiService.downloadConfigBundle = function (configBundle) { + return Restangular.one("config/download").withHttpConfig({responseType: 'blob'}).post(null, configBundle); + }; + + apiService.validateConfigBundle = function (configBundle, validationMode) { + return Restangular.one("config/validate?mode="+validationMode).post(null, configBundle); + }; + + apiService.commitToOperator = function (configBundle) { + return Restangular.one("config/operator").post(null, configBundle); + }; + + apiService.getErrorMessage = function (resp, defaultMessage) { + var message = defaultMessage; + if (resp && resp["data"]) { + // TODO: remove error_message and error_description (old style error) + message = + resp["data"]["detail"] || + resp["data"]["error_message"] || + resp["data"]["message"] || + resp["data"]["error_description"] || + message; + } + + return message; + }; + + apiService.errorDisplay = function (defaultMessage, opt_handler) { + return function (resp) { + var message = apiService.getErrorMessage(resp, defaultMessage); + if (opt_handler) { + var handlerMessage = opt_handler(resp); + if (handlerMessage) { + message = handlerMessage; + } + } + + message = UtilService.stringToHTML(message); + alert(message); + }; + }; + + return apiService; + }, +]); diff --git a/config-tool/pkg/lib/editor/js/services/util-service.js b/config-tool/pkg/lib/editor/js/services/util-service.js new file mode 100644 index 000000000..3359c153b --- /dev/null +++ b/config-tool/pkg/lib/editor/js/services/util-service.js @@ -0,0 +1,56 @@ +/** + * Service which exposes various utility methods. + */ +angular.module('quay-config').factory('UtilService', [ + function() { + var utilService = {}; + + utilService.isEmailAddress = function(val) { + var emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; + return emailRegex.test(val); + }; + + utilService.escapeHtmlString = function(text) { + var textStr = (text || '').toString(); + var adjusted = textStr.replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + + return adjusted; + }; + + utilService.stringToHTML = function(text) { + text = utilService.escapeHtmlString(text); + text = text.replace(/\n/g, '
    '); + return text; + }; + + utilService.getRestUrl = function(args) { + var url = ''; + for (var i = 0; i < arguments.length; ++i) { + if (i > 0) { + url += '/'; + } + url += encodeURI(arguments[i]) + } + return url; + }; + + return utilService; + }]) + .factory('CoreDialog', [() => { + var service = {}; + service['fatal'] = function(title, message) { + bootbox.dialog({ + "title": title, + "message": "
    " + message, + "buttons": {}, + "className": "co-dialog fatal-error", + "closeButton": false + }); + }; + + return service; + }]); diff --git a/config-tool/pkg/lib/editor/package-lock.json b/config-tool/pkg/lib/editor/package-lock.json new file mode 100644 index 000000000..8466633de --- /dev/null +++ b/config-tool/pkg/lib/editor/package-lock.json @@ -0,0 +1,19377 @@ +{ + "name": "quay-config-editor", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "quay-config-editor", + "version": "1.0.0", + "license": "UNLICENSED", + "dependencies": { + "angular": "1.6.2", + "angular-animate": "1.6.2", + "angular-cookies": "1.6.2", + "angular-file-upload": "^2.6.0", + "angular-route": "1.6.2", + "angular-sanitize": "1.6.2", + "bootbox": "^5.4.0", + "bootstrap": "^3.3.2", + "bootstrap-datepicker": "^1.6.4", + "cal-heatmap": "^3.3.10", + "clipboard": "^1.6.1", + "core-js": "^2.4.1", + "d3": "^3.3.3", + "eonasdan-bootstrap-datetimepicker": "^4.17.43", + "file-saver": "^1.3.8", + "highlight.js": "^9.12.0", + "jquery": "1.12.4", + "js-yaml": "^3.14.0", + "jsrsasign": "^10.1.13", + "jszip": "^3.5.0", + "moment": "^2.10", + "moment-timezone": "^0.4.0", + "ng-metadata": "^4.0.1", + "node-forge": "^0.10.0", + "raven-js": "^3.1.0", + "restangular": "^1.6.1", + "rxjs": "5.5.7", + "showdown": "^1.6.4", + "underscore": "^1.5.2", + "urijs": "^1.18.10", + "url-parse": "^1.4.0", + "uuid": "^8.3.1", + "zeroclipboard": "^2.3.0" + }, + "devDependencies": { + "@types/angular": "1.6.2", + "@types/angular-mocks": "^1.5.8", + "@types/angular-route": "^1.3.3", + "@types/angular-sanitize": "^1.3.4", + "@types/core-js": "^0.9.39", + "@types/jasmine": "^2.5.41", + "@types/jquery": "^2.0.40", + "@types/node": "^14.0.27", + "@types/showdown": "^1.4.32", + "angular-mocks": "1.6.2", + "css-loader": "0.25.0", + "html-loader": "^0.4.5", + "jasmine-core": "^2.5.2", + "jasmine-ts": "0.0.3", + "karma": "^6.4.1", + "karma-chrome-launcher": "^2.1.1", + "karma-coverage": "^0.5.5", + "karma-es6-shim": "^1.0.0", + "karma-jasmine": "^0.3.8", + "karma-webpack": "^1.8.1", + "ngtemplate-loader": "^1.3.1", + "protractor": "^5.1.2", + "script-loader": "^0.7.0", + "source-map-loader": "0.1.5", + "style-loader": "0.13.1", + "terser-webpack-plugin": "^2.1.2", + "ts-loader": "6.2.0", + "ts-mocks": "^0.2.2", + "ts-node": "^3.0.6", + "tslint": "^5.4.3", + "typescript": "3.6.3", + "webpack": "4.41.0", + "webpack-bundle-analyzer": "3.5.2", + "webpack-cli": "3.3.9" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha1-Fo2ho26Q2miujUnA8bSMfGJJITo=", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha1-p4x6clHgH2FlEtMbEK3PUq2l4NI=", + "dev": true + }, + "node_modules/@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha1-fRvf1ldTU4+r5sOFls23bZrGAUM=", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "dev": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@types/angular": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.2.tgz", + "integrity": "sha1-pcMj6l1EJq0YmEzIFn+gkffIIBs=", + "dev": true, + "dependencies": { + "@types/jquery": "*" + } + }, + "node_modules/@types/angular-mocks": { + "version": "1.7.0", + "resolved": "https://registry.yarnpkg.com/@types/angular-mocks/-/angular-mocks-1.7.0.tgz", + "integrity": "sha1-MQ2ZmjxHwQ7Nju9Ga1hh34R5lCk=", + "dev": true, + "dependencies": { + "@types/angular": "*" + } + }, + "node_modules/@types/angular-mocks/node_modules/@types/angular": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.2.tgz", + "integrity": "sha1-6Zb0pdwYTf6MB2xt274NYMqrY54=", + "dev": true + }, + "node_modules/@types/angular-route": { + "version": "1.7.1", + "resolved": "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.7.1.tgz", + "integrity": "sha1-D1RRif9Rr8dK+83eF7VqJ3lj1Rs=", + "dev": true, + "dependencies": { + "@types/angular": "*" + } + }, + "node_modules/@types/angular-route/node_modules/@types/angular": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.2.tgz", + "integrity": "sha1-6Zb0pdwYTf6MB2xt274NYMqrY54=", + "dev": true + }, + "node_modules/@types/angular-sanitize": { + "version": "1.7.0", + "resolved": "https://registry.yarnpkg.com/@types/angular-sanitize/-/angular-sanitize-1.7.0.tgz", + "integrity": "sha1-qaHENiHonTvYt6ymN5pKeKfIif4=", + "dev": true, + "dependencies": { + "@types/angular": "*" + } + }, + "node_modules/@types/angular-sanitize/node_modules/@types/angular": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.2.tgz", + "integrity": "sha1-6Zb0pdwYTf6MB2xt274NYMqrY54=", + "dev": true + }, + "node_modules/@types/angular/node_modules/@types/jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-zrsFes9QccQOQ58w6EDFejDUBsM=", + "dev": true, + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/core-js": { + "version": "0.9.46", + "resolved": "https://registry.yarnpkg.com/@types/core-js/-/core-js-0.9.46.tgz", + "integrity": "sha1-6nAe40y7bf5tEA8VMDGVR8k8jXk=", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "2.8.17", + "resolved": "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.17.tgz", + "integrity": "sha1-Zfo743cSYlP2x5iLNl38eNYtU24=", + "dev": true + }, + "node_modules/@types/jquery": { + "version": "2.0.54", + "resolved": "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.54.tgz", + "integrity": "sha1-15mSRfd8P6tdhOfTK4psIL/R8HI=", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha1-3M5EMOZLRDuolF8CkPtWStW6xt0=", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.0.27", + "resolved": "https://registry.yarnpkg.com/@types/node/-/node-14.0.27.tgz", + "integrity": "sha1-oVGHOvWl6FG1GzsGXJ5jOQqeDrE=", + "dev": true + }, + "node_modules/@types/q": { + "version": "0.0.32", + "resolved": "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "node_modules/@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha1-UL6gw8KswxyVnFsedHeYs7PQbUs=", + "dev": true + }, + "node_modules/@types/showdown": { + "version": "1.9.3", + "resolved": "https://registry.yarnpkg.com/@types/showdown/-/showdown-1.9.3.tgz", + "integrity": "sha1-6qiBsDoy03IBhHMXVNMCX8RQuXA=", + "dev": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha1-qBG4wY4rq6t9VCszZYh64uTZ3kc=", + "dev": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha1-UbHF/mV2o0lTv0slPfnw1JDZ41k=", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha1-G6kmopI2E+3OSW/VsC6M6KX0lyE=", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha1-xJ2tIvZFInxe22EL25aX8aq3Ifc=", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha1-/qk+Qphj3V5DOFVfQikjhaZT8gQ=", + "dev": true + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha1-mnQP9I4/qjAisd/1RCPfmqKTwl4=", + "dev": true, + "dependencies": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha1-ugt9Oz9+RzPaYFnJMyJ12GBwJFI=", + "dev": true + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha1-3vS5knsBAdyMu9jR7bW3ucguskU=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha1-U3p1Dt31weky83RCBlUckcG5PmE=", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha1-dMpqa8vhnlCjtrRihH5pUD5r/L8=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha1-cSMp2+8kDza/V70ve4+5v0FUQh4=", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha1-BE7es06mefPgTNT9mCTV41dnrhA=", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha1-qL87XY/+mGx8Hjc8y9wqCRXwztw=", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha1-li2hKqWswcExyBxCMpkcgs5W4Bo=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha1-VIQHZsLBAC62TtGr5yCt7XFPmLw=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha1-sk2fa6UDlK8TSfUQr6j/y4pj0mQ=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha1-IVdvDsiLkUJzV7hTY4NmjvfGa40=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha1-4Q7s1ULQ5705T2gnxJ899tTu+4w=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha1-EUu8SB/RDKDiOzVg+oEnSLC65bw=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha1-7vAUoxRa5Hehy8AM0eVSM23Ot5A=", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha1-0pHGpOl5ibXGHZrPOWrk/hM6cY0=", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", + "dev": true, + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "6.4.1", + "resolved": "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha1-Ux5Yuj9RudrLmmZGyk3r9bFMpHQ=", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha1-Ejy487hMIXHx9/slJhWxx4prGow=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha1-z0xQj9/6sCwmnLx/RxqHXwVXA2U=", + "dev": true, + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha1-gWXwHENgCbzK0LHRIvBe13Dvxu4=", + "dev": true, + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha1-2y/nJG5Tb0DZtUQqOeEX191qJOA=", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha1-Yk+PRJfWGbLZdoUx1Y9BIoVNclE=", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.3", + "resolved": "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha1-GMWvOKER3etPJpe9eNaKvByr1wY=", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha1-81mGrOuRr63sQQL72FAUlQzvpk0=", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0=", + "dev": true + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/angular": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular/-/angular-1.6.2.tgz", + "integrity": "sha1-0LZ3JCrEv5roFCQpfGMglzr0u1o=" + }, + "node_modules/angular-animate": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-animate/-/angular-animate-1.6.2.tgz", + "integrity": "sha1-3vKoue3lO0tuI0wl9cZOS0OF3xU=" + }, + "node_modules/angular-cookies": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-cookies/-/angular-cookies-1.6.2.tgz", + "integrity": "sha1-/7afNfhNHv5xrdrCCikFR2tliIQ=" + }, + "node_modules/angular-file-upload": { + "version": "2.6.0", + "resolved": "https://registry.yarnpkg.com/angular-file-upload/-/angular-file-upload-2.6.0.tgz", + "integrity": "sha1-x+sUs7zSKsrTEKIQlaxsqo1d5/U=", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/angular-mocks": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-mocks/-/angular-mocks-1.6.2.tgz", + "integrity": "sha1-+7KCCOdNNRJ2mv24dx9cxamfkSg=", + "dev": true + }, + "node_modules/angular-route": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-route/-/angular-route-1.6.2.tgz", + "integrity": "sha1-laNJ3i5zZ08914O7IejXs/xSYxI=" + }, + "node_modules/angular-sanitize": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-sanitize/-/angular-sanitize-1.6.2.tgz", + "integrity": "sha1-ijJ8GsssFPUNpbXK1epFJ1Cho3U=" + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha1-ucK/WAXx5kqt7tbfOiv6+1pz9aA=", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz", + "integrity": "sha1-VcEJqvbgrv2z3EtxJAxwv1dLGOs=", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types": { + "version": "0.9.6", + "resolved": "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/async": { + "version": "0.9.2", + "resolved": "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", + "dev": true, + "optional": true + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "dependencies": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.10.0", + "resolved": "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha1-oXs6jqgRBg501H0wYSJACtRJeuI=", + "dev": true + }, + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "dev": true + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=", + "dev": true + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bfj": { + "version": "6.1.2", + "resolved": "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha1-MlyGGoIryzWKQceKM7jm4ght3n8=", + "dev": true, + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha1-gdb9H+E6TA1pV99/kbdemNrEDLI=", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "blocking-proxy": "built/lib/bin.js" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", + "dev": true + }, + "node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bootbox": { + "version": "5.4.0", + "resolved": "https://registry.yarnpkg.com/bootbox/-/bootbox-5.4.0.tgz", + "integrity": "sha1-KFemPCcLG3l9YuTFWX50tJcmdlU=", + "dependencies": { + "bootstrap": "^4.4.0", + "jquery": "^3.4.1", + "popper.js": "^1.16.0" + } + }, + "node_modules/bootbox/node_modules/bootstrap": { + "version": "4.5.2", + "resolved": "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.2.tgz", + "integrity": "sha1-qFxO2lkVXw1xGGtuatm4dYE3eas=" + }, + "node_modules/bootbox/node_modules/jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U=" + }, + "node_modules/bootstrap": { + "version": "3.4.1", + "resolved": "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha1-w6NH1Bniia0R9AM+PEEyuHwIHXI=", + "engines": { + "node": ">=6" + } + }, + "node_modules/bootstrap-datepicker": { + "version": "1.9.0", + "resolved": "https://registry.yarnpkg.com/bootstrap-datepicker/-/bootstrap-datepicker-1.9.0.tgz", + "integrity": "sha1-5L/OP8zhlnh2sh3Ggz7FmUqu0JA=", + "dependencies": { + "jquery": ">=1.7.1 <4.0.0" + } + }, + "node_modules/bootstrap-datepicker/node_modules/jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U=" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha1-Mmc0ZC9APavDADIJhTu3CtQo70g=", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha1-6vSt1G3VS+O7OzbAzxWrvrp5VsM=", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/bn.js": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha1-yWhpAtPJoncp9DqxD515wgBNp7A=", + "dev": true + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/browserstack": { + "version": "1.6.0", + "resolved": "https://registry.yarnpkg.com/browserstack/-/browserstack-1.6.0.tgz", + "integrity": "sha1-WlarkJh2BdnBONeouIEoNwKX+b8=", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha1-Iw6tNEACmIZEhBqwJEr4xEu+Pvg=", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "13.0.1", + "resolved": "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha1-qAAMIWlwiQgvhSh6GuxuOCAkpxw=", + "dev": true, + "dependencies": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cache-base/node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "dev": true + }, + "node_modules/cal-heatmap": { + "version": "3.6.2", + "resolved": "https://registry.yarnpkg.com/cal-heatmap/-/cal-heatmap-3.6.2.tgz", + "integrity": "sha1-lhp/Roazvc9xBNlRtv8d1YwMYtE=", + "dependencies": { + "d3": "^3.0.6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "dependencies": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-db": { + "version": "1.0.30001111", + "resolved": "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001111.tgz", + "integrity": "sha1-svQO9c371s3z05gLxbDLXsIqKFY=", + "dev": true + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-types": { + "version": "8.0.3", + "resolved": "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha1-M1bMoZyIlUTy16le1JzlCKDs9VI=", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/chokidar/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/chokidar/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/chokidar/node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha1-b8nXtC0ypYNZYzdmbn0ICE2izGs=", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha1-I0CQ7pfH1K0aLEvq4nUF3v/GCKQ=", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clap": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz", + "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.3", + "resolved": "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha1-UHtd59l7SO5T2ErbAWD/YhY4D3g=", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha1-7oRy27Ep5yezHooQpCfe6d/kAIs=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/clipboard": { + "version": "1.7.1", + "resolved": "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha1-3u/P2y6AB4SqNPRvoI4GhRx7u8U=", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/coa": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "dependencies": { + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "0.11.4", + "resolved": "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "dependencies": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "node_modules/colormin": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "dependencies": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz", + "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha1-ZwY871fOts9Jk6KrOlWECujEkzY=", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "2.6.11", + "resolved": "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha1-OIMUafmSK97Y7iHJ3EaYXgOZMIw=" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha1-1uf0v/pmc2CFoHYv06YyaE2rzE4=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha1-iJB4rxGmN1a8+1m9IhmWvjqe8ZY=", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha1-aRcMeLOrlXFHsriwRXLkfq0iQ/8=", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/css-loader": { + "version": "0.25.0", + "resolved": "https://registry.yarnpkg.com/css-loader/-/css-loader-0.25.0.tgz", + "integrity": "sha1-w/68jOKPTINXa2sTcH9H+Qw5AiM=", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.11.0", + "css-selector-tokenizer": "^0.6.0", + "cssnano": ">=2.6.1 <4", + "loader-utils": "~0.2.2", + "lodash.camelcase": "^3.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.0.0", + "postcss-modules-local-by-default": "^1.0.1", + "postcss-modules-scope": "^1.0.0", + "postcss-modules-values": "^1.1.0", + "source-list-map": "^0.1.4" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/css-loader/node_modules/css-selector-tokenizer": { + "version": "0.6.0", + "resolved": "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz", + "integrity": "sha1-ZEX1gseTDSQdzFAHpD1vy48HMVI=", + "dev": true, + "dependencies": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "node_modules/css-loader/node_modules/cssesc": { + "version": "0.1.0", + "resolved": "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha1-c18mGG5nx0mq8nV4NAXPBmH66PE=", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "3.10.0", + "resolved": "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "dependencies": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/csso": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "dependencies": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "node_modules/d3": { + "version": "3.5.17", + "resolved": "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz", + "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/dateformat": { + "version": "1.0.12", + "resolved": "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "node_modules/del": { + "version": "2.2.2", + "resolved": "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "dependencies": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha1-U4IULhvcU/hdhtU+X0qn3rkeCEM=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz", + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha1-QOjumPVaIUlgcUaSHGPhrl89KHU=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/duplexer": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha1-SGYSh1c9zFPjZsehrlLDoSDuybo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.523", + "resolved": "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.523.tgz", + "integrity": "sha1-SUCAsxi6kpYU7r0EQFuUw1nqkzM=", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.3", + "resolved": "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha1-y1nrLv2vc6C9eMzXAVpirW4Pk9Y=", + "dev": true, + "dependencies": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha1-kzoEBShgyF6DwSJHnEdIqOTHIVY=" + }, + "node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/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 + }, + "node_modules/enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha1-O4BvO/r8HsfeaVUe+TzKRsFwQSY=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha1-MkwBKIuIZSlm0WHbd4OHIIRajjw=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "node_modules/eonasdan-bootstrap-datetimepicker": { + "version": "4.17.47", + "resolved": "https://registry.yarnpkg.com/eonasdan-bootstrap-datetimepicker/-/eonasdan-bootstrap-datetimepicker-4.17.47.tgz", + "integrity": "sha1-ekmXAEQGUnbnll79Fvgic1IZ5zU=", + "dependencies": { + "bootstrap": "^3.3", + "jquery": "^1.8.3 || ^2.0 || ^3.0", + "moment": "^2.10", + "moment-timezone": "^0.4.0" + } + }, + "node_modules/eonasdan-bootstrap-datetimepicker/node_modules/jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U=" + }, + "node_modules/errno": { + "version": "0.1.7", + "resolved": "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es5-shim": { + "version": "4.5.14", + "resolved": "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.14.tgz", + "integrity": "sha1-kACeEBnQ6jJ0R8tSPer/j+RWl+8=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=", + "dev": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/es6-shim": { + "version": "0.35.5", + "resolved": "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.5.tgz", + "integrity": "sha1-RvWdwKhKHFAp6P8RZsoKkCB3qas=", + "dev": true + }, + "node_modules/es6-templates": { + "version": "0.2.3", + "resolved": "https://registry.yarnpkg.com/es6-templates/-/es6-templates-0.2.3.tgz", + "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", + "dev": true, + "dependencies": { + "recast": "~0.11.12", + "through": "~2.3.6" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha1-ygODMxD2iJoyZHgaqC5j65z+eEg=", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", + "dev": true, + "dependencies": { + "estraverse": "^4.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha1-tUY6zmNaCD0Bi9x8kXtMXxCoU4Q=", + "dev": true + }, + "node_modules/events": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz", + "integrity": "sha1-k7h8GPjvzUICpGGuxN/AVWtjk3k=", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz", + "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha1-kXKMWllC7O2FMSg8eUQe5BIsNak=", + "dev": true + }, + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha1-tO7oFIq7Adzx0aw0Nn1Z4S+mHW4=", + "dev": true + }, + "node_modules/file-saver": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", + "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha1-CQuz7gG2+AGoqL6Z0xcQs0Irsxc=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha1-ibM/rUpGcNqpT4Vff74x1thP6IA=", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha1-l6/n1s3AvFkoWEt8jXsW6KmqXRk=", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha1-Gvujlq/WdqbUJQTQpno6frn2KqA=", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha1-o0KLtwiLOmApL2aRkni3wpetTwc=", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha1-8JkTPfft5CLoHR2ESCcO6z5CYfM=", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha1-F7EI+e5RLft6XH88iyfqnhqcCNE=", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha1-wSg6yfJ7Noq8HjbR/3sEUBowNWs=", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.12.1", + "resolved": "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha1-3lSmIFMRuT1gOY68Ac9wFWgjErY=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-access": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "dependencies": { + "null-check": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha1-f1A2/b8SxjwWkZDL5BmchSJx+fs=", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha1-mXYFrSNF8n9RU5vqJldEISFcd4A=", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha1-/IX3MGTfafUEIfR/iD/luRO6m5c=", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dependencies": { + "delegate": "^3.1.2" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha1-y5vuaS+HwGErIyhAqHOQTkwTUnQ=", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gzip-size/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/handlebars": { + "version": "4.7.6", + "resolved": "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha1-1MBcG6+Q6ZRfd6pop6IZqkp9904=", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/uglify-js": { + "version": "3.10.1", + "resolved": "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.10.1.tgz", + "integrity": "sha1-3RR2frcVDel/JXOl/yENsU//5K0=", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha1-HwgDufjLIMD6E4It8ezds2veHv0=", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha1-VcOB2eBuHSmXqIO0o/3f5/DTrzM=", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha1-C6vKU46NTuSg+JiNaIZlN6ADz0I=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "9.18.3", + "resolved": "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.3.tgz", + "integrity": "sha1-oaCiAo1eMUniOA+Khl7oUWcD1jQ=", + "engines": { + "node": "*" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha1-YJIH1mEQADOpqUAq096mdzgcGx0=", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha1-dTm9S8Hg4KiVgVouAmJCCxKFhIg=", + "dev": true + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", + "dev": true + }, + "node_modules/html-loader": { + "version": "0.4.5", + "resolved": "https://registry.yarnpkg.com/html-loader/-/html-loader-0.4.5.tgz", + "integrity": "sha1-X7zYfNY6XEmn/OL+VvQl4Fcpxow=", + "dev": true, + "dependencies": { + "es6-templates": "^0.2.2", + "fastparse": "^1.1.1", + "html-minifier": "^3.0.1", + "loader-utils": "^1.0.2", + "object-assign": "^4.1.0" + } + }, + "node_modules/html-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/html-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha1-0AQOBUcw41TbAIRjWTGUAVIS0gw=", + "dev": true, + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-minifier/node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz", + "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha1-QBVB8FNIhLv5UmAzTnL4juOXZUk=", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha1-TuenN6vZJniik9mzShr00NCMeHs=", + "dev": true, + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=", + "dev": true + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha1-VQcL44pZk88Y72236WH1vuXFoJ0=", + "dev": true, + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha1-xM78qo5RBRwqQLos6KPScpWvlGc=", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" + }, + "node_modules/ini": { + "version": "1.3.5", + "resolved": "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/interpret": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha1-1QYaYiS+WOgIOYX1AU2EQ1lXYpY=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha1-c5P1r6Weyf9fZ6J2INEcIm4+7AI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "dev": true + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha1-kEE1x3+0LAZB1qobzbxNqo2ggvM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", + "dev": true, + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-svg": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "dependencies": { + "html-comment-regex": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "node_modules/istanbul": { + "version": "0.4.5", + "resolved": "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/istanbul/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "node_modules/istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "node_modules/jasmine": { + "version": "2.8.0", + "resolved": "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "dependencies": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "node_modules/jasmine-ts": { + "version": "0.0.3", + "resolved": "https://registry.yarnpkg.com/jasmine-ts/-/jasmine-ts-0.0.3.tgz", + "integrity": "sha1-nfmQKc1j3P7fqiTzxorVSHCVa4o=", + "dev": true, + "dependencies": { + "jasmine": "^2.4.1", + "ts-node": "^1.2.1", + "typescript": "^2.0.0" + }, + "bin": { + "jasmine-ts": "index.js" + } + }, + "node_modules/jasmine-ts/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jasmine-ts/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jasmine-ts/node_modules/jasmine": { + "version": "2.99.0", + "resolved": "https://registry.yarnpkg.com/jasmine/-/jasmine-2.99.0.tgz", + "integrity": "sha1-jKctEC5jm4Z8ZImFbg4YqceqQrc=", + "dev": true, + "dependencies": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.99.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-ts/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jasmine-ts/node_modules/ts-node": { + "version": "1.7.3", + "resolved": "https://registry.yarnpkg.com/ts-node/-/ts-node-1.7.3.tgz", + "integrity": "sha1-3uf4qEdRcy08Lkl8rFoC+xF9/uc=", + "dev": true, + "dependencies": { + "arrify": "^1.0.0", + "chalk": "^1.1.1", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "pinkie": "^2.0.4", + "source-map-support": "^0.4.0", + "tsconfig": "^5.0.2", + "v8flags": "^2.0.11", + "xtend": "^4.0.0", + "yn": "^1.2.0" + }, + "bin": { + "ts-node": "dist/bin.js" + } + }, + "node_modules/jasmine-ts/node_modules/tsconfig": { + "version": "5.0.3", + "resolved": "https://registry.yarnpkg.com/tsconfig/-/tsconfig-5.0.3.tgz", + "integrity": "sha1-X0J45wGACWeo/Dg/0ZZIh48qbjo=", + "dev": true, + "dependencies": { + "any-promise": "^1.3.0", + "parse-json": "^2.2.0", + "strip-bom": "^2.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/jasmine-ts/node_modules/typescript": { + "version": "2.9.2", + "resolved": "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha1-HL9h0F1rliaSROtqO85L2RTg8Aw=", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/jasmine-ts/node_modules/v8flags": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "dependencies": { + "user-home": "^1.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/jasmine-ts/node_modules/yn": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/yn/-/yn-1.3.0.tgz", + "integrity": "sha1-GwgSq7jYBdSJZvjfOF3J2syaGdg=", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jasmine/node_modules/jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + }, + "node_modules/jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true, + "engines": { + "node": ">= 6.9.x" + } + }, + "node_modules/jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha1-JhHQcbec6g9D7lej0RhZOsFUfbE=", + "dev": true, + "dependencies": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha1-aOMlkd9z4lrRxLSRCKLsUHliv9E=", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jquery": { + "version": "1.12.4", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-1.12.4.tgz", + "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw=" + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha1-9OaGxd4eofhn28rT1G2WlCjfmMQ=", + "dev": true + }, + "node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "node_modules/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==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz", + "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/jsrsasign": { + "version": "10.1.13", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.1.13.tgz", + "integrity": "sha512-EKifn2DocDxU2fWVqTJgFYjZUcL4fTUtfgN5OQP4t4i/WOioios8wq350E1aJFxCLmtdxGNqhLX3O0tdVqJoFg==", + "funding": { + "url": "https://github.com/kjur/jsrsasign#donations" + } + }, + "node_modules/jszip": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "node_modules/karma": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", + "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha1-zxudBxNswY/iOTJ9JGVMPbw2is8=", + "dev": true, + "dependencies": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "0.5.5", + "resolved": "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-0.5.5.tgz", + "integrity": "sha1-sNWLECXVnVxmICYxhvHVj11TSMU=", + "dev": true, + "dependencies": { + "dateformat": "^1.0.6", + "istanbul": "^0.4.0", + "minimatch": "^3.0.0", + "source-map": "^0.5.1" + } + }, + "node_modules/karma-coverage/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma-es6-shim": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/karma-es6-shim/-/karma-es6-shim-1.0.0.tgz", + "integrity": "sha1-qutTCGK895NA69WI9PnlfehCtHk=", + "dev": true, + "dependencies": { + "es5-shim": "~4.5.8", + "es6-shim": "~0.35.0" + } + }, + "node_modules/karma-jasmine": { + "version": "0.3.8", + "resolved": "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-0.3.8.tgz", + "integrity": "sha1-W2RXeRrZuJqhc/B54+vhuMgFI2w=", + "dev": true + }, + "node_modules/karma-webpack": { + "version": "1.8.1", + "resolved": "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-1.8.1.tgz", + "integrity": "sha1-OdX9Lt7qPMPvW0BZibN9Ww5qO04=", + "dev": true, + "dependencies": { + "async": "~0.9.0", + "loader-utils": "^0.2.5", + "lodash": "^3.8.0", + "source-map": "^0.1.41", + "webpack-dev-middleware": "^1.0.11" + } + }, + "node_modules/karma-webpack/node_modules/lodash": { + "version": "3.10.1", + "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "node_modules/karma-webpack/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/karma/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/karma/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/karma/node_modules/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 + }, + "node_modules/karma/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/karma/node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/karma/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/karma/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/karma/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lcid": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha1-bvXS32DlL4LrIopMNz6NHzlyU88=", + "dev": true, + "dependencies": { + "invert-kv": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha1-7UcGa/5TTX6ExMe5mYwqdWB9k1c=", + "dev": true, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash._createcompounder": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz", + "integrity": "sha1-XdLLVTctbnDg4jkvsjBNZjEJEHU=", + "dev": true, + "dependencies": { + "lodash.deburr": "^3.0.0", + "lodash.words": "^3.0.0" + } + }, + "node_modules/lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz", + "integrity": "sha1-kyyLh/ikN3iXxnGXUzKC+Xrqwpg=", + "dev": true, + "dependencies": { + "lodash._createcompounder": "^3.0.0" + } + }, + "node_modules/lodash.deburr": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz", + "integrity": "sha1-baj1QzSjZqfPTEx2742Aqhs2XtU=", + "dev": true, + "dependencies": { + "lodash._root": "^3.0.0" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/lodash.words": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/lodash.words/-/lodash.words-3.2.0.tgz", + "integrity": "sha1-TiqGSbwIdFsXxpWxo86P7llmI7M=", + "dev": true, + "dependencies": { + "lodash._root": "^3.0.0" + } + }, + "node_modules/log4js": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.8.0.tgz", + "integrity": "sha512-g+V8gZyurIexrOvWQ+AcZsIvuK/lBnx2argejZxL4gVZ4Hq02kUYH6WZOnqxgBml+zzQZYdaEoTN84B6Hzm8Fg==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/log4js/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/log4js/node_modules/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 + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha1-QV6WcEazp/HRhSd9hKpYIDcmoT8=", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha1-LrLjfqm2fEiR9oShOUeZr0hM96I=", + "dev": true + }, + "node_modules/mamacro": { + "version": "0.0.3", + "resolved": "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha1-rSyVdhl8nxq/MI0Hh4Zb2XWj8+Q=", + "dev": true + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha1-fVg6cwZDTAVf5HSw9FB45uG0uSo=", + "dev": true, + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-expression-evaluator": { + "version": "1.2.22", + "resolved": "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz", + "integrity": "sha1-wU3LPYtNFQ5dzqnGjI2tgDCbDV4=", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha1-tdB7jjIW4+J81yjXL3DR5qNCAF8=", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz", + "integrity": "sha1-Rhr0l7xK4JYIzbLmDu+2m/90QXg=", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/micromatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=", + "dev": true, + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha1-ftLCzMyvhNP/y3pptXcR/CCDQBs=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.1.3", + "resolved": "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha1-fUL/HzljVILhX5zbUxhN7r1YFf0=", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha1-IrgTv3Rdxu26JXa5QAIq1u3Ixhc=", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha1-gucTXX6JpQ/+ZGEKeHlTxMTLs3M=", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha1-aEcveXEcCEZXwGfFxq2Tzd6oIUw=", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha1-6goykfl+C16HdrNj1fChLZTGcCI=", + "dev": true, + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.27.0", + "resolved": "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz", + "integrity": "sha1-i/9OPiaiNiIN/j423nVrbrqgEF0=", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.4.1.tgz", + "integrity": "sha1-gfWYw61eIs2teWtn7NjYjQ9bqgY=", + "dependencies": { + "moment": ">= 2.6.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha1-tKr7k+OustgXTKU88WOrfXMIMF8=", + "dev": true + }, + "node_modules/ng-metadata": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/ng-metadata/-/ng-metadata-4.0.1.tgz", + "integrity": "sha1-w2cBisnlwhTFe5h+gpQKMP1LnGc=" + }, + "node_modules/ngtemplate-loader": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/ngtemplate-loader/-/ngtemplate-loader-1.3.1.tgz", + "integrity": "sha1-v9BaHZoSAEFpjAb2FlP2BrOcEGA=", + "dev": true, + "dependencies": { + "jsesc": "^0.5.0", + "loader-utils": "~0.2.6" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "dev": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha1-tk9RPRgzhiX5A0bSew0jXmMfZCU=", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/nopt/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/null-check": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opener": { + "version": "1.5.1", + "resolved": "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz", + "integrity": "sha1-bS8Od/GgrwAyrKcWwsH7uOfoq+0=", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha1-hPodA2/p08fiHZmIS2ARZ+yPtJU=", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/os-locale": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha1-qAKm7hfyTBBIOrmTVxnO9O0Wvxo=", + "dev": true, + "dependencies": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha1-kYzrrqJIpiz3/6uOO8qMX4gvxC4=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha1-1wTZr4orpoTiYA2aIVmD1BQal50=", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz", + "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=" + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha1-kEnKN9bLIYLDsdLHIL6U0UpYFPw=", + "dev": true, + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha1-ADJxND2ljclMrOSU+u89IUfs6g4=", + "dev": true, + "dependencies": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha1-5sTd1+06onxoogzE5Q4aTug7vEo=", + "dev": true + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha1-y4cksPramEWWhW0abrr9NYRlS5Q=", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=", + "dev": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha1-KiI8s9x7YhPXQOQDcr5A3kPmWxs=" + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "dependencies": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "node_modules/postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "dependencies": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "dependencies": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "dependencies": { + "postcss": "^5.0.14" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "dependencies": { + "postcss": "^5.0.14" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "dependencies": { + "postcss": "^5.0.16" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "dependencies": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha1-giRf34IzcEFkXkdxFNjlk6oYuOw=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "dependencies": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "node_modules/postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "dependencies": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha1-3IfjQUjsfqtfeR981YSYMzdbdBo=", + "dev": true, + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-values/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-values/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "dependencies": { + "postcss": "^5.0.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "dependencies": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE=", + "dev": true + }, + "node_modules/postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz", + "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/protractor": { + "version": "5.4.4", + "resolved": "https://registry.yarnpkg.com/protractor/-/protractor-5.4.4.tgz", + "integrity": "sha1-skFGaq+Dt2vCxY32feuaXN/GFSk=", + "dev": true, + "dependencies": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.0.6", + "yargs": "^12.0.5" + }, + "bin": { + "protractor": "bin/protractor", + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/protractor/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/protractor/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/cliui": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=", + "dev": true, + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/protractor/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", + "dev": true + }, + "node_modules/protractor/node_modules/q": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/protractor/node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "node_modules/protractor/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/protractor/node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/protractor/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/yargs": { + "version": "12.0.5", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha1-BfWZe2CWR7ZPZrgeO0sQo2jnrRM=", + "dev": true, + "dependencies": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=", + "dev": true, + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz", + "integrity": "sha1-kyb4vPsBOtzABf3/BWrM4CDlHCQ=", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha1-T8ydd6B+SLp1J+fL4N4z0HATMeA=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha1-YOWl/WSn+L+k0qsu1v30yFutFU4=" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raven-js": { + "version": "3.27.2", + "resolved": "https://registry.yarnpkg.com/raven-js/-/raven-js-3.27.2.tgz", + "integrity": "sha1-bDPflSAmzXOCCqmZEit7dzemZ3U=" + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "dev": true, + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/recast": { + "version": "0.11.23", + "resolved": "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "dev": true, + "dependencies": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/esprima": { + "version": "3.1.3", + "resolved": "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "dependencies": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + } + }, + "node_modules/reduce-css-calc/node_modules/balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "node_modules/reduce-function-call": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz", + "integrity": "sha1-YDUPf7JSwKZ+sQ/UaU0WkJlxMA8=", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha1-ytkq2Oa1kXc0hfvgWkhcr09Ffm8=", + "dev": true + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "dependencies": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true, + "optional": true + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz", + "integrity": "sha1-1zyRhzHLWofaBH4gcjQUb2ZNErM=", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz", + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha1-0LMp7MfMD2Fkn2IhW+aa9UqomJs=" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha1-sllBtUloIxzC0bt2p5y38sC/hEQ=", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "node_modules/restangular": { + "version": "1.6.1", + "resolved": "https://registry.yarnpkg.com/restangular/-/restangular-1.6.1.tgz", + "integrity": "sha1-rdF0PyfXewuKZS5khadgtiNO0CQ=", + "dependencies": { + "lodash": "~4.17.0" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/rxjs": { + "version": "5.5.7", + "resolved": "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.7.tgz", + "integrity": "sha1-r7PRZCsGmy+/IDkD1lAdGstM2ic=", + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "dev": true + }, + "node_modules/saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha1-lAWnPDYNRJsjKDmRmobDltN5/Z0=", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "dev": true + }, + "node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/script-loader": { + "version": "0.7.2", + "resolved": "https://registry.yarnpkg.com/script-loader/-/script-loader-0.7.2.tgz", + "integrity": "sha1-IBbbb4byX1z1baOJFdgzeLsWa6c=", + "dev": true, + "dependencies": { + "raw-loader": "~0.5.1" + } + }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "node_modules/selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha1-K6h6FmLAILiYjJga5iyyoBKY6vw=", + "dev": true, + "dependencies": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.0.30", + "resolved": "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz", + "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha1-bGGeT5xgMIw4UZSYwU+7EKrOuwY=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha1-i/OpFwcSZk7yVhtEtpHq/jmSFOo=", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/showdown": { + "version": "1.9.1", + "resolved": "https://registry.yarnpkg.com/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha1-E04UjnXNRiPgnCGwURl315ta0O8=", + "dependencies": { + "yargs": "^14.2" + }, + "bin": { + "showdown": "bin/showdown.js" + } + }, + "node_modules/showdown/node_modules/yargs": { + "version": "14.2.3", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha1-Ghw+3O0a+yov6jNgS8bR2NaIpBQ=", + "dependencies": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "node_modules/showdown/node_modules/yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha1-VHhq9AuCDcsvuAJbEbTWWddjI7M=", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=", + "dev": true + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/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 + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/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 + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "0.1.5", + "resolved": "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.1.5.tgz", + "integrity": "sha1-DsbKWKoOYUY6KGc91MSei6Z9xxg=", + "dev": true, + "dependencies": { + "async": "^0.9.0", + "loader-utils": "~0.2.2", + "source-map": "~0.1.33" + } + }, + "node_modules/source-map-loader/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha1-3s6BrJweZxPl99G28X1Gj6U9iak=", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha1-PyjOGnegA3JoPq3kpDMYNSeiFj0=", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha1-NpS1gEVnpFjTyARYQqY1hjL2JlQ=", + "dev": true + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "7.1.0", + "resolved": "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha1-ksJBv23oI2W1x/tL126XVSLhKU0=", + "dev": true, + "dependencies": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha1-h1IdOKRKp+6RzhzSpH3wy0ndZgs=", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha1-6+J6DDibBPvMIzZClS4Qcxr6m64=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha1-1wiCgVWasneEJCebCHfaPDktWj0=", + "dev": true + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/streamroller/node_modules/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 + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "engines": { + "node": ">=6" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-loader": { + "version": "0.13.1", + "resolved": "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.1.tgz", + "integrity": "sha1-RoKA77wEcwI806bNVuM7Wh1/w6k=", + "dev": true, + "dependencies": { + "loader-utils": "^0.2.7" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svgo": { + "version": "0.7.2", + "resolved": "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "dependencies": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/colors": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/svgo/node_modules/js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha1-ofzMBrWNth/XpF2i2kT186Pme6I=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "4.8.0", + "resolved": "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz", + "integrity": "sha1-YwVjQ9fHC7KfOvZlhlpG/gOg3xc=", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "2.3.7", + "resolved": "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.7.tgz", + "integrity": "sha1-SRD/XRqHIWjMf6bNN0nisNYKigs=", + "dev": true, + "dependencies": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.6.12", + "webpack-sources": "^1.4.3" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha1-FxUfdtjq5n+793lgwzxnatn078c=", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha1-qYti+G3K9PZzmWSMCFKRq56P7WE=", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-stamp": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz", + "integrity": "sha1-kX4KZpBWiHkOx7u94EBGJZr4P1c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha1-gAsfPu4nLlvFPuRloE0OgEwxIR8=", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=" + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha1-zZ+yoKodWhK0c72fuW+j3P9lreI=", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha1-8shUBoALmw90yfdGW4HqrSQSUvg=", + "dev": true + }, + "node_modules/ts-loader": { + "version": "6.2.0", + "resolved": "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.0.tgz", + "integrity": "sha1-UtOZPsvFR0wVEyQjiOEEnaD86IA=", + "dev": true, + "dependencies": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ts-loader/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ts-loader/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/ts-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/ts-loader/node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/ts-loader/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-mocks": { + "version": "0.2.2", + "resolved": "https://registry.yarnpkg.com/ts-mocks/-/ts-mocks-0.2.2.tgz", + "integrity": "sha1-BR5bOjAGj2ufGx+qVSpvFyeTxtY=", + "dev": true + }, + "node_modules/ts-node": { + "version": "3.3.0", + "resolved": "https://registry.yarnpkg.com/ts-node/-/ts-node-3.3.0.tgz", + "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", + "dev": true, + "dependencies": { + "arrify": "^1.0.0", + "chalk": "^2.0.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.0", + "tsconfig": "^6.0.0", + "v8flags": "^3.0.0", + "yn": "^2.0.0" + }, + "bin": { + "_ts-node": "dist/_bin.js", + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/tsconfig": { + "version": "6.0.0", + "resolved": "https://registry.yarnpkg.com/tsconfig/-/tsconfig-6.0.0.tgz", + "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", + "dev": true, + "dependencies": { + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.13.0", + "resolved": "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha1-yIHhPMcBWJTtkUhi0nZDb6mkcEM=", + "dev": true + }, + "node_modules/tslint": { + "version": "5.20.1", + "resolved": "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha1-5AHortoBUrxE3QfmFANPP4DGe30=", + "dev": true, + "dependencies": { + "@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.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + } + }, + "node_modules/tslint/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz", + "integrity": "sha1-YPOuy4nV+uUgwRqhnvwruYKq3n0=", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha1-MrSIUBRnrL7dS4VJhnOggSrKC5k=", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typescript": { + "version": "3.6.3", + "resolved": "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha1-/qlC+rsg9+HKcWT/Ym8anz9wtNo=", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha1-mtlWPY6zrN+404WX0q8dgV9qdV8=", + "dev": true, + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz", + "integrity": "sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=", + "dev": true + }, + "node_modules/underscore": { + "version": "1.10.2", + "resolved": "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha1-c9aqNmjzGI5K2w8ZQ70Sz9fvqq8=" + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha1-HWl2k2mtoFgxA6HmrodoG1ZXMjA=", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha1-uqvOkQg/xk6UWw861hPiZPfNTmw=", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.2.2", + "resolved": "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urijs": { + "version": "1.19.2", + "resolved": "https://registry.yarnpkg.com/urijs/-/urijs-1.19.2.tgz", + "integrity": "sha1-+b4J8AxMUTS3yzz0dcHdOUUmJlo=" + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.4.7", + "resolved": "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha1-qKg1NejACjFuQDpdtKwbm4U64ng=", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/user-home": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true, + "bin": { + "user-home": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz", + "integrity": "sha1-MjZzNyDsZLsn9uJvQhqqLhtYjWE=", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha1-APdJTSritojP4omd9u0sVL75Hb4=", + "dev": true + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha1-skPjtN/XMfp3TnSSEoEJoP5m1lY=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha1-4rgApT56Kbk1BsPPQRANFsTErY4=", + "dev": true + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/extsprintf": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha1-eGQcSIuObKkadfUR56OzKobl3aA=", + "dev": true + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "1.7.4", + "resolved": "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz", + "integrity": "sha1-bp2lOzyAuy1lCBiPWyAEEIZs0ws=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.0" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "dev": true, + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + }, + "engines": { + "node": "<8.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha1-V9epPADbTMjVVuTT20tdsKgMO7c=", + "dev": true, + "dependencies": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager": { + "version": "12.1.7", + "resolved": "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha1-7U6u6PkGszwUboabVehQVTobEWI=", + "dev": true, + "dependencies": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "bin": { + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack": { + "version": "4.41.0", + "resolved": "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz", + "integrity": "sha1-22olS95nF2n3wU6QoaVec2Avxws=", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.1", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "3.5.2", + "resolved": "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.5.2.tgz", + "integrity": "sha1-rAKDT0sx3o4n1x5semEjAevdt58=", + "dev": true, + "dependencies": { + "acorn": "^6.0.7", + "acorn-walk": "^6.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 6.14.4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "6.2.1", + "resolved": "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz", + "integrity": "sha1-RC/fCkftZPWbal2P8TD0dI7VJPs=", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-cli": { + "version": "3.3.9", + "resolved": "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.9.tgz", + "integrity": "sha1-ecJ+cflLf+Mk1ZSrZKjjlrnaqRo=", + "dev": true, + "dependencies": { + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-cli/node_modules/enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha1-Qcfgv9/nSsH/4eV61qXGyfN0Kn8=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/webpack-cli/node_modules/loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-cli/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha1-+PwRIM47T8VoDO7LQ9d3lmshEF4=", + "dev": true, + "dependencies": { + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha1-OZO9hzv8SEecyp6jpUeDXHwVSzQ=", + "dev": true + }, + "node_modules/webpack/node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha1-ZovL0QWutfHZL+JVcOyVJcj6pAw=", + "dev": true, + "dependencies": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "node_modules/webpack/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/ssri": { + "version": "6.0.1", + "resolved": "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha1-KjxBso3UW2K2Nnbst0ABJlrp7dg=", + "dev": true, + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/webpack/node_modules/terser-webpack-plugin": { + "version": "1.4.4", + "resolved": "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", + "integrity": "sha1-LGNUQ0cyS6r6mla6rd8WNMir/C8=", + "dev": true, + "dependencies": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha1-YQY29rH3A4kb00dxzLF/uTtHB5w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha1-JqlMU5G7ypJhUgAvabhKS/dy5ag=", + "dev": true, + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha1-H9H2cjXVttD+54EFYAG/tpTAOwk=", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha1-oMaVFnUkIesqx1juTUzPWIQ+rGY=", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha1-vpuuHIoEbnazESdyY0fQrXACvrM=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha1-le+U+F7MgdAHwmThkKEg8KPIVms=" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=", + "dev": true + }, + "node_modules/yargs": { + "version": "13.2.4", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha1-C1YreUAW65ZRuYvTes82SqXW3IM=", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "node_modules/yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha1-h5oIZZc7yp9rq1y987HGfsfTvPQ=", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha1-Ew8JcC667vJlDVTObj5XBvek+zg=", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/zeroclipboard": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/zeroclipboard/-/zeroclipboard-2.3.0.tgz", + "integrity": "sha1-WS69gzpDCGiLBzlpfT2/mJACya8=" + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha1-Fo2ho26Q2miujUnA8bSMfGJJITo=", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha1-p4x6clHgH2FlEtMbEK3PUq2l4NI=", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha1-fRvf1ldTU4+r5sOFls23bZrGAUM=", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "dev": true + } + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "@types/angular": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.2.tgz", + "integrity": "sha1-pcMj6l1EJq0YmEzIFn+gkffIIBs=", + "dev": true, + "requires": { + "@types/jquery": "*" + }, + "dependencies": { + "@types/jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-zrsFes9QccQOQ58w6EDFejDUBsM=", + "dev": true, + "requires": { + "@types/sizzle": "*" + } + } + } + }, + "@types/angular-mocks": { + "version": "1.7.0", + "resolved": "https://registry.yarnpkg.com/@types/angular-mocks/-/angular-mocks-1.7.0.tgz", + "integrity": "sha1-MQ2ZmjxHwQ7Nju9Ga1hh34R5lCk=", + "dev": true, + "requires": { + "@types/angular": "*" + }, + "dependencies": { + "@types/angular": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.2.tgz", + "integrity": "sha1-6Zb0pdwYTf6MB2xt274NYMqrY54=", + "dev": true + } + } + }, + "@types/angular-route": { + "version": "1.7.1", + "resolved": "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.7.1.tgz", + "integrity": "sha1-D1RRif9Rr8dK+83eF7VqJ3lj1Rs=", + "dev": true, + "requires": { + "@types/angular": "*" + }, + "dependencies": { + "@types/angular": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.2.tgz", + "integrity": "sha1-6Zb0pdwYTf6MB2xt274NYMqrY54=", + "dev": true + } + } + }, + "@types/angular-sanitize": { + "version": "1.7.0", + "resolved": "https://registry.yarnpkg.com/@types/angular-sanitize/-/angular-sanitize-1.7.0.tgz", + "integrity": "sha1-qaHENiHonTvYt6ymN5pKeKfIif4=", + "dev": true, + "requires": { + "@types/angular": "*" + }, + "dependencies": { + "@types/angular": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.2.tgz", + "integrity": "sha1-6Zb0pdwYTf6MB2xt274NYMqrY54=", + "dev": true + } + } + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/core-js": { + "version": "0.9.46", + "resolved": "https://registry.yarnpkg.com/@types/core-js/-/core-js-0.9.46.tgz", + "integrity": "sha1-6nAe40y7bf5tEA8VMDGVR8k8jXk=", + "dev": true + }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "2.8.17", + "resolved": "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.17.tgz", + "integrity": "sha1-Zfo743cSYlP2x5iLNl38eNYtU24=", + "dev": true + }, + "@types/jquery": { + "version": "2.0.54", + "resolved": "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.54.tgz", + "integrity": "sha1-15mSRfd8P6tdhOfTK4psIL/R8HI=", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha1-3M5EMOZLRDuolF8CkPtWStW6xt0=", + "dev": true + }, + "@types/node": { + "version": "14.0.27", + "resolved": "https://registry.yarnpkg.com/@types/node/-/node-14.0.27.tgz", + "integrity": "sha1-oVGHOvWl6FG1GzsGXJ5jOQqeDrE=", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha1-UL6gw8KswxyVnFsedHeYs7PQbUs=", + "dev": true + }, + "@types/showdown": { + "version": "1.9.3", + "resolved": "https://registry.yarnpkg.com/@types/showdown/-/showdown-1.9.3.tgz", + "integrity": "sha1-6qiBsDoy03IBhHMXVNMCX8RQuXA=", + "dev": true + }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha1-qBG4wY4rq6t9VCszZYh64uTZ3kc=", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha1-UbHF/mV2o0lTv0slPfnw1JDZ41k=", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha1-G6kmopI2E+3OSW/VsC6M6KX0lyE=", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha1-xJ2tIvZFInxe22EL25aX8aq3Ifc=", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha1-/qk+Qphj3V5DOFVfQikjhaZT8gQ=", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha1-mnQP9I4/qjAisd/1RCPfmqKTwl4=", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha1-ugt9Oz9+RzPaYFnJMyJ12GBwJFI=", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha1-3vS5knsBAdyMu9jR7bW3ucguskU=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha1-U3p1Dt31weky83RCBlUckcG5PmE=", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha1-dMpqa8vhnlCjtrRihH5pUD5r/L8=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha1-cSMp2+8kDza/V70ve4+5v0FUQh4=", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha1-BE7es06mefPgTNT9mCTV41dnrhA=", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha1-qL87XY/+mGx8Hjc8y9wqCRXwztw=", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha1-li2hKqWswcExyBxCMpkcgs5W4Bo=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha1-VIQHZsLBAC62TtGr5yCt7XFPmLw=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha1-sk2fa6UDlK8TSfUQr6j/y4pj0mQ=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha1-IVdvDsiLkUJzV7hTY4NmjvfGa40=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha1-4Q7s1ULQ5705T2gnxJ899tTu+4w=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha1-EUu8SB/RDKDiOzVg+oEnSLC65bw=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha1-7vAUoxRa5Hehy8AM0eVSM23Ot5A=", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha1-0pHGpOl5ibXGHZrPOWrk/hM6cY0=", + "dev": true + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha1-Ux5Yuj9RudrLmmZGyk3r9bFMpHQ=", + "dev": true + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha1-Ejy487hMIXHx9/slJhWxx4prGow=", + "dev": true + }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha1-z0xQj9/6sCwmnLx/RxqHXwVXA2U=", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha1-gWXwHENgCbzK0LHRIvBe13Dvxu4=", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha1-2y/nJG5Tb0DZtUQqOeEX191qJOA=", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha1-Yk+PRJfWGbLZdoUx1Y9BIoVNclE=", + "dev": true + } + } + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha1-GMWvOKER3etPJpe9eNaKvByr1wY=", + "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" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha1-81mGrOuRr63sQQL72FAUlQzvpk0=", + "dev": true + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0=", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular/-/angular-1.6.2.tgz", + "integrity": "sha1-0LZ3JCrEv5roFCQpfGMglzr0u1o=" + }, + "angular-animate": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-animate/-/angular-animate-1.6.2.tgz", + "integrity": "sha1-3vKoue3lO0tuI0wl9cZOS0OF3xU=" + }, + "angular-cookies": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-cookies/-/angular-cookies-1.6.2.tgz", + "integrity": "sha1-/7afNfhNHv5xrdrCCikFR2tliIQ=" + }, + "angular-file-upload": { + "version": "2.6.0", + "resolved": "https://registry.yarnpkg.com/angular-file-upload/-/angular-file-upload-2.6.0.tgz", + "integrity": "sha1-x+sUs7zSKsrTEKIQlaxsqo1d5/U=" + }, + "angular-mocks": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-mocks/-/angular-mocks-1.6.2.tgz", + "integrity": "sha1-+7KCCOdNNRJ2mv24dx9cxamfkSg=", + "dev": true + }, + "angular-route": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-route/-/angular-route-1.6.2.tgz", + "integrity": "sha1-laNJ3i5zZ08914O7IejXs/xSYxI=" + }, + "angular-sanitize": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/angular-sanitize/-/angular-sanitize-1.6.2.tgz", + "integrity": "sha1-ijJ8GsssFPUNpbXK1epFJ1Cho3U=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha1-ucK/WAXx5kqt7tbfOiv6+1pz9aA=", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz", + "integrity": "sha1-VcEJqvbgrv2z3EtxJAxwv1dLGOs=", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", + "dev": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "dev": true + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha1-oXs6jqgRBg501H0wYSJACtRJeuI=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "dev": true + }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha1-MlyGGoIryzWKQceKM7jm4ght3n8=", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "dev": true, + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha1-gdb9H+E6TA1pV99/kbdemNrEDLI=", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", + "dev": true + }, + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bootbox": { + "version": "5.4.0", + "resolved": "https://registry.yarnpkg.com/bootbox/-/bootbox-5.4.0.tgz", + "integrity": "sha1-KFemPCcLG3l9YuTFWX50tJcmdlU=", + "requires": { + "bootstrap": "^4.4.0", + "jquery": "^3.4.1", + "popper.js": "^1.16.0" + }, + "dependencies": { + "bootstrap": { + "version": "4.5.2", + "resolved": "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.2.tgz", + "integrity": "sha1-qFxO2lkVXw1xGGtuatm4dYE3eas=" + }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U=" + } + } + }, + "bootstrap": { + "version": "3.4.1", + "resolved": "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha1-w6NH1Bniia0R9AM+PEEyuHwIHXI=" + }, + "bootstrap-datepicker": { + "version": "1.9.0", + "resolved": "https://registry.yarnpkg.com/bootstrap-datepicker/-/bootstrap-datepicker-1.9.0.tgz", + "integrity": "sha1-5L/OP8zhlnh2sh3Ggz7FmUqu0JA=", + "requires": { + "jquery": ">=1.7.1 <4.0.0" + }, + "dependencies": { + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U=" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha1-Mmc0ZC9APavDADIJhTu3CtQo70g=", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha1-6vSt1G3VS+O7OzbAzxWrvrp5VsM=", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "bn.js": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha1-yWhpAtPJoncp9DqxD515wgBNp7A=", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + }, + "browserstack": { + "version": "1.6.0", + "resolved": "https://registry.yarnpkg.com/browserstack/-/browserstack-1.6.0.tgz", + "integrity": "sha1-WlarkJh2BdnBONeouIEoNwKX+b8=", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha1-Iw6tNEACmIZEhBqwJEr4xEu+Pvg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", + "dev": true + }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha1-qAAMIWlwiQgvhSh6GuxuOCAkpxw=", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "dev": true + } + } + }, + "cal-heatmap": { + "version": "3.6.2", + "resolved": "https://registry.yarnpkg.com/cal-heatmap/-/cal-heatmap-3.6.2.tgz", + "integrity": "sha1-lhp/Roazvc9xBNlRtv8d1YwMYtE=", + "requires": { + "d3": "^3.0.6" + } + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "requires": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-db": { + "version": "1.0.30001111", + "resolved": "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001111.tgz", + "integrity": "sha1-svQO9c371s3z05gLxbDLXsIqKFY=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha1-M1bMoZyIlUTy16le1JzlCKDs9VI=", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "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 + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "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" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha1-b8nXtC0ypYNZYzdmbn0ICE2izGs=", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha1-I0CQ7pfH1K0aLEvq4nUF3v/GCKQ=", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz", + "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", + "dev": true, + "requires": { + "chalk": "^1.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha1-UHtd59l7SO5T2ErbAWD/YhY4D3g=", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha1-7oRy27Ep5yezHooQpCfe6d/kAIs=", + "dev": true + }, + "clipboard": { + "version": "1.7.1", + "resolved": "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha1-3u/P2y6AB4SqNPRvoI4GhRx7u8U=", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "requires": { + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "requires": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "requires": { + "color-name": "^1.0.0" + }, + "dependencies": { + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + } + } + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "requires": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz", + "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha1-ZwY871fOts9Jk6KrOlWECujEkzY=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "dev": true + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha1-OIMUafmSK97Y7iHJ3EaYXgOZMIw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha1-1uf0v/pmc2CFoHYv06YyaE2rzE4=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha1-iJB4rxGmN1a8+1m9IhmWvjqe8ZY=", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha1-aRcMeLOrlXFHsriwRXLkfq0iQ/8=", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-loader": { + "version": "0.25.0", + "resolved": "https://registry.yarnpkg.com/css-loader/-/css-loader-0.25.0.tgz", + "integrity": "sha1-w/68jOKPTINXa2sTcH9H+Qw5AiM=", + "dev": true, + "requires": { + "babel-code-frame": "^6.11.0", + "css-selector-tokenizer": "^0.6.0", + "cssnano": ">=2.6.1 <4", + "loader-utils": "~0.2.2", + "lodash.camelcase": "^3.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.0.0", + "postcss-modules-local-by-default": "^1.0.1", + "postcss-modules-scope": "^1.0.0", + "postcss-modules-values": "^1.1.0", + "source-list-map": "^0.1.4" + }, + "dependencies": { + "css-selector-tokenizer": { + "version": "0.6.0", + "resolved": "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz", + "integrity": "sha1-ZEX1gseTDSQdzFAHpD1vy48HMVI=", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + } + } + }, + "css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha1-c18mGG5nx0mq8nV4NAXPBmH66PE=", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "requires": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "requires": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d3": { + "version": "3.5.17", + "resolved": "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz", + "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "dev": true + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha1-U4IULhvcU/hdhtU+X0qn3rkeCEM=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz", + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha1-QOjumPVaIUlgcUaSHGPhrl89KHU=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha1-SGYSh1c9zFPjZsehrlLDoSDuybo=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.523", + "resolved": "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.523.tgz", + "integrity": "sha1-SUCAsxi6kpYU7r0EQFuUw1nqkzM=", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha1-y1nrLv2vc6C9eMzXAVpirW4Pk9Y=", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha1-kzoEBShgyF6DwSJHnEdIqOTHIVY=" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "dev": true + }, + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha1-O4BvO/r8HsfeaVUe+TzKRsFwQSY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha1-MkwBKIuIZSlm0WHbd4OHIIRajjw=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "eonasdan-bootstrap-datetimepicker": { + "version": "4.17.47", + "resolved": "https://registry.yarnpkg.com/eonasdan-bootstrap-datetimepicker/-/eonasdan-bootstrap-datetimepicker-4.17.47.tgz", + "integrity": "sha1-ekmXAEQGUnbnll79Fvgic1IZ5zU=", + "requires": { + "bootstrap": "^3.3", + "jquery": "^1.8.3 || ^2.0 || ^3.0", + "moment": "^2.10", + "moment-timezone": "^0.4.0" + }, + "dependencies": { + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U=" + } + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-shim": { + "version": "4.5.14", + "resolved": "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.14.tgz", + "integrity": "sha1-kACeEBnQ6jJ0R8tSPer/j+RWl+8=", + "dev": true + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "es6-shim": { + "version": "0.35.5", + "resolved": "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.5.tgz", + "integrity": "sha1-RvWdwKhKHFAp6P8RZsoKkCB3qas=", + "dev": true + }, + "es6-templates": { + "version": "0.2.3", + "resolved": "https://registry.yarnpkg.com/es6-templates/-/es6-templates-0.2.3.tgz", + "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", + "dev": true, + "requires": { + "recast": "~0.11.12", + "through": "~2.3.6" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha1-ygODMxD2iJoyZHgaqC5j65z+eEg=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha1-tUY6zmNaCD0Bi9x8kXtMXxCoU4Q=", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz", + "integrity": "sha1-k7h8GPjvzUICpGGuxN/AVWtjk3k=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz", + "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", + "dev": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha1-kXKMWllC7O2FMSg8eUQe5BIsNak=", + "dev": true + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha1-tO7oFIq7Adzx0aw0Nn1Z4S+mHW4=", + "dev": true + }, + "file-saver": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", + "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha1-CQuz7gG2+AGoqL6Z0xcQs0Irsxc=", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha1-ibM/rUpGcNqpT4Vff74x1thP6IA=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha1-l6/n1s3AvFkoWEt8jXsW6KmqXRk=", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha1-Gvujlq/WdqbUJQTQpno6frn2KqA=", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha1-o0KLtwiLOmApL2aRkni3wpetTwc=", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha1-8JkTPfft5CLoHR2ESCcO6z5CYfM=", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha1-F7EI+e5RLft6XH88iyfqnhqcCNE=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha1-wSg6yfJ7Noq8HjbR/3sEUBowNWs=", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.12.1", + "resolved": "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.12.1.tgz", + "integrity": "sha1-3lSmIFMRuT1gOY68Ac9wFWgjErY=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha1-f1A2/b8SxjwWkZDL5BmchSJx+fs=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha1-mXYFrSNF8n9RU5vqJldEISFcd4A=", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha1-/IX3MGTfafUEIfR/iD/luRO6m5c=", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + } + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "requires": { + "delegate": "^3.1.2" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha1-y5vuaS+HwGErIyhAqHOQTkwTUnQ=", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", + "dev": true + } + } + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha1-1MBcG6+Q6ZRfd6pop6IZqkp9904=", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "uglify-js": { + "version": "3.10.1", + "resolved": "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.10.1.tgz", + "integrity": "sha1-3RR2frcVDel/JXOl/yENsU//5K0=", + "dev": true, + "optional": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha1-HwgDufjLIMD6E4It8ezds2veHv0=", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha1-VcOB2eBuHSmXqIO0o/3f5/DTrzM=", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha1-C6vKU46NTuSg+JiNaIZlN6ADz0I=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "dev": true + }, + "highlight.js": { + "version": "9.18.3", + "resolved": "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.3.tgz", + "integrity": "sha1-oaCiAo1eMUniOA+Khl7oUWcD1jQ=" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha1-YJIH1mEQADOpqUAq096mdzgcGx0=", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha1-dTm9S8Hg4KiVgVouAmJCCxKFhIg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", + "dev": true + }, + "html-loader": { + "version": "0.4.5", + "resolved": "https://registry.yarnpkg.com/html-loader/-/html-loader-0.4.5.tgz", + "integrity": "sha1-X7zYfNY6XEmn/OL+VvQl4Fcpxow=", + "dev": true, + "requires": { + "es6-templates": "^0.2.2", + "fastparse": "^1.1.1", + "html-minifier": "^3.0.1", + "loader-utils": "^1.0.2", + "object-assign": "^4.1.0" + }, + "dependencies": { + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha1-0AQOBUcw41TbAIRjWTGUAVIS0gw=", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz", + "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=", + "dev": true + } + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha1-QBVB8FNIhLv5UmAzTnL4juOXZUk=", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha1-TuenN6vZJniik9mzShr00NCMeHs=", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha1-VQcL44pZk88Y72236WH1vuXFoJ0=", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha1-xM78qo5RBRwqQLos6KPScpWvlGc=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/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.yarnpkg.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha1-1QYaYiS+WOgIOYX1AU2EQ1lXYpY=", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha1-c5P1r6Weyf9fZ6J2INEcIm4+7AI=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM=", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "dev": true + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + } + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha1-kEE1x3+0LAZB1qobzbxNqo2ggvM=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "dependencies": { + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "jasmine-ts": { + "version": "0.0.3", + "resolved": "https://registry.yarnpkg.com/jasmine-ts/-/jasmine-ts-0.0.3.tgz", + "integrity": "sha1-nfmQKc1j3P7fqiTzxorVSHCVa4o=", + "dev": true, + "requires": { + "jasmine": "^2.4.1", + "ts-node": "^1.2.1", + "typescript": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "jasmine": { + "version": "2.99.0", + "resolved": "https://registry.yarnpkg.com/jasmine/-/jasmine-2.99.0.tgz", + "integrity": "sha1-jKctEC5jm4Z8ZImFbg4YqceqQrc=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.99.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "ts-node": { + "version": "1.7.3", + "resolved": "https://registry.yarnpkg.com/ts-node/-/ts-node-1.7.3.tgz", + "integrity": "sha1-3uf4qEdRcy08Lkl8rFoC+xF9/uc=", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "chalk": "^1.1.1", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "pinkie": "^2.0.4", + "source-map-support": "^0.4.0", + "tsconfig": "^5.0.2", + "v8flags": "^2.0.11", + "xtend": "^4.0.0", + "yn": "^1.2.0" + } + }, + "tsconfig": { + "version": "5.0.3", + "resolved": "https://registry.yarnpkg.com/tsconfig/-/tsconfig-5.0.3.tgz", + "integrity": "sha1-X0J45wGACWeo/Dg/0ZZIh48qbjo=", + "dev": true, + "requires": { + "any-promise": "^1.3.0", + "parse-json": "^2.2.0", + "strip-bom": "^2.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha1-HL9h0F1rliaSROtqO85L2RTg8Aw=", + "dev": true + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "^1.1.1" + } + }, + "yn": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/yn/-/yn-1.3.0.tgz", + "integrity": "sha1-GwgSq7jYBdSJZvjfOF3J2syaGdg=", + "dev": true, + "requires": { + "object-assign": "^4.1.1" + } + } + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha1-JhHQcbec6g9D7lej0RhZOsFUfbE=", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha1-aOMlkd9z4lrRxLSRCKLsUHliv9E=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jquery": { + "version": "1.12.4", + "resolved": "https://registry.yarnpkg.com/jquery/-/jquery-1.12.4.tgz", + "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw=" + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha1-9OaGxd4eofhn28rT1G2WlCjfmMQ=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "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==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz", + "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsrsasign": { + "version": "10.1.13", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.1.13.tgz", + "integrity": "sha512-EKifn2DocDxU2fWVqTJgFYjZUcL4fTUtfgN5OQP4t4i/WOioios8wq350E1aJFxCLmtdxGNqhLX3O0tdVqJoFg==" + }, + "jszip": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "karma": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", + "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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 + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "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" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "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 + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha1-zxudBxNswY/iOTJ9JGVMPbw2is8=", + "dev": true, + "requires": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "karma-coverage": { + "version": "0.5.5", + "resolved": "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-0.5.5.tgz", + "integrity": "sha1-sNWLECXVnVxmICYxhvHVj11TSMU=", + "dev": true, + "requires": { + "dateformat": "^1.0.6", + "istanbul": "^0.4.0", + "minimatch": "^3.0.0", + "source-map": "^0.5.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "karma-es6-shim": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/karma-es6-shim/-/karma-es6-shim-1.0.0.tgz", + "integrity": "sha1-qutTCGK895NA69WI9PnlfehCtHk=", + "dev": true, + "requires": { + "es5-shim": "~4.5.8", + "es6-shim": "~0.35.0" + } + }, + "karma-jasmine": { + "version": "0.3.8", + "resolved": "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-0.3.8.tgz", + "integrity": "sha1-W2RXeRrZuJqhc/B54+vhuMgFI2w=", + "dev": true + }, + "karma-webpack": { + "version": "1.8.1", + "resolved": "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-1.8.1.tgz", + "integrity": "sha1-OdX9Lt7qPMPvW0BZibN9Ww5qO04=", + "dev": true, + "requires": { + "async": "~0.9.0", + "loader-utils": "^0.2.5", + "lodash": "^3.8.0", + "source-map": "^0.1.41", + "webpack-dev-middleware": "^1.0.11" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha1-bvXS32DlL4LrIopMNz6NHzlyU88=", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha1-7UcGa/5TTX6ExMe5mYwqdWB9k1c=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash._createcompounder": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz", + "integrity": "sha1-XdLLVTctbnDg4jkvsjBNZjEJEHU=", + "dev": true, + "requires": { + "lodash.deburr": "^3.0.0", + "lodash.words": "^3.0.0" + } + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash.camelcase": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz", + "integrity": "sha1-kyyLh/ikN3iXxnGXUzKC+Xrqwpg=", + "dev": true, + "requires": { + "lodash._createcompounder": "^3.0.0" + } + }, + "lodash.deburr": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz", + "integrity": "sha1-baj1QzSjZqfPTEx2742Aqhs2XtU=", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "lodash.words": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/lodash.words/-/lodash.words-3.2.0.tgz", + "integrity": "sha1-TiqGSbwIdFsXxpWxo86P7llmI7M=", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, + "log4js": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.8.0.tgz", + "integrity": "sha512-g+V8gZyurIexrOvWQ+AcZsIvuK/lBnx2argejZxL4gVZ4Hq02kUYH6WZOnqxgBml+zzQZYdaEoTN84B6Hzm8Fg==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha1-QV6WcEazp/HRhSd9hKpYIDcmoT8=", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha1-LrLjfqm2fEiR9oShOUeZr0hM96I=", + "dev": true + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha1-rSyVdhl8nxq/MI0Hh4Zb2XWj8+Q=", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha1-fVg6cwZDTAVf5HSw9FB45uG0uSo=", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-expression-evaluator": { + "version": "1.2.22", + "resolved": "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz", + "integrity": "sha1-wU3LPYtNFQ5dzqnGjI2tgDCbDV4=", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha1-tdB7jjIW4+J81yjXL3DR5qNCAF8=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz", + "integrity": "sha1-Rhr0l7xK4JYIzbLmDu+2m/90QXg=", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha1-ftLCzMyvhNP/y3pptXcR/CCDQBs=", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha1-fUL/HzljVILhX5zbUxhN7r1YFf0=", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha1-IrgTv3Rdxu26JXa5QAIq1u3Ixhc=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha1-gucTXX6JpQ/+ZGEKeHlTxMTLs3M=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha1-aEcveXEcCEZXwGfFxq2Tzd6oIUw=", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha1-6goykfl+C16HdrNj1fChLZTGcCI=", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.27.0", + "resolved": "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz", + "integrity": "sha1-i/9OPiaiNiIN/j423nVrbrqgEF0=" + }, + "moment-timezone": { + "version": "0.4.1", + "resolved": "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.4.1.tgz", + "integrity": "sha1-gfWYw61eIs2teWtn7NjYjQ9bqgY=", + "requires": { + "moment": ">= 2.6.0" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true + } + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha1-tKr7k+OustgXTKU88WOrfXMIMF8=", + "dev": true + }, + "ng-metadata": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/ng-metadata/-/ng-metadata-4.0.1.tgz", + "integrity": "sha1-w2cBisnlwhTFe5h+gpQKMP1LnGc=" + }, + "ngtemplate-loader": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/ngtemplate-loader/-/ngtemplate-loader-1.3.1.tgz", + "integrity": "sha1-v9BaHZoSAEFpjAb2FlP2BrOcEGA=", + "dev": true, + "requires": { + "jsesc": "^0.5.0", + "loader-utils": "~0.2.6" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha1-tk9RPRgzhiX5A0bSew0jXmMfZCU=", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "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" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz", + "integrity": "sha1-bS8Od/GgrwAyrKcWwsH7uOfoq+0=", + "dev": true + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha1-hPodA2/p08fiHZmIS2ARZ+yPtJU=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha1-qAKm7hfyTBBIOrmTVxnO9O0Wvxo=", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha1-kYzrrqJIpiz3/6uOO8qMX4gvxC4=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha1-1wTZr4orpoTiYA2aIVmD1BQal50=", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz", + "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha1-kEnKN9bLIYLDsdLHIL6U0UpYFPw=", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha1-ADJxND2ljclMrOSU+u89IUfs6g4=", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha1-5sTd1+06onxoogzE5Q4aTug7vEo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha1-y4cksPramEWWhW0abrr9NYRlS5Q=", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha1-KiI8s9x7YhPXQOQDcr5A3kPmWxs=" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "requires": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "requires": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "requires": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "requires": { + "postcss": "^5.0.16" + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "requires": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha1-giRf34IzcEFkXkdxFNjlk6oYuOw=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "requires": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "requires": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha1-3IfjQUjsfqtfeR981YSYMzdbdBo=", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "^5.0.5" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "requires": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE=", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz", + "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "protractor": { + "version": "5.4.4", + "resolved": "https://registry.yarnpkg.com/protractor/-/protractor-5.4.4.tgz", + "integrity": "sha1-skFGaq+Dt2vCxY32feuaXN/GFSk=", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.0.6", + "yargs": "^12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha1-BfWZe2CWR7ZPZrgeO0sQo2jnrRM=", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + } + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz", + "integrity": "sha1-kyb4vPsBOtzABf3/BWrM4CDlHCQ=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha1-T8ydd6B+SLp1J+fL4N4z0HATMeA=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha1-YOWl/WSn+L+k0qsu1v30yFutFU4=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", + "dev": true + }, + "raven-js": { + "version": "3.27.2", + "resolved": "https://registry.yarnpkg.com/raven-js/-/raven-js-3.27.2.tgz", + "integrity": "sha1-bDPflSAmzXOCCqmZEit7dzemZ3U=" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "dev": true, + "requires": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "requires": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz", + "integrity": "sha1-YDUPf7JSwKZ+sQ/UaU0WkJlxMA8=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "regenerate": { + "version": "1.4.1", + "resolved": "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha1-ytkq2Oa1kXc0hfvgWkhcr09Ffm8=", + "dev": true + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true, + "optional": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz", + "integrity": "sha1-1zyRhzHLWofaBH4gcjQUb2ZNErM=", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz", + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha1-0LMp7MfMD2Fkn2IhW+aa9UqomJs=" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha1-sllBtUloIxzC0bt2p5y38sC/hEQ=", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restangular": { + "version": "1.6.1", + "resolved": "https://registry.yarnpkg.com/restangular/-/restangular-1.6.1.tgz", + "integrity": "sha1-rdF0PyfXewuKZS5khadgtiNO0CQ=", + "requires": { + "lodash": "~4.17.0" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "5.5.7", + "resolved": "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.7.tgz", + "integrity": "sha1-r7PRZCsGmy+/IDkD1lAdGstM2ic=", + "requires": { + "symbol-observable": "1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "dev": true + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha1-lAWnPDYNRJsjKDmRmobDltN5/Z0=", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "script-loader": { + "version": "0.7.2", + "resolved": "https://registry.yarnpkg.com/script-loader/-/script-loader-0.7.2.tgz", + "integrity": "sha1-IBbbb4byX1z1baOJFdgzeLsWa6c=", + "dev": true, + "requires": { + "raw-loader": "~0.5.1" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha1-K6h6FmLAILiYjJga5iyyoBKY6vw=", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz", + "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha1-bGGeT5xgMIw4UZSYwU+7EKrOuwY=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha1-i/OpFwcSZk7yVhtEtpHq/jmSFOo=", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "showdown": { + "version": "1.9.1", + "resolved": "https://registry.yarnpkg.com/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha1-E04UjnXNRiPgnCGwURl315ta0O8=", + "requires": { + "yargs": "^14.2" + }, + "dependencies": { + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha1-Ghw+3O0a+yov6jNgS8bR2NaIpBQ=", + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha1-VHhq9AuCDcsvuAJbEbTWWddjI7M=", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "requires": { + "ws": "~8.11.0" + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + }, + "source-map-loader": { + "version": "0.1.5", + "resolved": "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.1.5.tgz", + "integrity": "sha1-DsbKWKoOYUY6KGc91MSei6Z9xxg=", + "dev": true, + "requires": { + "async": "^0.9.0", + "loader-utils": "~0.2.2", + "source-map": "~0.1.33" + }, + "dependencies": { + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", + "dev": true, + "requires": { + "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha1-3s6BrJweZxPl99G28X1Gj6U9iak=", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha1-PyjOGnegA3JoPq3kpDMYNSeiFj0=", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha1-NpS1gEVnpFjTyARYQqY1hjL2JlQ=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha1-ksJBv23oI2W1x/tL126XVSLhKU0=", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha1-h1IdOKRKp+6RzhzSpH3wy0ndZgs=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha1-6+J6DDibBPvMIzZClS4Qcxr6m64=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha1-1wiCgVWasneEJCebCHfaPDktWj0=", + "dev": true + }, + "streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", + "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.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "style-loader": { + "version": "0.13.1", + "resolved": "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.1.tgz", + "integrity": "sha1-RoKA77wEcwI806bNVuM7Wh1/w6k=", + "dev": true, + "requires": { + "loader-utils": "^0.2.7" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + } + } + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "requires": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + } + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha1-ofzMBrWNth/XpF2i2kT186Pme6I=", + "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz", + "integrity": "sha1-YwVjQ9fHC7KfOvZlhlpG/gOg3xc=", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha1-qYti+G3K9PZzmWSMCFKRq56P7WE=", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "2.3.7", + "resolved": "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.7.tgz", + "integrity": "sha1-SRD/XRqHIWjMf6bNN0nisNYKigs=", + "dev": true, + "requires": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.6.12", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha1-FxUfdtjq5n+793lgwzxnatn078c=", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "time-stamp": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz", + "integrity": "sha1-kX4KZpBWiHkOx7u94EBGJZr4P1c=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha1-gAsfPu4nLlvFPuRloE0OgEwxIR8=", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=" + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha1-zZ+yoKodWhK0c72fuW+j3P9lreI=", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha1-8shUBoALmw90yfdGW4HqrSQSUvg=", + "dev": true + }, + "ts-loader": { + "version": "6.2.0", + "resolved": "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.2.0.tgz", + "integrity": "sha1-UtOZPsvFR0wVEyQjiOEEnaD86IA=", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "ts-mocks": { + "version": "0.2.2", + "resolved": "https://registry.yarnpkg.com/ts-mocks/-/ts-mocks-0.2.2.tgz", + "integrity": "sha1-BR5bOjAGj2ufGx+qVSpvFyeTxtY=", + "dev": true + }, + "ts-node": { + "version": "3.3.0", + "resolved": "https://registry.yarnpkg.com/ts-node/-/ts-node-3.3.0.tgz", + "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "chalk": "^2.0.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.0", + "tsconfig": "^6.0.0", + "v8flags": "^3.0.0", + "yn": "^2.0.0" + } + }, + "tsconfig": { + "version": "6.0.0", + "resolved": "https://registry.yarnpkg.com/tsconfig/-/tsconfig-6.0.0.tgz", + "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha1-yIHhPMcBWJTtkUhi0nZDb6mkcEM=", + "dev": true + }, + "tslint": { + "version": "5.20.1", + "resolved": "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha1-5AHortoBUrxE3QfmFANPP4DGe30=", + "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.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz", + "integrity": "sha1-YPOuy4nV+uUgwRqhnvwruYKq3n0=", + "dev": true + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha1-MrSIUBRnrL7dS4VJhnOggSrKC5k=", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.6.3", + "resolved": "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha1-/qlC+rsg9+HKcWT/Ym8anz9wtNo=", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha1-mtlWPY6zrN+404WX0q8dgV9qdV8=", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz", + "integrity": "sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=", + "dev": true + } + } + }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha1-c9aqNmjzGI5K2w8ZQ70Sz9fvqq8=" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha1-HWl2k2mtoFgxA6HmrodoG1ZXMjA=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha1-uqvOkQg/xk6UWw861hPiZPfNTmw=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urijs": { + "version": "1.19.2", + "resolved": "https://registry.yarnpkg.com/urijs/-/urijs-1.19.2.tgz", + "integrity": "sha1-+b4J8AxMUTS3yzz0dcHdOUUmJlo=" + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha1-qKg1NejACjFuQDpdtKwbm4U64ng=", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "dev": true + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz", + "integrity": "sha1-MjZzNyDsZLsn9uJvQhqqLhtYjWE=", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" + }, + "v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha1-APdJTSritojP4omd9u0sVL75Hb4=", + "dev": true + }, + "v8flags": { + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha1-skPjtN/XMfp3TnSSEoEJoP5m1lY=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha1-4rgApT56Kbk1BsPPQRANFsTErY4=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "extsprintf": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=", + "dev": true + } + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha1-eGQcSIuObKkadfUR56OzKobl3aA=", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.7.4", + "resolved": "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz", + "integrity": "sha1-bp2lOzyAuy1lCBiPWyAEEIZs0ws=", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.0" + } + }, + "watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } + } + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha1-V9epPADbTMjVVuTT20tdsKgMO7c=", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webdriver-manager": { + "version": "12.1.7", + "resolved": "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha1-7U6u6PkGszwUboabVehQVTobEWI=", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "webpack": { + "version": "4.41.0", + "resolved": "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz", + "integrity": "sha1-22olS95nF2n3wU6QoaVec2Avxws=", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.1", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha1-ZovL0QWutfHZL+JVcOyVJcj6pAw=", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha1-KjxBso3UW2K2Nnbst0ABJlrp7dg=", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "terser-webpack-plugin": { + "version": "1.4.4", + "resolved": "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", + "integrity": "sha1-LGNUQ0cyS6r6mla6rd8WNMir/C8=", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "3.5.2", + "resolved": "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.5.2.tgz", + "integrity": "sha1-rAKDT0sx3o4n1x5semEjAevdt58=", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-walk": "^6.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "ws": { + "version": "6.2.1", + "resolved": "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz", + "integrity": "sha1-RC/fCkftZPWbal2P8TD0dI7VJPs=", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "webpack-cli": { + "version": "3.3.9", + "resolved": "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.9.tgz", + "integrity": "sha1-ecJ+cflLf+Mk1ZSrZKjjlrnaqRo=", + "dev": true, + "requires": { + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" + }, + "dependencies": { + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha1-Qcfgv9/nSsH/4eV61qXGyfN0Kn8=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha1-+PwRIM47T8VoDO7LQ9d3lmshEF4=", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha1-OZO9hzv8SEecyp6jpUeDXHwVSzQ=", + "dev": true + } + } + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha1-YQY29rH3A4kb00dxzLF/uTtHB5w=", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha1-JqlMU5G7ypJhUgAvabhKS/dy5ag=", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha1-H9H2cjXVttD+54EFYAG/tpTAOwk=", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "requires": {} + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha1-oMaVFnUkIesqx1juTUzPWIQ+rGY=", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha1-vpuuHIoEbnazESdyY0fQrXACvrM=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha1-le+U+F7MgdAHwmThkKEg8KPIVms=" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha1-C1YreUAW65ZRuYvTes82SqXW3IM=", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + }, + "dependencies": { + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha1-Ew8JcC667vJlDVTObj5XBvek+zg=", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha1-h5oIZZc7yp9rq1y987HGfsfTvPQ=", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zeroclipboard": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/zeroclipboard/-/zeroclipboard-2.3.0.tgz", + "integrity": "sha1-WS69gzpDCGiLBzlpfT2/mJACya8=" + } + } +} diff --git a/config-tool/pkg/lib/editor/package.json b/config-tool/pkg/lib/editor/package.json new file mode 100644 index 000000000..02951b612 --- /dev/null +++ b/config-tool/pkg/lib/editor/package.json @@ -0,0 +1,89 @@ +{ + "name": "quay-config-editor", + "license": "UNLICENSED", + "private": true, + "version": "1.0.0", + "scripts": { + "clean": "rm -f static/build/*", + "watch": "npm run clean && webpack --watch --mode=development", + "build": "npm run clean && NODE_ENV=production webpack --mode=production --progress", + "dev-build": "npm run clean && webpack --mode=development" + }, + "repository": { + "type": "git", + "url": "https://github.com/quay/quay.git", + "directory": "config-tool" + }, + "homepage": "https://github.com/quay/quay/tree/HEAD/config-tool#readme", + "dependencies": { + "angular": "1.6.2", + "angular-animate": "1.6.2", + "angular-cookies": "1.6.2", + "angular-file-upload": "^2.6.0", + "angular-route": "1.6.2", + "angular-sanitize": "1.6.2", + "bootbox": "^5.4.0", + "bootstrap": "^3.3.2", + "bootstrap-datepicker": "^1.6.4", + "cal-heatmap": "^3.3.10", + "clipboard": "^1.6.1", + "core-js": "^2.4.1", + "d3": "^3.3.3", + "eonasdan-bootstrap-datetimepicker": "^4.17.43", + "file-saver": "^1.3.8", + "highlight.js": "^9.12.0", + "jquery": "1.12.4", + "js-yaml": "^3.14.0", + "jsrsasign": "^10.1.13", + "jszip": "^3.5.0", + "moment": "^2.10", + "moment-timezone": "^0.4.0", + "ng-metadata": "^4.0.1", + "node-forge": "^0.10.0", + "raven-js": "^3.1.0", + "restangular": "^1.6.1", + "rxjs": "5.5.7", + "showdown": "^1.6.4", + "underscore": "^1.5.2", + "urijs": "^1.18.10", + "url-parse": "^1.4.0", + "uuid": "^8.3.1", + "zeroclipboard": "^2.3.0" + }, + "devDependencies": { + "@types/angular": "1.6.2", + "@types/angular-mocks": "^1.5.8", + "@types/angular-route": "^1.3.3", + "@types/angular-sanitize": "^1.3.4", + "@types/core-js": "^0.9.39", + "@types/jasmine": "^2.5.41", + "@types/jquery": "^2.0.40", + "@types/node": "^14.0.27", + "@types/showdown": "^1.4.32", + "angular-mocks": "1.6.2", + "css-loader": "0.25.0", + "html-loader": "^0.4.5", + "jasmine-core": "^2.5.2", + "jasmine-ts": "0.0.3", + "karma": "^6.4.1", + "karma-chrome-launcher": "^2.1.1", + "karma-coverage": "^0.5.5", + "karma-es6-shim": "^1.0.0", + "karma-jasmine": "^0.3.8", + "karma-webpack": "^1.8.1", + "ngtemplate-loader": "^1.3.1", + "protractor": "^5.1.2", + "script-loader": "^0.7.0", + "source-map-loader": "0.1.5", + "style-loader": "0.13.1", + "terser-webpack-plugin": "^2.1.2", + "ts-loader": "6.2.0", + "ts-mocks": "^0.2.2", + "ts-node": "^3.0.6", + "tslint": "^5.4.3", + "typescript": "3.6.3", + "webpack": "4.41.0", + "webpack-bundle-analyzer": "3.5.2", + "webpack-cli": "3.3.9" + } +} diff --git a/config-tool/pkg/lib/editor/static/css/config-setup-app-component.css b/config-tool/pkg/lib/editor/static/css/config-setup-app-component.css new file mode 100644 index 000000000..2d41544d9 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/css/config-setup-app-component.css @@ -0,0 +1,93 @@ +.config-setup-wrapper { + display: flex; +} + +.config-setup_option { + font-size: 22px; + height: 250px; + display: flex; + flex: 1; + flex-direction: column; + align-items: center; + padding: 15px; + margin: 15px; + justify-content: space-evenly; +} + +.config-setup_option i { + padding-bottom: 10px; +} + +.config-setup_option div { + text-align: center; + min-height: 100px; +} + +.config-setup_option:hover { + background-color: #dddddd; + text-decoration: none; +} + +/* Overrides for fixing old quay styles*/ + +.quay-config-app .alert-danger { + padding: 25px; + display: flex; +} + +.quay-config-app .alert-danger:before { + content: "\f071"; + font-family: Font Awesome\ 5 Free; + font-weight: 900; + font-size: 30px; + padding-right: 15px; + color: #c53c3f; + text-align: center; +} + +.quay-config-app .co-alert.co-alert-success { + padding: 25px; + display: flex; + margin-bottom: 0; + text-align: left; +} + +.quay-config-app .co-alert.co-alert-success:before { + font-family: Font Awesome\ 5 Free; + font-weight: 900; + font-size: 30px; + padding-right: 15px; + color: green; + text-align: center; + position: static; +} + +.co-alert.co-alert-danger:after { + /* Ignore the exclamation mark, it also messes with spacing elements */ + content: none; +} + +/* Fixes the transition to font awesome 5 */ +.quay-config-app .co-alert.co-alert-warning::before { + font-family: Font Awesome\ 5 Free; + font-weight: 900; +} + +.quay-config-app .co-alert.co-alert-info::before { + font-family: Font Awesome\ 5 Free; + font-weight: 900; +} + +.quay-config-app .co-alert.co-alert-danger::after { + font-family: Font Awesome\ 5 Free; + font-weight: 900; + /* Font Awesome 5's icons are slightly bigger, so we have to adjust this one because it is inside another icon */ + font-size: 12px; + top: 18px; + left: 20.75px; +} + +.quay-config-app .co-modify-link::after { + font-family: Font Awesome\ 5 Free; + font-weight: 900; +} diff --git a/config-tool/pkg/lib/editor/static/css/cor-option.css b/config-tool/pkg/lib/editor/static/css/cor-option.css new file mode 100644 index 000000000..97ae7887d --- /dev/null +++ b/config-tool/pkg/lib/editor/static/css/cor-option.css @@ -0,0 +1,8 @@ +.cor-options-menu .fa-cog { + color: #999; + cursor: pointer; +} + +.open .fa-cog { + color: #428BCA; +} diff --git a/config-tool/pkg/lib/editor/static/css/cor-title.css b/config-tool/pkg/lib/editor/static/css/cor-title.css new file mode 100644 index 000000000..ef199785a --- /dev/null +++ b/config-tool/pkg/lib/editor/static/css/cor-title.css @@ -0,0 +1,4 @@ +.cor-title { + display: flex; + justify-content: center; +} diff --git a/config-tool/pkg/lib/editor/static/css/core-ui.css b/config-tool/pkg/lib/editor/static/css/core-ui.css new file mode 100644 index 000000000..b60db4c3c --- /dev/null +++ b/config-tool/pkg/lib/editor/static/css/core-ui.css @@ -0,0 +1,1506 @@ +/* Global Brand Bar */ +.co-m-global-nav { + background: white; + height: 30px; + line-height: 36px; + position: relative; + z-index: 90; +} + +.co-m-global-nav svg { + width: auto !important; +} + +.co-m-global-nav .co-m-global-nav-left { + text-align: left; + padding-left: 28px; +} + +.co-m-global-nav .co-m-global-nav-right { + text-align: right; + font-size: 16px; + line-height: 30px; + padding-right: 25px; +} + +.co-m-global-nav .co-m-global-nav-item { + padding: 0 20px 0 15px; + border-right: 1px solid #eee; + display: inline-block; + height: 16px; + line-height: 16px; +} + +.co-m-global-nav .co-m-global-nav-item:first-of-type { + padding-left: 0; +} + +.co-m-global-nav .co-m-global-nav-item:last-of-type { + padding-right: 0; + border-right: 0; +} + +/* Tweaks for small screens */ +@media screen and (max-width: 767px) { + .co-m-global-nav { + display: none; /* hide the whole thing */ + } +} + +a:active { + outline: none !important; +} + +a:focus { + outline: none !important; +} + +.co-form-table label { + white-space: nowrap; +} + +.co-form-table td { + padding: 8px; +} + +.co-form-table td:first-child { + vertical-align: top; + padding-top: 14px; +} + +.co-form-table td .co-help-text { + margin-top: 10px; + margin-bottom: 4px; +} + +.co-help-text { + margin-top: 6px; + color: #aaa; + display: inline-block; +} + +.co-options-menu .fa-gear { + color: #999; + cursor: pointer; +} + +.co-options-menu .dropdown.open .fa-gear { + color: #428BCA; +} + +.co-img-bg-network { + background: url('/static/img/network-tile.png') left top repeat, linear-gradient(30deg, #2277ad, #144768) no-repeat left top fixed; + background-color: #2277ad; + background-size: auto, 100% 100%; +} + +.co-m-navbar { + background-color: white; + margin: 0; + padding-left: 10px; +} + +.co-fx-box-shadow { + -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); + -ms-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); + -o-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); +} + +.co-fx-box-shadow-heavy { + -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -ms-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -o-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); +} + +.co-fx-text-shadow { + text-shadow: rgba(0, 0, 0, 1) 1px 1px 2px; +} + +.co-nav-title { + margin-top: -22px; + height: 70px; +} + +.co-nav-title .co-nav-title-content { + color: white; + text-align: center; + white-space: nowrap; +} + +.co-nav-title .co-nav-title-action { + display: block; + color: white; + text-align: center; + line-height: 70px; + font-size: 18px; +} + +.co-nav-title .co-nav-title-action a { + color: white; +} + +.co-nav-title .co-nav-title-action .fa { + margin-right: 6px; +} + + +@media (max-width: 767px) { + .co-nav-title { + height: auto; + min-height: 70px; + } + + .co-nav-title .co-nav-title-content { + height: 34px; + overflow: hidden; + text-overflow: ellipsis; + font-size: 22px; + } +} + +.co-main-content-panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + padding: 10px; + + -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -ms-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -o-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); +} + +.cor-log-box { + width: 100%; + height: 550px; + position: relative; +} + +.co-log-viewer { + position: absolute; + top: 20px; + left: 20px; + right: 20px; + height: 500px; + + padding: 20px; + + background: rgb(55, 55, 55); + border: 1px solid black; + color: white; + + overflow: scroll; +} + +.co-log-viewer .co-log-content { + font-family: Consolas, "Lucida Console", Monaco, monospace; + font-size: 12px; + white-space: pre; +} + +.cor-log-box .co-log-viewer-new-logs i { + margin-left: 10px; + display: inline-block; +} + +.cor-log-box .co-log-viewer-new-logs { + cursor: pointer; + position: absolute; + bottom: 40px; + right: 30px; + padding: 10px; + color: white; + border-radius: 10px; + background: rgba(72, 158, 72, 0.8); +} + +.co-panel { + margin-bottom: 40px; + + /*border: 1px solid #eee;*/ +} + +.co-panel .co-panel-heading img { + margin-right: 6px; + width: 24px; +} + +.co-panel .co-panel-heading > i.fa { + margin-right: 6px; + width: 24px; + text-align: center; +} + +.co-panel .co-panel-heading { + padding: 6px; + /*background: #eee;*/ + border-bottom: 1px solid #eee; + + margin-bottom: 4px; + font-size: 135%; + padding-left: 10px; +} + +.co-panel .co-panel-body { + padding: 10px; +} + +@media (max-width: 767px) { + .co-panel > .co-panel-body { + padding: 0px; + padding-top: 10px; + padding-bottom: 10px; + } + + .co-panel > .panel-body { + padding: 0px; + padding-top: 10px; + padding-bottom: 10px; + } +} + + +.co-panel .co-panel-button-bar { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #eee; +} + +.co-panel-body .co-panel-heading { + font-size: 120%; + border-bottom: 0px; + margin: 0px; + margin-bottom: -6px; +} + +.co-panel-body .co-panel-body { + padding-left: 38px; +} + + +.config-bool-field-element input { + margin-right: 6px; + font-size: 24px; +} + +.config-setup-tool-element .help-text { + margin-top: 6px; + color: #aaa; +} + +.config-setup-tool-element .description { + padding: 6px; +} + +.config-setup-tool-element .config-table > tbody > tr > td:first-child { + padding-top: 14px; + font-weight: bold; +} + +.config-setup-tool-element .config-table > tbody > tr > td.non-input { + padding-top: 8px; +} + +.config-setup-tool-element .config-table > tbody > tr > td { + padding: 8px; + vertical-align: top; +} + +.config-setup-tool-element .config-table > tbody > tr > td .config-numeric-field-element { + width: 100px; +} + +.config-setup-tool-element .config-table > tbody > tr > td .config-string-field-element { + width: 400px; +} + +.config-setup-tool-element .config-table > tbody > tr > td .config-string-list-field-element { + width: 400px; +} + +.config-map-field-element table { + margin-bottom: 10px; +} + +.config-map-field-element .form-control-container { + border-top: 1px solid #eee; + padding-top: 10px; +} + +.config-map-field-element .form-control-container select, .config-map-field-element .form-control-container input { + margin-bottom: 10px; +} + +.config-map-field-element .empty { + color: #ccc; + margin-bottom: 10px; + display: block; +} + +.config-map-field-element .item-title { + font-weight: bold; +} + +.config-contact-field { + margin-bottom: 4px; +} + +.config-contact-field .dropdown button { + width: 100px; + text-align: left; +} + +.config-contact-field .dropdown button .caret { + float: right; + margin-top: 9px; +} + +.config-contact-field .dropdown button i.fa { + margin-right: 6px; + width: 14px; + text-align: center; + display: inline-block; +} + +.config-contact-field .form-control { + width: 350px; +} + +.config-certificates-field-element .dns-name { + display: inline-block; + margin-right: 10px; +} + +.config-certificates-field-element .cert-status .fa { + margin-right: 4px; +} + +.config-certificates-field-element .cert-status .green { + color: #2FC98E; +} + +.config-certificates-field-element .cert-status .orange { + color: #FCA657; +} + +.config-certificates-field-element .cert-status .red { + color: #D64456; +} + +.config-certificates-field-element .file-upload-box-element .file-input-container { + padding: 0px; + text-align: left; +} + +.config-certificates-field-element .file-upload-box-element .file-drop + label { + margin-top: 0px; + margin-bottom: 4px; +} + +.config-list-field-element .empty { + color: #ccc; + margin-bottom: 10px; + display: block; +} + +.config-list-field-element input { + vertical-align: middle; +} + +.config-list-field-element .item-delete { + display: inline-block; + margin-left: 20px; +} + +.config-list-field-element input { + width: 350px; +} + +.config-setup-tool-element .inner-table { + margin-left: 10px; +} + +.config-setup-tool-element .inner-table tr td:first-child { + font-weight: bold; +} + +.config-setup-tool-element .inner-table td { + padding: 6px; +} + +.config-file-field-element input { + display: inline-block; + margin-left: 10px; +} + +.config-service-key-field-element { + position: relative; +} + +.config-service-key-field-element .co-modify-link { + margin-left: 10px; +} + +.config-service-key-field-element .fa-check { + margin-right: 4px; +} + +.co-checkbox { + position: relative; +} + +.co-checkbox input { + display: none; +} + +.co-checkbox label { + position: relative; + padding-left: 28px; + cursor: pointer; +} + +.co-checkbox label:before { + content: ''; + cursor: pointer; + position: absolute; + width: 20px; + height: 20px; + top: 0; + left: 0; + border-radius: 4px; + + -webkit-box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,.4); + -moz-box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,.4); + box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,.4); + + background: -webkit-linear-gradient(top, #222 0%, #45484d 100%); + background: -moz-linear-gradient(top, #222 0%, #45484d 100%); + background: -o-linear-gradient(top, #222 0%, #45484d 100%); + background: -ms-linear-gradient(top, #222 0%, #45484d 100%); + background: linear-gradient(top, #222 0%, #45484d 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#222', endColorstr='#45484d',GradientType=0 ); +} + +.co-checkbox label:after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + opacity: 0; + content: ''; + position: absolute; + width: 11px; + height: 7px; + background: transparent; + top: 5px; + left: 4px; + border: 3px solid #fcfff4; + border-top: none; + border-right: none; + + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); +} + +.co-checkbox label:hover::after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + filter: alpha(opacity=30); + opacity: 0.3; +} + +.co-checkbox input[type=checkbox]:checked + label:after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; + border: 3px solid rgb(26, 255, 26); + border-top: none; + border-right: none; +} + +.co-floating-bottom-bar { + height: 50px; +} + +.co-floating-bottom-bar.floating { + position: fixed; + bottom: 0px; +} + +.config-setup-tool .cor-floating-bottom-bar button i.fa { + margin-right: 6px; +} + +.config-setup-tool .service-verification { + padding: 20px; + background: #343434; + color: white; + margin-bottom: -14px; +} + +.config-setup-tool .service-verification-row { + margin-bottom: 6px; +} + +.config-setup-tool .service-verification-row .service-title { + font-variant: small-caps; + font-size: 145%; + vertical-align: middle; +} + +#validateAndSaveModal .fa-warning { + font-size: 22px; + margin-right: 10px; + vertical-align: middle; + color: rgb(255, 186, 53); +} + +#validateAndSaveModal .fa-check-circle { + font-size: 22px; + margin-right: 10px; + vertical-align: middle; + color: rgb(53, 186, 53); +} + + +.config-setup-tool .service-verification-error { + margin-top: 10px; + margin-left: 36px; + margin-bottom: 20px; + max-height: 250px; + overflow: auto; + border: 1px solid #797979; + background: #000; + padding: 6px; + font-family: Consolas,Lucida Console,Monaco,monospace; + font-size: 12px; +} + +.config-setup-tool .validation-issue { + color: white; + padding: 10px; + margin-bottom: 6px; +} + +.co-m-loader, .co-m-inline-loader { + min-width: 28px; } + +.co-m-loader { + display: block; + position: absolute; + left: 50%; + top: 50%; + margin: -11px 0 0 -13px; } + +.co-m-inline-loader { + display: inline-block; + cursor: default; } + .co-m-inline-loader:hover { + text-decoration: none; } + +.co-m-loader-dot__one, .co-m-loader-dot__two, .co-m-loader-dot__three { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + animation-fill-mode: both; + -webkit-animation-fill-mode: both; + -moz-animation-fill-mode: both; + -ms-animation-fill-mode: both; + -o-animation-fill-mode: both; + animation-name: bouncedelay; + animation-duration: 1s; + animation-timing-function: ease-in-out; + animation-delay: 0; + animation-direction: normal; + animation-iteration-count: infinite; + animation-fill-mode: forwards; + animation-play-state: running; + -webkit-animation-name: bouncedelay; + -webkit-animation-duration: 1s; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-delay: 0; + -webkit-animation-direction: normal; + -webkit-animation-iteration-count: infinite; + -webkit-animation-fill-mode: forwards; + -webkit-animation-play-state: running; + -moz-animation-name: bouncedelay; + -moz-animation-duration: 1s; + -moz-animation-timing-function: ease-in-out; + -moz-animation-delay: 0; + -moz-animation-direction: normal; + -moz-animation-iteration-count: infinite; + -moz-animation-fill-mode: forwards; + -moz-animation-play-state: running; + display: inline-block; + height: 6px; + width: 6px; + background: #419eda; + border-radius: 100%; + display: inline-block; } + +.co-m-loader-dot__one { + animation-delay: -0.32s; + -webkit-animation-delay: -0.32s; + -moz-animation-delay: -0.32s; + -ms-animation-delay: -0.32s; + -o-animation-delay: -0.32s; } + +.co-m-loader-dot__two { + animation-delay: -0.16s; + -webkit-animation-delay: -0.16s; + -moz-animation-delay: -0.16s; + -ms-animation-delay: -0.16s; + -o-animation-delay: -0.16s; } + +@-webkit-keyframes bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0.25, 0.25); + -moz-transform: scale(0.25, 0.25); + -ms-transform: scale(0.25, 0.25); + -o-transform: scale(0.25, 0.25); + transform: scale(0.25, 0.25); } + + 40% { + -webkit-transform: scale(1, 1); + -moz-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); } } + +@-moz-keyframes bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0.25, 0.25); + -moz-transform: scale(0.25, 0.25); + -ms-transform: scale(0.25, 0.25); + -o-transform: scale(0.25, 0.25); + transform: scale(0.25, 0.25); } + + 40% { + -webkit-transform: scale(1, 1); + -moz-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); } } + +@-ms-keyframes bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0.25, 0.25); + -moz-transform: scale(0.25, 0.25); + -ms-transform: scale(0.25, 0.25); + -o-transform: scale(0.25, 0.25); + transform: scale(0.25, 0.25); } + + 40% { + -webkit-transform: scale(1, 1); + -moz-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); } } + +@keyframes bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0.25, 0.25); + -moz-transform: scale(0.25, 0.25); + -ms-transform: scale(0.25, 0.25); + -o-transform: scale(0.25, 0.25); + transform: scale(0.25, 0.25); } + + 40% { + -webkit-transform: scale(1, 1); + -moz-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); } } + +.co-dialog .modal-body { + padding: 10px; + min-height: 100px; +} + +.co-dialog .modal-body h4 { + margin-bottom: 20px; +} + +.co-dialog .modal-content { + border-radius: 0px; +} + +.co-dialog.fatal-error .modal-content { + padding-left: 175px; +} + +.co-dialog.fatal-error .alert-icon-container-container { + position: absolute; + top: -36px; + left: -175px; + bottom: 20px; +} + +.co-dialog.fatal-error .alert-icon-container { + height: 100%; + display: table; +} + +.co-dialog.fatal-error .alert-icon { + display: table-cell; + vertical-align: middle; + border-right: 1px solid #eee; + margin-right: 20px; +} + +.co-dialog.fatal-error .alert-icon:before { + content: "\f071"; + font-family: FontAwesome; + font-size: 60px; + padding-left: 50px; + padding-right: 50px; + color: #c53c3f; + text-align: center; +} + + +.co-dialog .modal-header .cor-step-bar { + float: right; +} + +.co-dialog .modal-footer.working { + text-align: left; +} + +.co-dialog .modal-footer.working .btn { + float: right; +} + +.co-dialog .modal-footer.working .cor-loader-inline { + margin-right: 10px; +} + +.co-dialog .modal-footer .left-align { + float: left; + vertical-align: middle; + font-size: 16px; + margin-top: 8px; +} + +.co-dialog .modal-footer .left-align i.fa-warning { + color: #ffba35; + display: inline-block; + margin-right: 6px; +} + +.co-dialog .modal-footer .left-align i.fa-check { + color: green; + display: inline-block; + margin-right: 6px; +} + +.co-dialog .co-single-field-dialog { + padding: 10px; +} + +.co-dialog .co-single-field-dialog input { + margin-top: 10px; +} + +.co-step-bar .co-step-element { + cursor: default; + display: inline-block; + width: 28px; + height: 28px; + + position: relative; + color: #ddd; + + text-align: center; + line-height: 24px; + font-size: 16px; +} + +.co-step-bar .co-step-element.text { + margin-left: 24px; + background: white; +} + +.co-step-bar .co-step-element.icon { + margin-left: 22px; +} + +.co-step-bar .co-step-element:first-child { + margin-left: 0px; +} + +.co-step-bar .co-step-element.active { + color: #53a3d9; +} + +.co-step-bar .co-step-element:first-child:before { + display: none; +} + +.co-step-bar .co-step-element:before { + content: ""; + position: absolute; + top: 12px; + width: 14px; + border-top: 2px solid #ddd; +} + +.co-step-bar .co-step-element.icon:before { + left: -20px; +} + +.co-step-bar .co-step-element.text:before { + left: -22px; +} + +.co-step-bar .co-step-element.active:before { + border-top: 2px solid #53a3d9; +} + + +.co-step-bar .co-step-element.text { + border-radius: 100%; + border: 2px solid #ddd; +} + +.co-step-bar .co-step-element.text.active { + border: 2px solid #53a3d9; +} + +@media screen and (min-width: 900px) { + .co-dialog .modal-dialog { + width: 800px; + } +} + +@media screen and (min-width: 1200px) { + .co-dialog.wider .modal-dialog { + width: 1000px; + } +} + +.co-alert .co-step-bar { + float: right; + margin-top: 6px; +} + +.cor-container { + padding-left: 15px; + padding-right: 15px; +} + +.cor-title-link { + font-weight: 300; + line-height: 30px; + margin-top: 22px; + margin-bottom: 10px; + font-size: 16px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: white; +} + +.cor-title-link a { + color: white; + text-decoration: none !important; +} + +.cor-title-link a.back-link .avatar { + margin-right: 6px; +} + +.cor-title-link a.back-link:before { + content: "\f060"; + color: white; + display: inline-block; + margin-right: 10px; + vertical-align: middle; + font-family: FontAwesome; +} + +.co-table { + width: 100%; +} + +.co-fixed-table { + table-layout: fixed; +} + +.co-fixed-table .co-flowing-col { + overflow: hidden; + text-overflow: ellipsis; + padding-left: 16px; + vertical-align: middle; +} + +.co-fixed-table .nowrap-col { + white-space: nowrap; + overflow: hidden; +} + +.co-table td { + border-bottom: 1px solid #eee; + padding: 10px; +} + +.co-table.no-lines td { + border-bottom: 0px; + padding: 6px; +} + +.co-table thead td { + color: #999; + font-size: 90%; + text-transform: uppercase; + font-weight: 300; + padding-top: 0px !important; +} + +.co-table thead td a { + color: #666; +} + +.co-table thead td:after { + content: "\f175"; + font-family: FontAwesome; + font-size: 12px; + margin-left: 10px; + visibility: hidden; +} + +.co-table thead td.unorderable-col:after { + display: none; +} + +.co-table thead td.current:after { + content: "\f175"; + visibility: visible; +} + +.co-table thead td.current.reversed:after { + content: "\f176"; + visibility: visible; +} + +.co-table thead td.current a { + color: #337ab7; +} + +.co-table .checkbox-col { + width: 24px; + text-align: center; +} + +.co-table .checkbox-col .co-checkable-menu a { + color: black; + text-transform: none; +} + +.co-table thead td.checkbox-menu-col:after { + display: none; +} + +.co-table .offset-check-col { + padding-left: 22px; +} + +.co-table td.options-col { + width: 36px; + text-align: center; +} + +.co-table td.caret-col { + width: 10px; + padding-left: 6px; + padding-right: 0px; + color: #aaa; + text-align: center; + max-width: 20px; +} + +.co-table td.caret-col i.fa { + cursor: pointer; +} + +.co-table td.caret-col i.fa.fa-caret-down { + color: black; +} + +.co-table .add-row-spacer td { + padding: 5px; +} + +.co-table .add-row td { + padding-top: 10px; + border-top: 2px solid #eee; + border-bottom: none; +} + +.co-table tr.co-table-header-row td { + font-size: 12px; + text-transform: uppercase; + color: #ccc; + border-bottom: none; + padding-left: 10px; + padding-top: 10px; + padding-bottom: 4px; +} + +.co-table tr.co-table-header-row td i.fa { + margin-right: 4px; +} + +.co-table tr.indented-row td:first-child { + padding-left: 28px; +} + +@media (max-width: 767px) { + .co-table tr.indented-row td:first-child { + padding-left: 10px; + } +} + +.co-table .mobile-row { + border-bottom: 2px solid #eee; + padding-bottom: 10px; + margin-bottom: 10px; + + position: relative; +} + +.co-table .mobile-row:last-child { + border-bottom: 0px solid #eee; + padding-bottom: 0px; + margin-bottom: 0px; +} + +.co-table .mobile-row .mobile-col-header { + font-weight: bold; + color: #444; +} + +.co-table .mobile-row .mobile-col-value { + padding: 6px; +} + +.co-table .mobile-row .options-col { + position: absolute; + top: -6px; + right: 0px; +} + + +.cor-checkable-menu { + display: inline-block; +} + +.co-checkable-menu .co-checkable-menu-state { + display: inline-block; + margin-left: -1px; + margin-right: 4px; +} + +.co-checkable-menu .dropdown { + display: inline-block; +} + +.co-checkable-item, .co-checkable-menu-state { + width: 18px; + height: 18px; + cursor: pointer; + border: 1px solid #ddd; + display: inline-block; + vertical-align: middle; + + position: relative +} + +.co-checkable-item:after, .co-checkable-menu-state:after { + content: "\f00c"; + font-family: FontAwesome; + color: #ccc; + + position: absolute; + top: -1px; + left: 1px; + + visibility: hidden; +} + +.co-checkable-menu-state.some:after { + content: "-"; + font-size: 24px; + top: -10px; + left: 4px; +} + +@media (min-width: 768px) { + .co-checkable-item:hover:after { + visibility: visible; + } +} + +.co-checkable-item.checked:after, .co-checkable-menu-state.all:after, .co-checkable-menu-state.some:after { + visibility: visible; + color: #428bca; +} + +.co-table .co-checkable-row.checked { + background: #F6FCFF; +} + +.co-filter-box { + position: relative;; +} + +.co-filter-box input { + display: inline-block; + width: auto !important; +} + +.co-filter-box .filter-message { + display: inline-block; + position: absolute; + left: -220px; + top: 7px; + color: #ccc; +} + +.co-filter-box .filter-options { + margin-top: 4px; + font-size: 14px; + text-align: right; + display: inline-block; +} + +.co-filter-box .filter-options label input { + margin-right: 4px; +} + + +.co-filter-box.with-options > input { + display: inline-block; + width: 200px; + margin-right: 4px; +} + +.co-check-bar { + margin-bottom: 10px; +} + +.co-check-bar .co-checked-actions { + display: inline-block; + border-left: 1px solid #eee; + margin-left: 10px; + padding-left: 4px; +} + +.co-top-bar { + height: 50px; + padding-bottom: 40px; +} + +.co-check-bar .co-checked-actions .btn { + margin-left: 6px; +} + +.co-check-bar .co-checked-actions .btn .fa { + margin-right: 4px; +} + +.co-check-bar .co-filter-box, .co-top-bar .co-filter-box { + float: right; +} + +.co-check-bar .co-filter-box .page-controls, .co-top-bar .co-filter-box .page-controls { + margin-right: 6px; + margin-bottom: 6px; +} + +.co-check-bar .co-filter-box input, .co-top-bar .co-filter-box input[type="text"] { + width: 300px; + display: inline-block; + vertical-align: middle; +} + +.co-check-bar .co-filter-box input, .co-top-bar .co-filter-box label { + margin-left: 6px; +} + +.co-top-bar .co-filter-box input { + vertical-align: top; +} + +@media screen and (max-width: 640px) { + .co-top-bar .page-controls { + margin-bottom: 10px; + text-align: right; + } + + .co-top-bar .co-filter-box { + display: block; + margin-bottom: 10px; + } + + .co-top-bar .filter-options { + display: block; + margin-bottom: 10px; + } + + .co-filter-box input { + display: block !important; + } +} + +.empty { + border-bottom: none !important; +} + +.empty-icon { + color: #aaa; + font-size: 60px; + margin-bottom: 0px; + text-align: center; +} + +.empty-primary-msg { + font-size: 18px; + margin-bottom: 10px; + text-align: center; +} + +.empty-secondary-msg { + font-size: 14px; + color: #999; + text-align: center; + margin-bottom: 10px; +} + +.co-alert { + padding: 16px; + padding-left: 46px; + position: relative; + margin-bottom: 20px; + position: relative; + border: 1px solid #eee; +} + +.co-alert.co-alert-success { + background: #F0FFF4; +} + +.co-alert.co-alert-success:before { + font-family: FontAwesome; + content: "\f058"; + position: absolute; + top: 11px; + left: 12px; + font-size: 22px; + color: #83D29C; +} + +.co-alert.co-alert-info { + background: #F0FAFF; +} + +.co-alert.co-alert-info:before { + font-family: FontAwesome; + content: "\f05a"; + position: absolute; + top: 11px; + left: 12px; + font-size: 22px; + color: #83B7D2; +} + +.co-alert.co-alert-warning { + background: #FFFBF0; +} + +.co-alert.co-alert-warning:before { + font-family: FontAwesome; + content: "\f071"; + position: absolute; + top: 11px; + left: 12px; + font-size: 22px; + color: #FCA657; +} + +.co-alert.co-alert-danger { + background: #FFF0F0; +} + +.co-alert.co-alert-danger:before { + font-family: core-icons; + content: "\f109"; + position: absolute; + top: 11px; + left: 12px; + font-size: 22px; + color: red; +} + +.co-alert.co-alert-danger:after { + font-family: FontAwesome; + content: "\f12a"; + position: absolute; + top: 16px; + left: 20px; + font-size: 16px; + color: white; + z-index: 2; +} + +.co-alert.thin { + padding: 6px; + padding-left: 38px; + margin-bottom: 0px; +} + +.co-alert.thin:before { + top: 5px; + font-size: 18px; +} + +.co-alert.thin:after { + top: 9px; + font-size: 13px; + left: 19px; +} + +.co-alert-inline:before { + position: relative !important; + top: auto !important; + left: auto !important; + vertical-align: middle; + margin-right: 10px; +} + +.co-alert-popin-warning { + margin-left: 10px; +} + +@media screen and (max-width: 767px) { + .co-alert-popin-warning { + display: block; + margin: 0px; + margin-top: 10px; + float: none; + } +} + +.co-alert-inline { + border: 0px; + display: inline-block; + background-color: transparent !important; + margin: 0px; + padding: 4px; +} + +.co-list-table tr td:first-child { + font-weight: bold; + padding-right: 10px; + vertical-align: top; + width: 120px; + padding-left: 0px; +} + +.co-list-table tr td { + padding: 10px; + font-size: 15px; +} + +.co-list-table .help-text { + margin-top: 6px; + font-size: 14px; + color: #aaa; +} + +.co-modify-link:after { + font-family: FontAwesome; + content: "\f054"; + color: #ccc; + vertical-align: middle; + display: inline-block; + margin-left: 10px; + font-size: 10px; + line-height: 16px; +} + +.co-option-table tr td:first-child { + padding-left: 16px; + padding-right: 16px; + padding-top: 0px; + vertical-align: top; +} + +.co-option-table tr td:last-child { + padding-bottom: 10px; +} + +.co-option-table .help-text { + margin-top: 4px; + margin-bottom: 10px; + font-size: 14px; + color: #aaa; +} + +.co-modal-body-scrollable { + overflow-y: auto; + overflow-x: hidden; + max-height: 400px; +} + +.cor-confirm-dialog-element .modal-body { + padding: 20px; +} + +.cor-confirm-dialog-element .progress-message { + margin-bottom: 10px; + font-size: 16px; +} + +.co-top-tab-bar { + padding: 0px; + margin: 0px; + padding-left: 10px; + + margin-bottom: 10px; + border-bottom: 1px solid #eee; +} + +.co-top-tab-bar li { + display: inline-block; + list-style: none; + text-align: center; + padding: 6px; + padding-left: 10px; + padding-right: 10px; + border-bottom: 1px solid #eee; + font-size: 15px; + cursor: pointer; + color: #666; + + bottom: -2px; + position: relative; +} + +.co-top-tab-bar li.active { + color: #51a3d9; + border-bottom: 2px solid #51a3d9; + top: 2px; +} + +.modal-header.ahead-of-tabs { + border-bottom: 0px; + padding-bottom: 4px; +} diff --git a/config-tool/pkg/lib/editor/static/css/quay.css b/config-tool/pkg/lib/editor/static/css/quay.css new file mode 100644 index 000000000..e2b3893a1 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/css/quay.css @@ -0,0 +1,4084 @@ +* { + font-family: 'Source Sans Pro', sans-serif; + margin: 0; +} + +.btn { + outline: none !important; +} + +@media (max-width: 410px) { + .olrk-normal { + display: none; + } +} + +.announcement a { + color: lightblue; +} + +.announcement { + position: absolute; + z-index: 3; + top: 0px; + left: 0px; + right: 0px; + + display: block; + background: rgba(123, 123, 123, 0.6); + min-height: 45px; + text-align: center; + font-size: 14px; + line-height: 45px; + color: white; +} + +.announcement.inline { + position: relative; + z-index: 2; +} + +.announcement.inline .message { + display: flex; + justify-content: center; + align-items: center; +} + +.announcement .spacer { + display: inline-block; + width: 45px; +} + +.announcement img { + height: 45px; + padding-top: 6px; + padding-bottom: 6px; +} + +.announcement .plus { + display: inline-block; + margin-left: 10px; + margin-right: 10px; +} + +.scrollable-menu { + max-height: 400px; + overflow: auto; +} + +.dropdown.input-group-addon { + padding: 0px; + border: 0px; + background-color: transparent; + text-align: left; +} + +.dropdown.input-group-addon .dropdown-toggle { + border-left: 0px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; +} + +#padding-container { + padding: 20px; + padding-top: 20px; + padding-bottom: 6px; +} + +.d3-tip { + position: absolute; + left: -100000px; +} + +#co-l-footer-wrapper { + clear: both; + min-height: 100%; + height: auto !important; + height: 100%; + margin-bottom: -64px; +} + +#co-l-footer-wrapper #co-l-footer-push { + height: 64px; +} + +#co-l-footer img { + height: 46px; + margin-top: 0px; + padding: 6px; +} + +#co-l-footer .col-md-4 { + text-align: right; +} + +#co-l-footer { + clear: both; + position: relative; + background-color: white; + height: 64px; + min-height: 64px; + overflow: hidden; + margin: 0; +} + +.main-panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + padding: 30px; + + -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -ms-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + -o-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); +} + +.container { + max-width: none !important; +} + +.notification-view-element { + cursor: pointer; + margin-bottom: 10px; + border-bottom: 1px solid #eee; + padding-bottom: 10px; + position: relative; + max-width: 320px; +} + +.notification-view-element .right-controls button { + margin-left: 10px; +} + +.notification-view-element .message i.fa { + margin-right: 6px; +} + + +.notification-view-element .orginfo { + margin-top: 8px; + float: left; +} + +.notification-view-element .orginfo .orgname { + font-size: 12px; + color: #aaa; +} + +.notification-view-element .circle { + position: absolute; + top: 15px; + left: 0px; + + width: 12px; + height: 12px; + display: inline-block; + border-radius: 50%; +} + +.notification-view-element .datetime { + margin-top: 16px; + font-size: 12px; + color: #aaa; + text-align: right; +} + +.notification-view-element .message { + margin-bottom: 4px; +} + +.notification-view-element .notification-content { + padding: 10px; + border-radius: 6px; + margin-left: 16px; +} + +.notification-view-element .notification-content:hover { + background: rgba(66, 139, 202, 0.1); +} + +.notification-view-element .right-controls { + text-align: right; + font-size: 12px; +} + +.dockerfile-path { + margin-top: 10px; + padding: 20px; + padding-bottom: 0px; + font-family: Consolas, "Lucida Console", Monaco, monospace; + font-size: 14px; +} + +.dockerfile-path:before { + content: "\f15b"; + font-family: FontAwesome; + margin-right: 8px; + font-size: 18px; +} + +.codetooltipcontainer .tooltip-inner { + white-space:pre; + max-width:none; +} + +.codetooltip { + font-family: Consolas, "Lucida Console", Monaco, monospace; + display: block; +} + +.resource-view-element { + position: relative; +} + +.resource-view-element .resource-spinner { + z-index: 1; + position: absolute; + top: 10px; + left: 10px; + opacity: 0; + transition: opacity 0s ease-in-out; + text-align: center; +} + +.resource-view-element .resource-content { + visibility: hidden; +} + +.resource-view.element .resource-content.hidden .tab-content > .active { + visibility: hidden; +} + +.resource-view-element .resource-content.visible { + z-index: 2; + visibility: visible; +} + +.resource-view-element .resource-error { + margin: 10px; + font-size: 16px; + color: #444; + text-align: center; +} + +.resource-view-element .resource-spinner.visible { + opacity: 1; + transition: opacity 1s ease-in-out; +} + +.super-option { + border: 1px solid #ddd; + border-radius: 10px; + background: #eee; + margin-bottom: 26px; +} + +.small-spinner { + display: inline-block; + width: 14px; + height: 14px; + border: solid 2px transparent; + border-top-color: #444; + border-left-color: #444; + border-radius: 10px; + -webkit-animation: loading-bar-spinner 400ms linear infinite; + -moz-animation: loading-bar-spinner 400ms linear infinite; + -ms-animation: loading-bar-spinner 400ms linear infinite; + -o-animation: loading-bar-spinner 400ms linear infinite; + animation: loading-bar-spinner 400ms linear infinite; +} + +#loading-bar-spinner { + top: 70px; +} + +.dropdown-menu i.fa { + margin-right: 6px; + position: relative; +} + +.dropdown-menu .new-action i.fa { + atext-shadow: 2px 2px 7px #5cb85c, 2px 2px 7px #5cb85c; +} + +.dropdown-menu .new-action i.fa:after { + content: "+"; + color: #5cb85c; + position: absolute; + left: -14px; + top: -2px; + font-weight: bold; + font-size: 115%; +} + +#input-box { + padding: 4px; + font-size: 14px; +} + +html, body { + height: 100%; +} + +.tooltip { + word-break: normal !important; + word-wrap: normal !important; + pointer-events: none; +} + +.toggle-icon { + font-size: 22px; + padding: 6px; + cursor: pointer; + color: black; +} + +i.toggle-icon:hover { + color: steelblue; +} + +.toggle-icon.active { + border-radius: 4px; + background: #eee; +} + +.docker-auth-dialog .token-dialog-body .well { + margin-bottom: 0px; + position: relative; + padding-right: 24px; +} + +.docker-auth-dialog .token-dialog-body .well i.fa-refresh { + position: absolute; + top: 9px; + right: 9px; + font-size: 20px; + color: gray; + transition: all 0.5s ease-in-out; + cursor: pointer; +} + +.docker-auth-dialog .token-dialog-body .well i.fa-refresh:hover { + color: black; +} + +.docker-auth-dialog .token-view { + background: transparent; + display: block; + border: 0px transparent; + font-size: 12px; + width: 100%; +} + +.docker-auth-dialog .download-cfg.not-supported { + font-size: 14px; + color: #ccc; +} + +.docker-auth-dialog .download-cfg { + float: left; + padding-top: 6px; + font-size: 16px; +} + +.docker-auth-dialog .download-cfg .fa-download { + margin-right: 10px; + font-size: 25px; + vertical-align: middle; +} + +.content-container { + padding-bottom: 70px; +} + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -176px; +} + +.button-hidden { + visibility: hidden; +} + +.organization-header-element { + padding: 20px; + margin-bottom: 20px; + border-bottom: 1px solid #eee; + + font-size: 20px; +} + +.organization-header-element .organization-name { + display: inline-block; + margin-left: 10px; +} + +.organization-header-element .divider { + color: #aaa; + margin-left: 10px; + margin-right: 10px; +} + +.organization-header-element .organization-name { + display: inline-block; + font-size: 20px; + margin-left: 10px; +} + +.organization-header-element .team-name { + text-transform: none; +} + +.organization-header-element .header-buttons { + float: right; +} + +.notification-primary { + background: #428bca; + color: white; +} + +.notification-info { + color: black; + background: #d9edf7; +} + +.notification-warning { + color: #8a6d3b; + background: #ffde2f; +} + +.notification-error { + background: red; + color: white; +} + +.user-notification.notification-animated { + min-width: 21px; + + transform: scale(0); + -moz-transform: scale(0); + -webkit-transform: scale(0); + + animation: scaleup 500ms 1; + animation-timing-function: ease-in-out; + animation-fill-mode: forwards; + + -moz-animation: scaleup 500ms 1; + -moz-animation-timing-function: ease-in-out; + -moz-animation-fill-mode: forwards; + + -webkit-animation: scaleup 500ms 1; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-fill-mode: forwards; +} + +@-moz-keyframes scaleup { + 0% { -moz-transform: scale(0); } + 100% { -moz-transform: scale(1); } +} + +@-webkit-keyframes scaleup { + 0% { -webkit-transform: scale(0); } + 100% { -webkit-transform: scale(1); } +} + +@keyframes scaleup { + 0% { transform: scale(0); } + 100% { transform: scale(1); } +} + +.status-box a { + padding: 6px; + color: black; +} + +.status-box a b { + margin-right: 10px; +} + +.build-info { + margin: 4px; + padding: 4px; + margin-left: 6px; + margin-right: 6px; + border-bottom: 1px solid #eee; +} + +.build-info.clickable:hover { + background: rgba(66, 139, 202, 0.2); + cursor: pointer; + border-radius: 4px; +} + + +.build-info:last-child { + border-bottom: 0px; +} + +.repo-circle { + position: relative; + background: #eee; + padding: 4px; + border-radius: 50%; + display: inline-block; + width: 46px; + height: 46px; + text-align: center; +} + +.repo-circle.no-background { + background: transparent; + position: relative; + height: 40px; + width: 40px; +} + +.repo-circle .fa-hdd-o { + font-size: 36px; +} + +.repo-circle.no-background .fa-hdd-o { + font-size: 30px; +} + +.repo-circle .fa-lock { + position: absolute; + bottom: -2px; + right: -4px; + background: rgb(253, 191, 191); + width: 20px; + display: inline-block; + border-radius: 50%; + text-align: center; + height: 20px; + line-height: 21px; + font-size: 16px !important; +} + +.repo-circle.no-background .fa-lock { + bottom: -2px; + right: -6px; + color: #444; +} + + +.description-overview { + padding: 4px; + font-size: 16px; +} + +.description-list { + margin: 10px; + padding: 0px; +} + +.description-list li:before { + content: "\00BB"; + margin-right: 6px; + font-size: 18px; +} + +.description-list li { + list-style-type: none; + margin: 0px; + padding: 6px; +} + +.accordion-toggle { + cursor: pointer; + text-decoration: none !important; +} + +.user-guide h3 { + margin-bottom: 20px; +} + +.user-guide h3 .label { + float: right; +} + +.plans .callout { + font-size: 2em; + text-align: center; + margin-top: 10px; + margin-bottom: 30px; +} + +.plans .all-plans { + font-size: 16px; + text-align: center; + margin-bottom: 25px; +} + +.plans .all-plans .feature { + color: #428bca; +} + +.plans .all-plans .business-feature { + color: #46ac39; +} + +.plans-list { + text-align: center; + margin-bottom: 25px; +} + +.plans-list .plan-container { + padding: 5px; +} + +.plans-list .plan { + vertical-align: top; + font-size: 1.4em; +} + +.plans-list .plan.bus-small, .plans-list .plan.bus-small-30 { + border-top: 6px solid #46ac39; + margin-top: -10px; +} + +.plans-list .plan.bus-small .plan-box, .plans-list .plan.bus-small-30 .plan-box { + background: black !important; +} + +.plans-list .plan:last-child { + margin-right: 0px; +} + +.plans-list .plan .plan-box { + background: #444; + padding: 10px; + color: white; +} + +.plans-list .plan .plan-title { + text-transform: uppercase; + padding-top: 25px; + padding-bottom: 20px; + margin-bottom: 10px; + font-weight: bold; + border-bottom: 1px solid #eee; +} + +.visible-sm-inline { + display: none !important; +} + +.visible-md-inline { + display: none !important; +} + +.hidden-sm-inline { + display: inline !important; +} + +.hidden-xs-inline { + display: inline !important; +} + +@media (min-width: 991px) { + .visible-md-inline { + display: inline !important; + } +} + +@media (max-width: 991px) and (min-width: 768px) { + .visible-sm-inline { + display: inline !important; + } + + .hidden-sm-inline { + display: none !important; + } +} + +@media (max-width: 700px) { + .hidden-xs-inline { + display: none !important; + } +} + + +.visible-xl { + display: none !important; +} + +.visible-xl-inline { + display: none !important; +} + +@media (min-width: 1200px) { + .visible-xl { + display: block !important; + } + + .visible-xl-inline { + display: inline-block !important; + } +} + +.plans-list .plan-box .description { + color: white; + margin-top: 6px; + font-size: 12px !important; +} + +.plans-list .plan button { + margin-top: 6px; + margin-bottom: 6px; +} + +.plans-list .plan.bus-small button, .plans-list .plan.bus-small-30 button { + font-size: 1em; +} + +.plans-list .features-bar { + padding-top: 248px; +} + +.plans-list .features-bar .feature .count-panel { + padding: 10px; +} + +.plans-list .features-bar .feature { + height: 43px; + text-align: right; + white-space: nowrap; +} + +.context-tooltip { + border-bottom: 1px dotted black; + cursor: default; +} + +.plans-list .features-bar .feature i { + margin-left: 16px; + float: right; + width: 16px; + font-size: 16px; + text-align: center; + margin-top: 2px; +} + +.plans-list .plan .features { + padding: 6px; + background: #eee; + padding-bottom: 0px; +} + +.plans-list .plan .feature, +.plans-list .plan .count-feature { + text-align: center; + padding: 10px; + border-bottom: 1px solid #ddd; + font-size: 14px; +} + +.plans-list .plan .feature:after { + content: ""; + border-radius: 50%; + display: inline-block; + width: 16px; + height: 16px; +} + +.plans-list .plan .visible-xs .feature { + text-align: left; +} + +.plans-list .plan .visible-xs .feature:after { + float: left; + margin-right: 10px; +} + +.plans-list .plan .feature.notpresent { + color: #ccc; +} + +.plans-list .plan .feature.present:after { + background: #428bca; +} + +.plans-list .plan.business-plan .feature.present:after { + background: #46ac39; +} + +.plans-list .plan .count-panel, .plans-list .features-bar .count-panel { + background: white; + border-bottom: 0px; + text-align: center !important; + font-size: 14px; + padding: 10px; +} + +.plans-list .plan .count-panel b, .plans-list .features-bar .count-panel b { + display: block; +} + +.plans-list .plan .feature:last-child { + border-bottom: 0px; +} + +.plans-list .plan-price { + margin-bottom: 10px; +} + +.plan-price { + display: block; + font-weight: bold; + font-size: 1.8em; + position: relative; +} + + +@media (min-width: 768px) { + .plan-price:after { + content: "/ mo"; + position: absolute; + bottom: 0px; + right: 20px; + font-size: 12px; + color: #aaa; + } +} + +.plans-list .plan .count-panel { + margin-bottom: 10px; +} + +.plans-list .plan .count b { + font-size: 1.5em; + vertical-align: text-bottom; + color: #428bca; +} + +.plans-list .plan .count-feature { + line-height: 1.5em; +} + +.plans-list .plan.business-plan .count b { + color: #46ac39; +} + +.plans-list .plan .description { + font-size: 1em; + font-size: 16px; + height: 34px; + +} + +.plans-list .plan .smaller { + font-size: 12px; + margin-bottom: 30px; +} + +.plans-list .plan .features { + font-size: 16px; + text-align: left; + margin-bottom: 10px; +} + +.plans-list .plan .features i { + margin-right: 5px; +} + +.plans .plan-faq dd { + margin-bottom: 20px; +} + +.enterprise-plan .plan-combine { + text-align: center; + padding: 10px; + margin-bottom: 20px; +} + +.enterprise-plan .plan-combine .plus { + font-size: 22px; + display: inline-block; + margin-left: 10px; + margin-right: 10px; +} + +.enterprise-plan a { + margin-top: 20px; +} + +.enterprise-plan .plan-combine img { + height: 45px; +} + +.enterprise-tour .tour-section { + margin-bottom: 50px !important; + padding-bottom: 50px !important; + border-bottom: 1px solid #eee !important; +} + +.enterprise-tour .tour-section p { + margin: 0 !important; +} + +.enterprise-tour .btn { + font-size: 20px; +} + +.feature-illustration { + text-align: center; +} + +.feature-illustration svg { + margin: 0 auto; + width: auto !important; + max-width: 100%; + max-height: 110px; +} + +.loading { + padding: 20px; +} + +.follow-button { + text-align: center; +} + +form input.ng-invalid.ng-dirty, +*[ng-form] input.ng-invalid.ng-dirty { + background-color: #FDD7D9 !important; +} + +form input.ng-valid.ng-dirty, +*[ng-form] input.ng-valid.ng-dirty { + background-color: #DDFFEE; +} + +.product-tour .tour-header { + text-align: center; + margin-bottom: 40px; + border-bottom: 4px solid #333; +} + +.product-tour .tour-header .tour-shoutout-header { + margin: 10px; + font-size: 4em; +} + +.product-tour .tour-header .tour-shoutout { + font-size: 2em; + color: #444; +} + +.product-tour .tour-section { + margin-bottom: 60px; +} + +.product-tour .tour-section .tour-section-title { + font-size: 1.8em; + margin-top: 10px; + margin-bottom: 10px; +} + +.product-tour .tour-section .tour-section-description { + font-size: 1.2em; + margin-bottom: 20px; +} + +.product-tour .testimonial { + margin-bottom: 20px; + margin-top: 10px; + padding-top: 20px; + border-top: 4px solid #ccc; +} + + +.signin-buttons { + text-align: center; +} + +.footer-container, .push { + height: 100px; +} + +.footer-container.fixed { + position: fixed; + bottom: 16px; + left: 0px; + right: 0px; +} + +.page-footer-padder { + margin-top: 76px; + background-color: white; + background-image: none; + + overflow: hidden; + width: 100%; + height: 80px; + padding-top: 24px; +} + +.page-footer .row { + max-width: 100%; +} + +.page-footer .logo-container { + font-size: 11px; + color: black; + text-align: right; + padding-right: 30px; +} + +.page-footer .logo-container a { + display: block; + margin-top: 4px; + margin-right: 10px; +} + +.page-footer .dt-logo { + vertical-align: center; + max-width: 150px; +} + +.page-footer .copyright { + font-size: 12px; + color: black; +} + +.page-footer ul { + list-style-type: none; + margin: 0px; + padding-left: 0px; + margin-top: 10px; +} + +.page-footer li { + margin: 0px; + display: inline-block; + padding: 10px; +} + +.user-welcome { + text-align: center; +} + +.repo-search-box { + width: 240px; +} + +.repo-mini-listing { + margin: 2px; +} + +.repo-mini-listing i { + color: #aaa; + margin-right: 4px; +} + +.repo-mini-listing .description { + margin-left: 20px; + color: #aaa; + font-size: 85%; + padding-top: 4px; + border-top: 1px solid #eee; + margin-top: 4px; + display: block; + overflow: hidden; + text-overflow: ellipsis; +} + +.editable { + position: relative; +} + +.editable i { + position: absolute; + top: 0px; + right: -40px; + + opacity: 0.2; + font-size: 85%; + display: inline-block; + + -moz-transition: opacity 500ms ease-in-out; + -webkit-transition: opacity 500ms ease-in-out; + -o-transition: opacity 500ms ease-in-out; + -ms-transition: opacity 500ms ease-in-out; + transition: opacity 500ms ease-in-out; +} + +.noteditable i { + display: none; +} + +p.editable { + display: inline-block; +} + +p.editable .empty { + display: inline-block; + color: #aaa; +} + +p.editable:hover { + cursor: pointer; +} + +p.editable:hover i { + opacity: 1; +} + +.tag-dropdown { + display: inline-block; + font-size: 1.15em; +} + +#tagContextMenu { + display: none; + position: absolute; + z-index: 100000; + outline: none; +} + +#tagContextMenu ul { + display: block; + position: static; + margin-bottom: 5px; +} + +.tag-controls { + display: inline-block; + margin-right: 20px; + margin-top: 2px; + opacity: 0; + float: right; + -webkit-transition: opacity 200ms ease-in-out; + -moz-transition: opacity 200ms ease-in-out; + transition: opacity 200ms ease-in-out; +} + +.right-title { + display: inline-block; + float: right; + font-size: 12px; + color: #aaa; +} + +.right-tag-controls { + cursor: default; + display: inline-block; + float: right; + padding: 4px; + padding-left: 10px; + border-left: 1px solid #ccc; + vertical-align: middle; + margin-top: -2px; + margin-right: -10px; + color: #666; +} + +.right-tag-controls .tag-count { + display: inline-block; + margin-left: 4px; + margin-right: 6px; +} + +.tag-image-sizes .tag-image-size { + height: 22px; + position: relative; +} + +.tag-image-sizes .tag-image-size .size-title { + position: absolute; + right: 0px; + top: 0px; + font-size: 12px; +} + +.tag-image-sizes .tag-image-size .size-limiter { + display: inline-block; + padding-right: 90px; + width: 100%; +} + +.tag-image-sizes .tag-image-size .size-bar { + display: inline-block; + background: steelblue; + height: 12px; +} + +#current-tag .control-bar { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #eee; + text-align: right; +} + +.tag-dropdown a { + color: black; +} + +.modal-body textarea { + width: 100%; + height: 150px; +} + +.tag-specific-images-view .image-listings { + margin: 10px; +} + +.tag-specific-images-view .image-listings .image-listing { + margin: 4px; + padding: 2px; + position: relative; +} + +.tag-specific-images-view .image-listings .image-listing .image-listing-id { + display: inline-block; + margin-left: 20px; +} + +.tag-specific-images-view .image-listings .image-listing .image-listing-line { + border-left: 2px solid steelblue; + display: inline-block; + position: absolute; + top: -2px; + bottom: 8px; + left: 6px; + width: 1px; + z-index: 1; +} + +.tag-specific-images-view .image-listings .image-listing.tag-image .image-listing-line { + top: 8px; +} + +.tag-specific-images-view .image-listings .image-listing.child .image-listing-line { + bottom: -2px; +} + +.tag-specific-images-view .image-listings .image-listing .image-listing-circle { + position: absolute; + top: 8px; + + border-radius: 50%; + border: 2px solid steelblue; + width: 10px; + height: 10px; + display: inline-block; + background: white; + z-index: 2; +} + +.tag-specific-images-view .image-listings .image-listing.tag-image .image-listing-circle { + background: steelblue; +} + +.tag-specific-images-view .more-changes { + margin-left: 16px; +} + +.repo.container-fluid { + padding-left: 10px; + padding-right: 10px; +} + +@media (min-width: 768px) { + .repo.container-fluid { + padding-left: 20px; + padding-right: 20px; + } +} + +@media (min-width: 1200px) { + .repo.container-fluid { + padding-left: 40px; + padding-right: 40px; + } + + .repo.container-fluid .col-md-4 { + width: 30%; + } + + .repo.container-fluid .col-md-8 { + width: 70%; + } +} + + +.repo .current-context { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; + vertical-align: middle; +} + +.repo .current-context-icon { + vertical-align: middle; + margin-right: 4px; +} + +.repo .header { + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #eee; + + position: relative; +} + +.repo .header .back { + margin-right: 6px; +} + +.repo .header .repo-circle { + line-height: 38px; + margin-right: 10px; +} + +.repo .description { + margin-top: 10px; + margin-bottom: 20px; +} + +.repo .empty-message { + padding: 6px; + font-size: 1.8em; + color: #666; + margin-bottom: 10px; +} + +.repo .empty-description { + padding: 6px; +} + +.repo .empty-description pre:last-child { + margin-bottom: 0px; +} + +.repo .empty-description .panel-default { + margin-top: 20px; +} + +.repo dl.dl-horizontal dt { + width: 80px; + padding-right: 10px; +} + +.repo dl.dl-horizontal dd { + margin-left: 80px; +} + +.repo dl.dl-normal dd { + padding-left: 14px; + margin-bottom: 10px; + overflow: hidden; + text-overflow: ellipsis; +} + +.repo .header h3 { + display: inline-block; +} + +.repo .repo-controls .dropdown { + margin-right: 10px; +} + +.repo .repo-controls .count-panel { + display: inline-block; + padding-left: 4px; + padding-right: 4px; + font-weight: bold; + color: #428bca; + + transform: scaleX(0); + -webkit-transform: scaleX(0); + -moz-transform: scaleX(0); + + transition: transform 500ms ease-in-out; + -webkit-transition: -webkit-transform 500ms ease-in-out; + -moz-transition: -moz-transform 500ms ease-in-out; +} + +.repo .repo-controls .count-panel.visible { + transform: scaleX(1); + -webkit-transform: scaleX(1); + -moz-transform: scaleX(1); +} + +.repo .repo-controls { + float: right; + display: inline-block; + font-size: 0.8em; + position: relative; + margin-top: 30px; +} + +.repo .pull-container { + display: inline-block; + width: 460px; + margin-left: 10px; + margin-right: 10px; + vertical-align: middle; + position: relative; +} + +.repo .pull-container .pull-selector { + display: inline-block; + width: 114px; + font-size: 14px; + height: 36px; + vertical-align: top; + border: 1px solid #ddd; + margin-right: -3px; + background: #f8f8f8; + outline: none; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.repo .pull-container .pull-selector i { + display: inline-block; + margin-right: 6px; +} + + +.repo .pull-container .copy-box { + width: 340px; + display: inline-block; +} + +.repo .pull-container .copy-box .copy-container { + border-top-left-radius: 0px !important; + border-bottom-left-radius: 0px !important; + border-left: 0px; +} + +.repo .pull-container .dropdown-menu li i.fa { + text-align: center; + width: 12px; + display: inline-block; +} + +.repo .pull-container sup { + margin-left: 4px; + color: red; +} + +.repo-image-view #clipboardCopied { + position: relative; + top: -14px; +} + +.repo-image-view .changes-container .change-side-controls { + float: right; + clear: both; + margin-top: 10px; + margin-bottom: 10px; +} + +.repo-image-view .changes-container .filter-input { + display: inline-block; + width: 400px; +} + +.repo-image-view .changes-container .result-count { + display: inline-block; + margin-right: 10px; + font-size: 14px; + color: #888; +} + +.repo-image-view .changes-container .changes-list { + padding: 10px; + margin-top: 28px; +} + +.copy-box-element { + position: relative; +} + +.copy-box-element .copy-container { + border-radius: 4px; + border: 1px solid #ddd; + position: relative; +} + +.copy-box-element input { + border: 0px; + padding-right: 32px; + cursor: pointer !important; +} + +.copy-box-element .copy-container .copy-icon { + position: absolute; + top: 8px; + right: 10px; + display: inline-block; + color: #ddd; + font-size: 16px; + cursor: pointer; + transition: color 0.5s ease-in-out; +} + +.copy-box-element .copy-container .copy-icon:hover { + color: #444; +} + +.copy-box-element.disabled input { + margin-right: 0px; +} + +.copy-box-element.disabled .copy-icon { + display: none; +} + +.global-zeroclipboard-container embed { + cursor: pointer; +} + +.copy-box-element input { + background-color: white !important; +} + +.clipboard-copied-message { + font-size: 0.8em; + display: inline-block; + + background: black; + color: white; + padding: 6px; + border-radius: 4px; + + position: absolute; + right: 6px; + top: 4px; + pointer-events: none; + z-index: 100; + + white-space: nowrap; + height: 27px; + overflow: hidden; +} + +.clipboard-copied-message { + -webkit-animation: fadeOutSlide 2s ease-in-out 0s 1 forwards; + -moz-animation: fadeOutSlide 2s ease-in-out 0s 1 forwards; + -ms-animation: fadeOutSlide 2s ease-in-out 0s 1 forwards; + -o-animation: fadeOutSlide 2s ease-in-out 0s 1 forwards; + animation: fadeOutSlide 2s ease-in-out 0s 1 forward; + animation-delay: 1s; +} + +@-webkit-keyframes fadeOutSlide { from { opacity: 1; width: 105px; } to { opacity: 0; width: 0px; } } +@-moz-keyframes fadeOutSlide { from { opacity: 1; width: 105px; } to { opacity: 0; width: 0px; } } +@-ms-keyframes fadeOutSlide { from { opacity: 1; width: 105px; } to { opacity: 0; width: 0px; } } +@-o-keyframes fadeOutSlide { from { opacity: 1; width: 105px; } to { opacity: 0; width: 0px; } } +@keyframes fadeOutSlide { from { opacity: 1; width: 105px; } to { opacity: 0; width: 0px; } } + +.repo .settings-cog { + margin-left: 20px; +} + +.repo .settings-cog a { + text-decoration: none !important; +} + +.repo .image-comment { + margin-bottom: 4px; +} + +.repo .image-section { + margin-top: 12px; + padding-bottom: 2px; + position: relative; +} + +.repo .image-section .tag { + margin: 2px; + border-radius: 8px; + display: inline-block; + padding: 4px; +} + + +.repo .image-section .section-icon { + float: left; + font-size: 16px; + margin-left: -4px; + margin-right: 14px; + color: #bbb; + width: 18px; + text-align: center; + padding-top: 6px; +} + +.repo .image-section .section-info { + padding: 4px; + padding-left: 6px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.05); + box-shadow: inset 0 1px 1px rgba(0,0,0,0.05); + background-color: #f5f5f5; + + vertical-align: middle; + display: block; + overflow: hidden; + text-overflow: ellipsis; + border-radius: 6px; +} + +.repo .image-section .section-info-with-dropdown { + padding-right: 30px; +} + +.repo .image-section .dropdown { + display: inline-block; + position: absolute; + top: 0px; + bottom: 2px; + right: 0px; +} + +.repo .image-section .dropdown-button { + position: absolute; + right: 0px; + top: 0px; + bottom: 0px; + + background: white; + padding: 4px; + padding-left: 8px; + padding-right: 8px; + border: 1px solid #eee; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + cursor: pointer; +} + +.repo-list { + margin-bottom: 40px; +} + +.repo-list .button-bar-right { + float: right; +} + +.button-bar-bottom { + margin-bottom: 60px; +} + + +.repo-list .section-header { + padding: 10px; + border-bottom: 1px solid #eee; + margin-bottom: 10px; +} + +.repo-list .button-bar-right button { + margin-right: 10px; +} + +.repo-listing { + display: block; + margin-bottom: 20px; + border-bottom: 1px solid #eee; + padding: 10px; + font-size: 14px; + line-height: normal; +} + +.repo-listing:last-child { + border-bottom: 0px; +} + +.landing .repo-listing { + border-bottom: 0px; + margin-bottom: 0px; +} + +.repo-listing a { + font-size: 1.5em; +} + +.repo-listing i { + color: #999; + display: inline-block; + margin-right: 6px; +} + +.repo-listing .description { + padding-left: 44px; +} + + +.repo-build .build-id:before { + content: "Build ID: " +} + +.repo-build .build-id { + float: right; + font-size: 12px; + color: #aaa; + padding: 10px; +} + +.repo-build .build-pane .timing { + float: right; +} + +.repo-build .build-tab-link { + white-space: nowrap; +} + +.repo-build .build-pane .build-header { + padding-top: 10px; + border-bottom: 1px solid #eee; + padding-bottom: 10px; +} + +.repo-build .build-pane .build-progress { + margin-top: 16px; + margin-bottom: 10px; +} + +.repo-build .build-pane .build-progress .progress { + height: 14px; + margin-bottom: 0px; +} + +.repo-build .build-pane .quay-spinner { + margin-top: 4px; + display: inline-block; +} + +.repo-build .build-pane .build-logs { + background: #222; + color: white; + padding: 10px; + overflow: auto; +} + +.repo-build .build-pane .build-logs .container-header { + padding: 2px; +} + +.repo-build .build-pane .build-logs .container-logs { + margin: 4px; + padding-bottom: 4px; +} + +.repo-build .build-pane .build-logs .command-title, +.repo-build .build-pane .build-logs .log-entry .message, +.repo-build .build-pane .build-logs .log-entry .message span { + font-family: Consolas, "Lucida Console", Monaco, monospace; + font-size: 13px; +} + +.repo-build .build-pane .build-logs .container-header { + cursor: pointer; + position: relative; +} + +.repo-build .build-pane .build-logs .container-header i.fa.chevron { + color: #666; + margin-right: 4px; + width: 14px; + text-align: center; + + position: absolute; + top: 6px; + left: 0px; +} + +.repo-build .build-pane .build-logs .log-container.command { + margin-left: 22px; +} + +.repo-build .build-pane .build-logs .container-header.building { + margin-bottom: 10px; +} + +.repo-build .build-pane .build-logs .container-header.pushing { + margin-top: 10px; +} + +.repo-build .build-log-error-element .error-message-container { + position: relative; + display: inline-block; + margin: 10px; + padding: 10px; + background: rgba(255, 0, 0, 0.17); + border-radius: 10px; + margin-left: 22px; +} + +.repo-build .build-log-error-element .error-message-container i.fa { + color: red; + position: absolute; + top: 13px; + left: 11px; +} + +.repo-build .build-log-error-element .error-message { + display: inline-block; + margin-left: 25px; +} + +.repo-build .build-pane .build-logs .container-header .label { + padding-top: 4px; + text-align: right; + margin-right: 4px; + width: 86px; + display: inline-block; + + border-right: 4px solid #aaa; + background-color: #444; + + position: absolute; + top: 4px; + left: 24px; +} + +.repo-build .build-pane .build-logs .dockerfile-command { + position: inherit; +} + +.repo-build .build-pane .build-logs .dockerfile-command .command-title { + padding-left: 0px; +} + +.repo-build .build-pane .build-logs .container-header .container-content { + display: block; + padding-left: 20px; +} + +.repo-build .build-pane .build-logs .container-header .container-content.build-log-command { + padding-left: 120px; +} + +.repo-build .build-pane .build-logs .log-entry { + position: relative; +} + +.repo-build .build-pane .build-logs .log-entry .message { + display: inline-block; + margin-left: 46px; +} + +.repo-build .build-pane .build-logs .log-entry .id { + color: #aaa; + padding-right: 6px; + margin-right: 6px; + text-align: right; + font-size: 12px; + width: 40px; + + position: absolute; + top: 4px; + left: 4px; +} + +.repo-admin .right-controls { + text-align: right; + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #eee; +} + +.repo-admin .right-info { + font-size: 11px; + margin-top: 10px; + text-align: right; +} + +.repo-admin .panel { + display: inline-block; + width: 720px; +} + +.repo-admin .prefix { + color: #aaa; +} + +.repo-admin .admin-search { + padding-top: 20px; +} + +.repo-admin .entity { + font-size: 1.2em; + min-width: 300px; +} + +.repo-admin .entity .popover { + font-size: 14px; +} + +.repo-admin .entity i.fa-exclamation-triangle { + color: #c09853; + float: right; + margin-right: 10px; + margin-top: 4px; +} + +.repo-admin .token a { + cursor: pointer; +} + +.repo .section { + display: block; + margin-bottom: 20px; + padding-bottom: 20px; + border-bottom: 1px solid #eee; +} + +.repo .description p:last-child { + margin: 0px; +} + +.repo thead td { + padding: 4px; + color: #999; +} + +.repo td { + padding: 6px; +} + +.repo .images { + margin: 10px; +} + +.repo .images .image-id { + font-size: 85%; + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; +} + +.repo .images p { + margin: 0px; +} + +.repo .formatted-command { + margin-top: 4px; + padding: 4px; + font-size: 12px; + font-family: Consolas, "Lucida Console", Monaco, monospace; +} + +.repo .formatted-command.trimmed { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.repo .change-count { + font-size: 16px; + display: inline-block; + margin-right: 10px; +} + +.repo .change-count b { + font-weight: normal; + margin-left: 6px; + vertical-align: middle; +} + +.repo .changes-container .well { + border: 0px; +} + +.repo .changes-container i.fa-plus-square { + color: rgb(73, 209, 73); +} + +.repo .changes-container i.fa-minus-square { + color: rgb(209, 73, 73); +} + +.repo .changes-container i.fa-pencil-square { + color: rgb(73, 100, 209); +} + +.repo .change-count:last-child { + margin-right: 0px; +} + +.repo .change-count i { + font-size: 16px; + vertical-align: middle; +} + +.repo .change i { + float: right; + vertical-align: middle; + margin-left: 4px; +} + +.repo .more-changes { + padding: 6px; + text-align: right; +} + +.repo #collapseChanges .well { + margin-top: 10px; + margin-bottom: 0px; +} + +.repo #collapseChanges .change { + overflow: hidden; + font-size: 14px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.navbar-nav > li > .user-dropdown { + padding-top: 9px; + padding-bottom: 9px; +} + +.navbar-brand { + padding: 6px; + line-height: 1px; + font-size: 1px; +} + +.user-dropdown > img { + padding-right: 6px; +} + +.delete-ui-element { + outline: none; +} + +.delete-ui-element i { + cursor: pointer; + vertical-align: middle; +} + +.delete-ui-element .delete-ui-button { + display: inline-block; + vertical-align: middle; + color: white; + width: 0px; + overflow: hidden; + + cursor: pointer; + + -moz-transition: width 500ms ease-in-out; + -webkit-transition: width 500ms ease-in-out; + -o-transition: width 500ms ease-in-out; + -ms-transition: width 500ms ease-in-out; + transition: width 500ms ease-in-out; +} + +.delete-ui-element .delete-ui-button button { + padding: 4px; +} + +.delete-ui-element:focus i { + visibility: hidden; +} + +.delete-ui-element:focus .delete-ui-button { + width: 60px; +} + +.repo-admin .repo-delete { + text-align: center; +} + +.repo-admin .repo-delete button { + font-size: 125%; +} + +.repo-admin .repo-access-state .state-icon { + text-align: center; + margin-bottom: 10px; +} + +.repo-admin .repo-access-state .state-icon i { + font-size: 46px; + width: 54px; + height: 54px; + line-height: 54px; + border-radius: 50%; + display: inline-block; +} + +.repo-admin .repo-access-state { + text-align: center; + width: 580px; +} + +.repo-admin .repo-access-state .state-icon i.fa-lock { + background: rgb(253, 191, 191); +} + +.repo-admin .repo-access-state .state-icon i.fa-unlock { + background: rgb(170, 236, 170); +} + +.repo-admin .change-access { + margin-top: 20px; + padding-top: 20px; + border-top: 1px solid #eee; + text-align: center; +} + +.repo-admin .change-access .alert { + color: black; + background: white; + border: 1px solid transparent; +} + +.repo-admin .change-access .alert .alert-content { + opacity: 0; +} + +.repo-admin .change-access:hover .alert { + background: inherit; + border: inherit; +} + +.user-admin #migrate .panel { + max-width: 600px; + text-align: center; +} + +.user-admin .panel-plan { + text-align: center; +} + +.panel-setting-content { + margin-left: 16px; + margin-top: 16px; + font-family: monospace; +} + +.user-admin .plan-description { + font-size: 1.2em; + margin-bottom: 10px; +} + +.user-admin .used-description { + font-size: 1.2em; + margin-bottom: 10px; +} + +.user-admin .convert-form h3 { + margin-bottom: 20px; +} + +.user-admin #convertForm { + max-width: 500px; +} + +.user-admin #convertForm .form-group { + margin-bottom: 20px; +} + +.user-admin #convertForm input { + margin-bottom: 10px; + margin-left: 20px; +} + +.user-admin #convertForm .existing-data { + font-size: 16px; + font-weight: bold; +} + +.user-admin #convertForm .description { + margin-top: 10px; + display: block; + color: #888; + font-size: 12px; + margin-left: 20px; +} + +.user-admin #convertForm .existing-data { + display: block; + padding-left: 20px; + margin-top: 10px; +} + +.user-admin .check-green { + color: #46ac39; +} + +#image-history-container { + overflow: hidden; + min-height: 400px; +} + +#image-history-container .node circle { + cursor: pointer; + fill: #fff; + stroke-width: 1.5px; + stroke: #ccc; + position: relative; +} + +#image-history-container .node circle.highlighted { + stroke: steelblue; +} + +#image-history-container .node circle.current { + fill: steelblue; + stroke-width: 2.5px; +} + +#image-history-container .node text.current { + font-size: 14px; + font-weight: bold; +} + +#image-history-container .node text.collapsed { + fill: gray; +} + +#image-history-container .node text { + font-size: 15px; + cursor: pointer; +} + +#image-history-container path.link { + fill: none; + stroke: #ccc; + stroke-width: 1.5px; +} + +#image-history-container path.link.highlighted { + stroke: steelblue; +} + +#image-history-container .tags { + text-align: center; +} + +.tags .tag, .tag-specific-images-view .tag { + display: inline-block; + border-radius: 10px; + margin-right: 4px; + cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; +} + +.tooltip-tags { + display: block; + margin-top: 10px; + border-top: 1px dotted #aaa; + padding-top: 10px; +} + +#changes-tree-container { + overflow: hidden; +} + +#changes-tree-container .node .change-icon { + font-size: 14px; +} + +#changes-tree-container .node.changed text { + fill: rgb(73, 100, 209); +} + +#changes-tree-container .node.added text { + fill: rgb(32, 163, 32); +} + +#changes-tree-container .node.removed text { + text-decoration: line-through; + fill: rgb(209, 73, 73); +} + +.file-tree-base .fa { + display: inline !important; +} + +.file-tree-base .node rect { + cursor: pointer; + fill: #fff; + fill-opacity: 1; + stroke: #fff; + stroke-width: 1.5px; +} + +.file-tree-base .node text { + font: 12px sans-serif; + pointer-events: none; +} + +.file-tree-base path.link { + fill: none; + stroke: #9ecae1; + stroke-width: 1.5px; +} + +.usage-chart-element { + display: inline-block; + vertical-align: middle; + width: 200px; + height: 200px; +} + +.usage-chart-element .count-text { + font-size: 22px; +} + +.usage-chart-element.limit-at path.arc-0 { + fill: #c09853; +} + +.usage-chart-element.limit-over path.arc-0 { + fill: #b94a48; +} + +.usage-chart-element.limit-near path.arc-0 { + fill: #468847; +} + +.usage-chart-element.limit-over path.arc-1 { + fill: #fcf8e3; +} + +.usage-chart-element.limit-at path.arc-1 { + fill: #f2dede; +} + +.usage-chart-element.limit-near path.arc-1 { + fill: #dff0d8; +} + +.usage-caption { + display: inline-block; + color: #aaa; + font-size: 26px; + margin-left: 10px; +} + + +/* Overrides for the markdown editor. */ + +.wmd-panel .btn-toolbar { + margin-bottom: 10px; +} + +.wmd-panel textarea { + outline: none; +} + +.wmd-panel.wmd-preview:before { + display: inline-block; + content: "Preview"; + background: #eee; + padding: 4px; + + position: absolute; + top: 0px; + right: 0px; +} + +.wmd-panel.wmd-preview { + position: relative; + border: 1px solid #eee; + margin-top: 10px; + padding: 10px; + min-height: 50px; +} + +.org-view .team-listing { + padding: 4px; +} + +.org-view .header-col { + color: #444; + margin-bottom: 10px; +} + +.org-view .header-col dd { + margin-bottom: 20px; +} + +.org-view .header-col .info-icon { + float: none; + margin-left: 10px; +} + +.org-view .team-listing .control-col button.btn-danger { + margin-left: 10px; +} + +.org-view .team-listing i { + margin-right: 10px; +} + +.org-view .highlight .team-title { + animation: highlighttemp 1s 2; + animation-timing-function: ease-in-out; + animation-direction: alternate; + + -moz-animation: highlighttemp 1s 2; + -moz-animation-timing-function: ease-in-out; + -moz-animation-direction: alternate; + + -webkit-animation: highlighttemp 1s 2; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-direction: alternate; +} + +@-moz-keyframes highlighttemp { + 0% { background-color: white; } + 100% { background-color: rgba(92, 184, 92, 0.36); } +} + +@-webkit-keyframes highlighttemp { + 0% { background-color: white; } + 100% { background-color: rgba(92, 184, 92, 0.36); } +} + +@keyframes highlighttemp { + 0% { background-color: white; } + 100% { background-color: rgba(92, 184, 92, 0.36); } +} + +.org-view .team-title { + font-size: 20px; + text-transform: none; + padding: 4px; +} + +.org-view .team-listing .team-description { + margin-top: 6px; + margin-left: 41px; + font-size: 16px; +} + +.org-view #create-team-box { + border: none; + font-size: 14px; + padding: 6px; +} + +.org-admin .team-link { + display: inline-block; + text-transform: none; + margin-right: 20px; +} + +.org-admin #members table td { + font-size: 16px; +} + +.org-admin #members table i { + margin-right: 4px; +} + +.side-controls { + float: right; +} + +.side-controls .result-count { + display: inline-block; + margin-right: 10px; +} + +.side-controls .filter-input { + display: inline-block; +} + +.table-container { + max-width: 100%; +} + +.org-list h2 { + margin-bottom: 20px; +} + +.org-list .button-bar-right { + text-align: right; +} + +.org-list .organization-listing { + font-size: 18px; + padding: 10px; +} + +.create-org .steps-container { + text-align: center; +} + +.create-org .steps { + + background: #222; + + display: inline-block; + margin-top: 16px; + margin-left: 0px; + border-radius: 4px; + padding: 0px; + list-style: none; + height: 46px; + width: 675px; + text-align: left; +} + + +.create-org .steps .step { + width: 225px; + float: left; + padding: 10px; + border-right: 1px solid #222; + margin: 0px; + background: rgba(255, 255, 255, 0.2); + color: #aaa; + border-left: 4px solid transparent; +} + +.create-org .steps .step i { + font-size: 26px; + margin-right: 6px; + vertical-align: middle; +} + +.create-org .steps .step.active { + color: white; + border-left: 4px solid steelblue; + background: transparent; +} + +.create-org .steps .step:last-child { + border-right: 0px; +} + +.create-org .steps .step b { + display: block; +} + +.create-org .button-bar { + margin-bottom: 40px; +} + +.create-org .form-group { + margin-bottom: 32px; +} + +.create-org .plan-group { + padding-left: 10px; +} + +.create-org .plan-group strong { + margin-bottom: 10px; +} + +.form-group .description { + margin-top: 10px; + display: block; + color: #888; + font-size: 12px; + margin-left: 10px; +} + +.form-group.nested input { + margin-top: 10px; + margin-left: 10px; +} + +.create-org h3 { + margin-bottom: 20px; +} + +.repo-breadcrumb-element .crumb { + cursor: pointer; +} + +.repo-breadcrumb-element .crumb:nth-last-of-type(3), .repo-breadcrumb-element .crumb:nth-last-of-type(3) a { + color: #aaa !important; +} + +.repo-breadcrumb-element .crumb:nth-last-of-type(2), .repo-breadcrumb-element .crumb:nth-last-of-type(2) a { + color: #888 !important; +} + +.repo-breadcrumb-element .current a { + color: #444 !important; +} + +.repo-breadcrumb-element .crumb:after { + content: "/"; + color: #ccc; + margin-left: 4px; +} + +.repo-breadcrumb-element .crumb:hover, .repo-breadcrumb-element .crumb:hover a, .repo-breadcrumb-element .current:hover a { + color: #2a6496 !important; + text-decoration: none; +} + +/* Overrides for typeahead to work with bootstrap 3. */ + +.twitter-typeahead .tt-query, +.twitter-typeahead .tt-hint { + margin-bottom: 0; +} + +.tt-dropdown-menu { + min-width: 160px; + margin-top: 2px; + padding: 5px 0; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); + box-shadow: 0 5px 10px rgba(0,0,0,.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.tt-suggestion { + display: block; + padding: 3px 20px; + cursor: pointer; +} + +.tt-suggestion.tt-cursor { + color: #fff; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0) +} + +.tt-suggestion.tt-cursor a { + color: #fff; +} + +.tt-empty { + padding: 10px; + font-size: 12px; + color: #aaa; + white-space: nowrap; +} + +.tt-message { + padding: 10px; + font-size: 12px; + white-space: nowrap; +} + +.tt-suggestion p { + margin: 0; +} + +.twitter-typeahead .tt-hint { + display: block; + height: 34px; + padding: 5px 12px; + font-size: 14px; + line-height: 1.428571429; + border: 1px solid transparent; + width: 100%; +} + +/** Fix for bootstrap dialogs that are broken. */ + +.modal-backdrop.in { + opacity: 0.5 !important; +} + +/** D3 tooltip styling */ + +.d3-tip, .nvtooltip { + line-height: 1 !important; + padding: 12px !important; + background: rgba(0, 0, 0, 0.8) !important; + color: #fff !important; + border-radius: 2px !important; + max-width: 500px !important; + z-index: 9999999; +} + +/* Creates a small triangle extender for the tooltip */ +.d3-tip:after { + box-sizing: border-box; + display: inline; + font-size: 10px; + width: 100%; + line-height: 1; + color: rgba(0, 0, 0, 0.8); + position: absolute; +} + +/* Nrthward tooltips */ +.d3-tip.n:after { + content: "\25BC"; + margin: -1px 0 0 0; + top: 100%; + left: 0; + text-align: center; +} + +/* Eastward tooltips */ +.d3-tip.e:after { + content: "\25C0"; + margin: -4px 0 0 0; + top: 50%; + left: -8px; +} + +/* Southward tooltips */ +.d3-tip.s:after { + content: "\25B2"; + margin: 0 0 1px 0; + top: -8px; + left: 0; + text-align: center; +} + +/* Westward tooltips */ +.d3-tip.w:after { + content: "\25B6"; + margin: -4px 0 0 -1px; + top: 50%; + left: 100%; +} + +.d3-tip .full-id { + font-size: 11px; + color: #ddd; + display: block; +} + +.d3-tip .command { + font-size: 12px; + color: white; + display: block; + font-family: Consolas, "Lucida Console", Monaco, monospace; +} + +.d3-tip .info-line { + display: block; + margin-top: 6px; + padding-top: 6px; +} + +.d3-tip .info-line i { + margin-right: 10px; + font-size: 14px; + color: #888; +} + +.d3-tip .comment { + display: block; + font-size: 14px; + padding-bottom: 4px; + margin-bottom: 10px; + border-bottom: 1px dotted #ccc; +} + +.d3-tip .created { + font-size: 12px; + color: white; + display: block; + margin-bottom: 6px; +} + +.privacy-policy dd, +.tos dd { + margin-bottom: 20px; +} + +.tos li { + margin-bottom: 10px; +} + +.tos ul { + margin-top: 10px; +} + +.social-alternate { + color: #777; + font-size: 3em; + margin-left: 43px; +} + +.social-alternate .inner-text { + text-align: center; + position: relative; + color: white; + left: -38px; + top: -9px; + font-weight: bold; + font-size: .4em; +} + +.page-description { + margin-bottom: 40px; +} + +.contact-options { + margin-top: 60px; +} + +pre.command { + padding: 20px; + background: #fff; + text-shadow: none; + overflow: auto; + border: solid 1px #ccc; + position: relative; + margin-top: 10px; + margin-bottom: 20px; +} + +pre.command:before { + content: "\f120"; + font-family: "FontAwesome"; + font-size: 16px; + margin-right: 6px; + color: #999; +} + +.form-inline { + display: inline-block; + margin-left: 10px; +} + +/*********************************************/ + +.contact-options .option-twitter .fa-circle { + color: #00b0ed; +} + +.contact-options .option-tel .fa-circle { + color: #1dd924; +} + +.contact-options .option-irc .fa-circle { + color: #e52f00; +} + +.contact-options .option-mailto .fa-circle { + color: #1b72f1; +} + +.contact-options .option-url .fa-circle { + color: #F1A51B; +} + +.about-us .row { + margin-bottom: 30px; +} + +.about-basic-icon { + display: inline-block; + margin-right: 10px; + color: #555; +} + +.about-basic-text { + display: inline-block; +} + +.repo-name-small:before { + content: "\f0a0"; + font-family: FontAwesome; + font-size: 20px; + margin-right: 6px; + vertical-align: middle; + font-weight: normal; +} + +.repo-name-small { + margin-left: 2px; + margin-right: 2px; + display: inline-block; + font-size: 14px; + font-weight: bold; + padding: 4px 8px; +} + +.user-enter-box { + padding: 10px; + margin-bottom: 20px; + border: 1px solid #DDE7ED; + background: #F0FAFF; +} + +.file-drop { + padding: 10px; + margin: 10px; +} + +.dropdown-select { + margin: 10px; + position: relative; +} + +.dropdown-select .dropdown-select-icon { + position: absolute; + top: 6px; + left: 6px; + z-index: 2; + display: none; +} + +.dropdown-select .dropdown-select-icon.fa { + top: 10px; + left: 8px; + font-size: 20px; +} + +.dropdown-select .dropdown-select-icon.none-icon { + color: #ccc; + display: inline; +} + +.dropdown-select.has-item .dropdown-select-icon { + display: inline; +} + +.dropdown-select.has-item .dropdown-select-icon.none-icon { + display: none; +} + +.dropdown-select input.form-control[readonly] { + cursor: pointer; + background-color: #fff; +} + +.dropdown-select .lookahead-input { + padding-left: 32px; +} + +.dropdown-select .twitter-typeahead { + display: block !important; +} + +.dropdown-select .twitter-typeahead .tt-hint { + padding-left: 32px; +} + +.dropdown-select .dropdown { + position: absolute; + right: 0px; + top: 0px; +} + +.dropdown-select .dropdown button.dropdown-toggle { + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; +} + +.slideinout { + -webkit-transition:0.5s all; + transition:0.5s linear all; + opacity: 1; + + position: relative; + + height: 32px; + opacity: 1; +} + +.slideinout.ng-hide { + opacity: 0; + height: 0px; +} + +.slideinout.ng-hide-add, .slideinout.ng-hide-remove { + display: block !important; +} + +.auth-header > .avatar { + float: left; + display: inline-block; + margin-top: 12px; + margin-right: 20px; +} + +.auth-header { + padding-bottom: 10px; + border-bottom: 1px solid #eee; + margin-bottom: 10px; +} + +.auth-scopes .reason { + margin-top: 20px; + margin-bottom: 20px; + font-size: 18px; +} + +.auth-scopes ul { + margin-top: 10px; + list-style: none; +} + +.auth-scopes li { + display: block; +} + +.auth-scopes .scope { + max-width: 500px; +} + +.auth-scopes .scope .fa-exclamation-triangle { + color: orange; + margin-left: 16px; + display: inline-block; +} + +.auth-scopes .scope-container:last-child { + border-bottom: 0px; +} + +.auth-scopes .panel-default { + border: 0px; + margin-bottom: 0px; + padding-bottom: 10px; + box-shadow: none; +} + +.auth-scopes .panel-default:last-child { + border-bottom: 0px; +} + +.auth-scopes .panel-heading { + border: 0px; + background: transparent; +} + +.auth-scopes .scope .title { + min-width: 300px; + cursor: pointer; + display: inline-block; +} + +.auth-scopes .scope .title a { + color: #444; +} + +.auth-scopes .scope .description { + padding: 10px; +} + +.auth-scopes .scope i { + margin-right: 10px; + margin-top: 2px; +} + +.auth-scopes .scope i.fa-lg { + font-size: 24px; +} + +.auth-scopes .title i.arrow:before { + content: "\f0d7"; +} + +.auth-scopes .title.collapsed i.arrow:before { + content: "\f0da" !important; +} + +.auth-container .button-bar form { + display: inline-block; +} + +.auth-container .button-bar { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #eee; +} + +.auth-container .button-bar button { + margin: 6px; +} + +.manage-application #oauth td { + padding: 6px; + padding-bottom: 20px; +} + +.manage-application .button-bar { + margin-top: 10px; + padding-top: 20px; + border-top: 1px solid #eee; +} + +.auth-info .by:before { + content: "by"; + margin-right: 4px; +} + +.auth-info .by { + color: #aaa; + font-size: 12px; +} + +.auth-info .scope { + cursor: pointer; + margin-right: 4px; +} + +.trigger-pull-credentials { + padding-left: 26px; + font-size: 12px; +} + +.trigger-pull-credentials .entity-reference { + margin-left: 10px; +} + +.trigger-pull-credentials .context-tooltip { + color: gray; + margin-right: 4px; +} + +.trigger-option-section:not(:first-child) { + border-top: 1px solid #eee; + padding-top: 16px; + margin-top: 10px; +} + +.trigger-option-section .entity-search-element input { + width: 100%; +} + +.trigger-option-section table td { + padding: 6px; +} + +.form-change input { + margin-top: 12px; + margin-bottom: 12px; +} + +.location-view { + display: inline-block; + margin-rigth: 6px; +} + +.location-view .flag { + position: relative; + transition: opacity 0.5s ease; +} + +.location-view .strength-indicator { + display: inline-block; + vertical-align: middle; +} + +/* +This is the visible area of you carousel. +Set a width here to define how much items are visible. +The width can be either fixed in px or flexible in %. +Position must be relative! +*/ +.jcarousel { + position: relative; + overflow: hidden; + margin-left: 30px; + margin-right: 30px; +} + +.jcarousel-wrapper { + position: relative; +} + +.jcarousel-wrapper .jcarousel-control { + font-size: 64px; + position: absolute; + top: -16px; + text-decoration: none !important; +} + +.jcarousel-wrapper .jcarousel-control-prev { + left: 0px; +} + +.jcarousel-wrapper .jcarousel-control-next { + right: 0px; +} + +/* +This is the container of the carousel items. +You must ensure that the position is relative or absolute and +that the width is big enough to contain all items. +*/ +.jcarousel ul { + width: 20000em; + position: relative; + + /* Optional, required in this case since it's a
      element */ + list-style: none; + margin: 0; + padding: 0; +} + +/* +These are the item elements. jCarousel works best, if the items +have a fixed width and height (but it's not required). +*/ +.jcarousel li { + /* Required only for block elements like
    • 's */ + float: left; + display: block; +} + +.jcarousel-page { + padding: 10px; + text-decoration: none !important; + font-size: 18px; + color: #aaa; +} + +.jcarousel-page.active { + color: #428bca; +} + +.jcarousel-page:before { + content: "\f10c"; + font-family: FontAwesome; +} + +.jcarousel-page.active:before { + content: "\f111"; + font-family: FontAwesome; +} + +.jcarousel-pagination { + display: block; + text-align: center; +} + +.tour-section-container { + text-align: center; + border-bottom: 1px solid #eee; +} + +.tour-sections { + margin: 0px; + padding: 0px; +} + +.tour-sections li { + margin: 0px; + font-size: 16px; + list-style: none; + display: inline-block; + border-right: 1px solid #eee; + border-bottom: 2px solid transparent; + cursor: pointer; +} + +.tour-sections li a { + display: block; + color: black; + padding: 10px; + padding-left: 20px; + padding-right: 20px; + text-decoration: none !important; +} + +.tour-sections li:hover { + background: #eee; +} + +.tour-sections li.active { + border-bottom: 2px solid #428bca; +} + +.tour-sections li:first-child { + border-left: 1px solid #eee; +} + +.tour-action { + text-align: center; + padding: 40px; + background: #eee; +} + +.tour-action button { + font-size: 24px; +} + +.tour-section.tour-header { + margin-top: 40px; + margin-bottom: 40px; +} + +.tour-section.tour-header .tour-section-title { + text-align: center; +} + +.screenshot-feature { + margin-bottom: 20px; + position: relative; +} + +.screenshot-feature .fa { + float: left; + display: inline-block; + width: 50px; + height: 50px; + line-height: 50px; + border-radius: 50%; + background: #eee; + font-size: 28px; + text-align: center; + margin-right: 20px; + margin-top: 0px; + clear: both; +} + +.screenshot-feature.active .fa { + color: white; + background: #428bca; +} + + +.screenshot-feature .sf-title { + font-size: 20px; + display: inline-block; + margin-bottom: 10px; + margin-top: 6px; +} + +.screenshot-feature:hover { + cursor: pointer; +} + +.screenshot-feature:hover .sf-title { + text-decoration: underline; +} + +.screenshot-feature .sf-text { + margin-left: 70px; +} + +.trusted-logos { + text-align: center; +} + +.trusted-logos .trusted-logo { + min-height: 75px; +} + +.trusted-logos .trusted-logo.apptentive { + padding-top: 20px; +} + +.enterprise-icon { + text-align: center; +} + +.testimonial { + text-align: center; +} + +.testimonial .message:before { + font-size: 32px; + content: "“"; + color: #aaa; +} + +.testimonial .message:after { + font-size: 32px; + content: "”"; + color: #aaa; +} + +.testimonial .message { + font-size: 22px; +} + +.testimonial img { + width: 120px; + margin: 10px; + border: 1px solid black; + border-radius: 10px; + margin-right: 20px; +} + +.testimonial .speaker-info { + font-size: 18px; + margin-top: 10px; +} + +.testimonial .speaker-info span { + display: inline-block; + margin-right: 20px; +} + +.testimonial .speaker-info .speaker-title { + color: #888; +} + +.testimonial .speaker-info .speaker-title a { + text-decoration: none; + color: #888; +} + +.learn-more { + float: right; + font-size: 22px; +} + +i.quay-icon { + background-image: url(/static/img/quay_favicon.png); + background-size: contain; + width: 16px; + height: 15px; +} + +i.flowdock-icon { + background-image: url(/static/img/flowdock.ico); + background-size: 16px; + width: 16px; + height: 16px; +} + +i.hipchat-icon { + background-image: url(/static/img/hipchat.png); + background-size: 16px; + width: 16px; + height: 16px; +} + +i.slack-icon { + background-image: url(/static/img/slack.ico); + background-size: 16px; + width: 16px; + height: 16px; +} + +i.docker-icon { + background-image: url(/static/img/docker.png); + background-size: 16px; + width: 16px; + height: 16px; +} + +i.rocket-icon { + background-image: url(/static/img/rocket.png); + background-size: 16px; + width: 16px; + height: 16px; +} + +i.kubernetes-icon { + background-image: url(/static/img/kubernetes.svg); + background-size: 16px; + width: 16px; + height: 16px; + background-position-y: -4px; +} + +i.mesos-icon { + background-image: url(/static/img/mesos.svg); + background-size: 16px; + width: 16px; + height: 16px; +} + +.external-notification-view-element { + margin: 10px; + padding: 6px; + border: 1px solid #eee; + border-radius: 6px; +} + +.external-notification-view-element i.fa { + margin-right: 6px; +} + +.external-notification-view-element .view-row { + margin-bottom: 10px; +} + +.external-notification-view-element .view-row:last-child { + margin-bottom: 0px; +} + +.external-notification-view-element .flow-text { + display: inline-block; + color: #aaa; + text-transform: lowercase; + font-variant: small-caps; + width: 50px; +} + +.external-notification-view-element .side-controls button { + border: 1px solid transparent; + transition: all 300ms ease-in-out; +} + +.external-notification-view-element:hover .side-controls button { + border: 1px solid #eee; +} + +.member-listing { + width: 100%; +} + +.member-listing .section-header { + color: #ccc; + margin-top: 20px; + margin-bottom: 10px; +} + +.member-listing .avatar { + vertical-align: middle; + margin-right: 10px; +} + +.member-listing .entity-reference { + margin-bottom: 10px; + display: inline-block; +} + +.member-listing .invite-listing { + margin-bottom: 10px; + display: inline-block; +} + +.team-view .organization-header .popover { + max-width: none !important; +} + +.team-view .organization-header .popover.bottom-right .arrow:after { + border-bottom-color: #f7f7f7; + top: 2px; +} + +.team-view .organization-header .popover-content { + font-size: 14px; + padding-top: 6px; +} + +.team-view .organization-header .popover-content input { + background: white; +} + +.team-view .team-view-add-element .help-text { + font-size: 13px; + color: #ccc; + margin-top: 10px; +} + +.team-view .organization-header .popover-content { + min-width: 500px; +} + +#startTriggerDialog trigger-description { + margin-bottom: 20px; + padding-bottom: 20px; + border-bottom: 1px solid #eee; +} + +#startTriggerDialog #runForm .field-title { + width: 120px; + padding-right: 10px; +} + +.progress.active .progress-bar { + /* Note: There is a bug in Chrome which results in high CPU usage for active progress-bars + due to their animation. This enables the GPU for the rendering, which cuts CPU usage in + half (although it is still not great) + */ + transform: translateZ(0); +} + +#gen-token table { + margin: 10px; +} + +#gen-token input[type="checkbox"] { + margin-right: 10px; +} + +.system-log-download-panel { + padding: 20px; + text-align: center; + font-size: 18px; +} + +.system-log-download-panel a { + margin-top: 20px; +} + +.initial-setup-modal .quay-spinner { + vertical-align: middle; + margin-right: 10px; + display: inline-block; +} + +.initial-setup-modal .valid-database p { + font-size: 18px; +} + +.verified { + font-size: 16px; + margin-bottom: 16px; +} + +.verified i.fa { + font-size: 26px; + margin-right: 10px; + vertical-align: middle; + color: rgb(53, 186, 53); +} + +.registry-logo-preview { + border: 1px solid #eee; + vertical-align: middle; + padding: 4px; + max-width: 150px; +} + +.modal-footer.alert { + text-align: left; + margin-bottom: -16px; +} + + +.restart-required { + position: relative; + padding-left: 54px; +} + +.restart-required button { + float: right; + margin-top: 4px; +} + +.restart-required button i.fa { + margin-right: 6px; +} + +.restart-required i.fa-warning { + position: absolute; + top: 24px; + left: 16px; + font-size: 28px; +} + +.chart-col h4, .chart-col h5 { + display: block; + text-align: center; +} + +.section-description-header { + position: relative; + margin-bottom: 10px; + min-height: 50px; + padding-bottom: 10px; +} + +.nvtooltip h3 { + margin: 0; + padding: 4px 14px; + line-height: 18px; + font-weight: normal; + background-color: transparent !important; + text-align: center; + font-size: 16px !important; + border: none !important; +} + +.build-log-view { + float: left; + padding-right: .5em; +} + +@keyframes loading-js-fade-out-animation { + from { opacity: 1; } + to { opacity: 0; } +} + +@keyframes loading-js-fade-in-animation { + from { opacity: 0; } + to { opacity: 1; } +} + +.loading-js-fade-out { + opacity: 1; + animation-name: loading-js-fade-out-animation; + animation-duration: 1s; + animation-delay: 3s; + animation-fill-mode: forwards; +} + +.loading-js-fade-in { + opacity: 0; + animation-name: loading-js-fade-in-animation; + animation-duration: 1s; + animation-delay: 3s; + animation-fill-mode: forwards; +} + +.page-footer { + display: flex; + align-items: center; +} + +/* New styling below this line */ +* { + font-family: "Overpass", arial, helvetica, sans-serif; +} + +.navbar-header{ + width: 100%; +} + +.search-box-element input{ + width: auto; +} + +.page-footer a{ + color: #252525; +} + +.page-footer a:hover{ + color: #06c; + text-decoration: none; +} + +.user-tools .user-tool, +.navbar-right a{ + margin-top: 0px; + color: #252525; +} + + +/******************************/ +/* begin styles from main.css */ +.landing-page .product-description{ + width: auto; +} + +.landing.jumbotron .buttons .btn{ + font-size: 20px; + padding: 6px; +} +/* end styles from main.css */ + + +@media (min-width: 768px){ + .navbar-collapse { + padding-left: 10px; + } + + nav.navbar-default .navbar-nav > li > a { + font-size: 13px; + text-transform: uppercase; + padding: 25px 5px; + color: #252525; + } + + body.signedin nav.navbar-default .navbar-nav > li > a.user-view{ + padding: 19px 5px; + text-transform: initial; + width: 150px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .navbar-header{ + width: auto; + } + + .header-bar-element .header-bar-content{ + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + } + + .header-bar-element .search-box-element{ + margin-top: 0px; + } + + .navbar-right{ + display: flex; + align-items: center; + } + + .navbar-default .navbar-collapse{ + width: 100%; + } + + /* begin styles from main.css */ + .landing-page .product-description{ + width: 400px; + } + + .landing.jumbotron .buttons .btn{ + font-size: 26px; + padding: 6px 40px; + } + + /* end styles from main.css */ +} + +@media (min-width: 992px){ + .navbar-collapse { + padding-left: 15px; + } + + nav.navbar-default .navbar-nav > li > a { + font-size: 15px; + padding: 25px 15px; + } + + body.signedin nav.navbar-default .navbar-nav > li > a.user-view{ + padding: 19px 15px; + } +} + + +/*** repo css ***/ + +.onprem a { + color:#0066cc; +} + +.onprem .main-panel.co-fx-box-shadow-heavy { + box-shadow:none; + border:none; +} + +.onprem .co-fx-text-shadow { + text-shadow:none; + color:#404040; +} + +.onprem .btn-primary.active, +.onprem .btn-primary.focus, +.onprem .btn-primary:active, +.onprem .btn-primary:focus, +.onprem .btn-primary:hover, +.onprem .open>.dropdown-toggle.btn-primary { + background:#6C6C6C; +} + +.onprem .btn-info.active, +.onprem .btn-info.focus, +.onprem .btn-info:active, +.onprem .btn-info:focus, +.onprem .btn-info:hover, +.onprem .open>.dropdown-toggle.btn-info { + background-color: #adadad; + border-color: #afafaf; +} + +.onprem .btn-primary { + background-color: #7b7b7b; + border-color: #a1a3a5; +} + +.onprem .btn-primary.disabled, +.onprem .btn-primary.disabled.active, +.onprem .btn-primary.disabled.focus, +.onprem .btn-primary.disabled:active, +.onprem .btn-primary.disabled:focus, +.onprem .btn-primary.disabled:hover, +.onprem .btn-primary[disabled], +.onprem .btn-primary[disabled].active, +.onprem .btn-primary[disabled].focus, +.onprem .btn-primary[disabled]:active, +.onprem .btn-primary[disabled]:focus, +.onprem .btn-primary[disabled]:hover, +.onprem fieldset[disabled] .btn-primary, +.onprem fieldset[disabled] .btn-primary.active, +.onprem fieldset[disabled] .btn-primary.focus, +.onprem fieldset[disabled] .btn-primary:active, +.onprem fieldset[disabled] .btn-primary:focus, +.onprem fieldset[disabled] .btn-primary:hover { + background-color: #cccccc; + border-color: #b3b3b3; +} + +.onprem .cor-title-link a { + color: black; +} + +.onprem .cor-title-link a.back-link:before { + color: black; +} + +.onprem .co-fx-text-shadow span { + text-shadow:none; + color: black; +} + +.onprem .co-img-bg-network { + background: url(/static/img/network-tile.png) 0 0 repeat,linear-gradient(30deg,#f5f5f5,#f5f5f5) no-repeat 0 0 fixed; + background-color: #d8d8d8; +} + +.hosted .co-img-bg-network { + background: url(/static/img/network-tile.png) 0 0 repeat,linear-gradient(10deg,#2d2d2d, #484848) no-repeat 0 0 fixed; + background-size: auto, 100% 100%; +} + +.onprem .co-main-content-panel { + border:1px solid #E7E7E7; + box-shadow: none; +} + +.onprem .vertical cor-tabs { + background-color:#E7E7E7; +} + +.hosted .vertical cor-tabs { + background-color:#E7E7E7; +} + +.onprem .file-upload-box-element .file-drop+label .choose-button { + background-color: #CC0000; +} + +.file-upload-box-element .file-drop + label .choose-button { + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; + display: inline-block; + overflow: hidden; + padding: 8px; + color: #fff; + font-weight: 400; + font-size: 16px; + height: 34px; + vertical-align: top; + padding-left: 16px; + padding-right: 16px; + line-height: 18px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.file-upload-box-element .file-drop { + width: .1px; + height: .1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +} + +.file-upload-box-element .select-message { + color: #888; +} + +.file-upload-box-element .file-drop + label { + cursor: pointer; +} + +.file-upload-box-element .file-drop + label .chosen-file { + background: #fff; + display: inline-block; + height: 34px; + line-height: 34px; + padding-left: 10px; + padding-right: 10px; + font-weight: 400; + width: 250px; + border: 1px solid #ccc; + border-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.file-upload-box-element .file-drop + label * { + pointer-events: none; +} + +.onprem .co-nav-title .co-nav-title-action a { + color: black; +} + +.onprem .co-table thead td.current a { + color: #212121; + font-weight: bold; +} + +.onprem .aside .aside-dialog .aside-header { + background: #b0b1b1; +} + +.onprem .co-m-loader-dot__one, +.onprem .co-m-loader-dot__two, +.onprem .co-m-loader-dot__three { + background: #656565; +} + +.hosted .co-m-loader-dot__one, +.hosted .co-m-loader-dot__two, +.hosted .co-m-loader-dot__three { + background: ; +} + +.onprem .co-nav-title .co-nav-title-content { + color: black; +} + +.onprem .manifest-view-layer-element.first .image-layer-dot { + background: #868686; +} + +.onprem .manifest-view-layer-element .image-layer-dot { + border: 2px solid #868686; +} + +.onprem .manifest-view-layer-element .image-layer-line { + border-left: 2px solid #868686; +} + +.onprem .manifest-link .id-label { + width: 46px; +} + +.hosted .manifest-link .id-label { + width: 46px; +} + +.onprem .form-control:focus { + border-color: #cecece; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(175, 175, 175, 0.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(175, 175, 175, 0.6); +} + +.onprem .tt-suggestion.tt-cursor { + color: black; + background: #dfdfdf; +} + +.onprem .signin-form-element .external-login-button a { + color: #000000; +} + +.onprem i.quay-icon { + width: 15px; + height: 15px; +} + +.page-footer ul { + margin-top: 0px; +} diff --git a/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.eot b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.eot differ diff --git a/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.svg b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.ttf b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.ttf differ diff --git a/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.woff b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.woff differ diff --git a/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.woff2 b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/fontawesome-webfont.woff2 differ diff --git a/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.eot b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.eot differ diff --git a/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.svg b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.ttf b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.ttf differ diff --git a/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.woff b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..9e612858f Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.woff differ diff --git a/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.woff2 b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/config-tool/pkg/lib/editor/static/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/config-tool/pkg/lib/editor/static/img/favicon.ico b/config-tool/pkg/lib/editor/static/img/favicon.ico new file mode 100644 index 000000000..2b6e308db Binary files /dev/null and b/config-tool/pkg/lib/editor/static/img/favicon.ico differ diff --git a/config-tool/pkg/lib/editor/static/img/network-tile.png b/config-tool/pkg/lib/editor/static/img/network-tile.png new file mode 100644 index 000000000..c27deaff2 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/img/network-tile.png differ diff --git a/config-tool/pkg/lib/editor/static/img/quay-horizontal-color.svg b/config-tool/pkg/lib/editor/static/img/quay-horizontal-color.svg new file mode 100644 index 000000000..5ff210b1b --- /dev/null +++ b/config-tool/pkg/lib/editor/static/img/quay-horizontal-color.svg @@ -0,0 +1 @@ +quay \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/static/img/redis-small.png b/config-tool/pkg/lib/editor/static/img/redis-small.png new file mode 100644 index 000000000..c330a720e Binary files /dev/null and b/config-tool/pkg/lib/editor/static/img/redis-small.png differ diff --git a/config-tool/pkg/lib/editor/static/img/rocket.png b/config-tool/pkg/lib/editor/static/img/rocket.png new file mode 100644 index 000000000..b9ffddf39 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/img/rocket.png differ diff --git a/config-tool/pkg/lib/editor/static/index.html b/config-tool/pkg/lib/editor/static/index.html new file mode 100644 index 000000000..9885006e7 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/index.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + Quay Config Editor + + + + + + \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/static/ldn/all.css-334bc939e54d.css b/config-tool/pkg/lib/editor/static/ldn/all.css-334bc939e54d.css new file mode 100644 index 000000000..05d0860e9 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/ldn/all.css-334bc939e54d.css @@ -0,0 +1,5 @@ +/*! + * Font Awesome Free 5.0.4 by @fontawesome - http://fontawesome.com + * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:a 2s infinite linear;animation:a 2s infinite linear}.fa-pulse{-webkit-animation:a 1s infinite steps(8);animation:a 1s infinite steps(8)}@-webkit-keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-aws:before{content:"\f375"}.fa-backward:before{content:"\f04a"}.fa-balance-scale:before{content:"\f24e"}.fa-ban:before{content:"\f05e"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bicycle:before{content:"\f206"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blind:before{content:"\f29d"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-braille:before{content:"\f2a1"}.fa-briefcase:before{content:"\f0b1"}.fa-btc:before{content:"\f15a"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-car:before{content:"\f1b9"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-certificate:before{content:"\f0a3"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-square:before{content:"\f14a"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-clipboard:before{content:"\f328"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comments:before{content:"\f086"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-credit-card:before{content:"\f09d"}.fa-crop:before{content:"\f125"}.fa-crosshairs:before{content:"\f05b"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-deviantart:before{content:"\f1bd"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dollar-sign:before{content:"\f155"}.fa-dot-circle:before{content:"\f192"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drupal:before{content:"\f1a9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-excel:before{content:"\f1c3"}.fa-file-image:before{content:"\f1c5"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fire:before{content:"\f06d"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-order:before{content:"\f2b0"}.fa-firstdraft:before{content:"\f3a1"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frown:before{content:"\f119"}.fa-futbol:before{content:"\f1e3"}.fa-gamepad:before{content:"\f11b"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-gift:before{content:"\f06b"}.fa-git:before{content:"\f1d3"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-martini:before{content:"\f000"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-gofore:before{content:"\f3a7"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-handshake:before{content:"\f2b5"}.fa-hashtag:before{content:"\f292"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-heart:before{content:"\f004"}.fa-heartbeat:before{content:"\f21e"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hospital:before{content:"\f0f8"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-houzz:before{content:"\f27c"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-internet-explorer:before{content:"\f26b"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-jenkins:before{content:"\f3b6"}.fa-joget:before{content:"\f3b7"}.fa-joomla:before{content:"\f1aa"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-key:before{content:"\f084"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-korvue:before{content:"\f42f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-male:before{content:"\f183"}.fa-map:before{content:"\f279"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-maxcdn:before{content:"\f136"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-meh:before{content:"\f11a"}.fa-mercury:before{content:"\f223"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-moon:before{content:"\f186"}.fa-motorcycle:before{content:"\f21c"}.fa-mouse-pointer:before{content:"\f245"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nintendo-switch:before{content:"\f418"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-paint-brush:before{content:"\f1fc"}.fa-palfed:before{content:"\f3d8"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-paragraph:before{content:"\f1dd"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-percent:before{content:"\f295"}.fa-periscope:before{content:"\f3da"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phone:before{content:"\f095"}.fa-phone-square:before{content:"\f098"}.fa-phone-volume:before{content:"\f2a0"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-plane:before{content:"\f072"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-print:before{content:"\f02f"}.fa-product-hunt:before{content:"\f288"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-random:before{content:"\f074"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-rebel:before{content:"\f1d0"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-rendact:before{content:"\f3e4"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-resolving:before{content:"\f3e7"}.fa-retweet:before{content:"\f079"}.fa-road:before{content:"\f018"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-rupee-sign:before{content:"\f156"}.fa-safari:before{content:"\f267"}.fa-sass:before{content:"\f41e"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-scribd:before{content:"\f28a"}.fa-search:before{content:"\f002"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shower:before{content:"\f2cc"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowflake:before{content:"\f2dc"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-spinner:before{content:"\f110"}.fa-spotify:before{content:"\f1bc"}.fa-square:before{content:"\f0c8"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-star:before{content:"\f005"}.fa-star-half:before{content:"\f089"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-strava:before{content:"\f428"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-table:before{content:"\f0ce"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-trademark:before{content:"\f25c"}.fa-train:before{content:"\f238"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-circle:before{content:"\f2bd"}.fa-user-md:before{content:"\f0f0"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-volume-down:before{content:"\f027"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vuejs:before{content:"\f41f"}.fa-weibo:before{content:"\f18a"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wrench:before{content:"\f0ad"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:Font Awesome\ 5 Brands;font-style:normal;font-weight:400;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:Font Awesome\ 5 Brands}@font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:400;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-weight:400}@font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:900;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.far,.fas{font-family:Font Awesome\ 5 Free}.fa,.fas{font-weight:900} \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/static/ldn/bootstrap.min.css-13f668494557.css b/config-tool/pkg/lib/editor/static/ldn/bootstrap.min.css-13f668494557.css new file mode 100644 index 000000000..28f154dec --- /dev/null +++ b/config-tool/pkg/lib/editor/static/ldn/bootstrap.min.css-13f668494557.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.2 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px \9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.form-group-sm .form-control{height:30px;line-height:30px}select[multiple].form-group-sm .form-control,textarea.form-group-sm .form-control{height:auto}.form-group-sm .form-control-static{height:30px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.form-group-lg .form-control{height:46px;line-height:46px}select[multiple].form-group-lg .form-control,textarea.form-group-lg .form-control{height:auto}.form-group-lg .form-control-static{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.active,.btn-default.focus,.btn-default:active,.btn-default:focus,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.active,.btn-primary.focus,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.active,.btn-success.focus,.btn-success:active,.btn-success:focus,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.active,.btn-info.focus,.btn-info:active,.btn-info:focus,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.active,.btn-warning.focus,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.active,.btn-danger.focus,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none;visibility:hidden}.collapse.in{display:block;visibility:visible}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none;visibility:hidden}.tab-content>.active{display:block;visibility:visible}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important;visibility:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:absolute;top:0;right:0;left:0;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/static/ldn/core-icons.css-d500848fee49.css b/config-tool/pkg/lib/editor/static/ldn/core-icons.css-d500848fee49.css new file mode 100644 index 000000000..41c7dfd34 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/ldn/core-icons.css-d500848fee49.css @@ -0,0 +1,168 @@ +/* Generated by grunt-webfont */ +/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */ + + +@font-face { + font-family:"core-icons"; + src:url("core-icons.eot?a86b92879b1f73264a64fbc6c6cab7e1"); + src:url("core-icons.eot?#iefix") format("embedded-opentype"), + url("core-icons.woff?a86b92879b1f73264a64fbc6c6cab7e1") format("woff"), + url("core-icons.ttf?a86b92879b1f73264a64fbc6c6cab7e1") format("truetype"), + url("core-icons.svg?a86b92879b1f73264a64fbc6c6cab7e1#core-icons") format("svg"); + font-weight:normal; + font-style:normal; +} + + +/* Bootstrap Overrides */ +[class^="ci-"]:before, +[class*=" ci-"]:before { + font-family:"core-icons"; + display:inline-block; + vertical-align:middle; + line-height:1; + font-weight:normal; + font-style:normal; + speak:none; + text-decoration:inherit; + text-transform:none; + text-rendering:auto; + -webkit-font-smoothing:antialiased; + -moz-osx-font-smoothing:grayscale; +} + + + +/* Icons */ + + +.ci-appcube:before { + content:"\f10e"; +} + + + +.ci-application:before { + content:"\f101"; +} + + + +.ci-cog:before { + content:"\f10a"; +} + + + +.ci-gitlab:before { + content:"\f102"; +} + + + +.ci-invite:before { + content:"\f103"; +} + + + +.ci-invoice:before { + content:"\f104"; +} + + + +.ci-k8s-logo:before { + content:"\f116"; +} + + + +.ci-layers:before { + content:"\f105"; +} + + + +.ci-package:before { + content:"\f10b"; +} + + + +.ci-robot:before { + content:"\f106"; +} + + + +.ci-shared-database:before { + content:"\f115"; +} + + + +.ci-shield-check-full:before { + content:"\f10f"; +} + + + +.ci-shield-check-outline:before { + content:"\f110"; +} + + + +.ci-shield-invalid-full:before { + content:"\f111"; +} + + + +.ci-shield-invalid-outline:before { + content:"\f112"; +} + + + +.ci-shield-lightning-full:before { + content:"\f113"; +} + + + +.ci-shield-none:before { + content:"\f114"; +} + + + +.ci-squashed:before { + content:"\f107"; +} + + + +.ci-stamp:before { + content:"\f108"; +} + + + +.ci-stop:before { + content:"\f109"; +} + + + +.ci-tpm-icon:before { + content:"\f10c"; +} + + + +.ci-undelete:before { + content:"\f10d"; +} + diff --git a/config-tool/pkg/lib/editor/static/ldn/core-icons.eot b/config-tool/pkg/lib/editor/static/ldn/core-icons.eot new file mode 100644 index 000000000..4dab2d306 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/ldn/core-icons.eot differ diff --git a/config-tool/pkg/lib/editor/static/ldn/core-icons.svg b/config-tool/pkg/lib/editor/static/ldn/core-icons.svg new file mode 100644 index 000000000..165fdbdbc --- /dev/null +++ b/config-tool/pkg/lib/editor/static/ldn/core-icons.svg @@ -0,0 +1,174 @@ + + + + +Created by FontForge 20170924 at Thu Aug 16 14:31:34 2018 + By root + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config-tool/pkg/lib/editor/static/ldn/core-icons.ttf b/config-tool/pkg/lib/editor/static/ldn/core-icons.ttf new file mode 100644 index 000000000..498cc58ab Binary files /dev/null and b/config-tool/pkg/lib/editor/static/ldn/core-icons.ttf differ diff --git a/config-tool/pkg/lib/editor/static/ldn/core-icons.woff b/config-tool/pkg/lib/editor/static/ldn/core-icons.woff new file mode 100644 index 000000000..90c37720a Binary files /dev/null and b/config-tool/pkg/lib/editor/static/ldn/core-icons.woff differ diff --git a/config-tool/pkg/lib/editor/static/ldn/cssfamilySourceSansPro300400700-30cd793a4b9b.css b/config-tool/pkg/lib/editor/static/ldn/cssfamilySourceSansPro300400700-30cd793a4b9b.css new file mode 100644 index 000000000..146ed5312 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/ldn/cssfamilySourceSansPro300400700-30cd793a4b9b.css @@ -0,0 +1,18 @@ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format('truetype'); +} +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 400; + src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format('truetype'); +} +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdr.ttf) format('truetype'); +} diff --git a/config-tool/pkg/lib/editor/static/ldn/font-awesome.css-a166e3c966c6.css b/config-tool/pkg/lib/editor/static/ldn/font-awesome.css-a166e3c966c6.css new file mode 100644 index 000000000..ee906a819 --- /dev/null +++ b/config-tool/pkg/lib/editor/static/ldn/font-awesome.css-a166e3c966c6.css @@ -0,0 +1,2337 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper-pp:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-resistance:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} +.fa-gitlab:before { + content: "\f296"; +} +.fa-wpbeginner:before { + content: "\f297"; +} +.fa-wpforms:before { + content: "\f298"; +} +.fa-envira:before { + content: "\f299"; +} +.fa-universal-access:before { + content: "\f29a"; +} +.fa-wheelchair-alt:before { + content: "\f29b"; +} +.fa-question-circle-o:before { + content: "\f29c"; +} +.fa-blind:before { + content: "\f29d"; +} +.fa-audio-description:before { + content: "\f29e"; +} +.fa-volume-control-phone:before { + content: "\f2a0"; +} +.fa-braille:before { + content: "\f2a1"; +} +.fa-assistive-listening-systems:before { + content: "\f2a2"; +} +.fa-asl-interpreting:before, +.fa-american-sign-language-interpreting:before { + content: "\f2a3"; +} +.fa-deafness:before, +.fa-hard-of-hearing:before, +.fa-deaf:before { + content: "\f2a4"; +} +.fa-glide:before { + content: "\f2a5"; +} +.fa-glide-g:before { + content: "\f2a6"; +} +.fa-signing:before, +.fa-sign-language:before { + content: "\f2a7"; +} +.fa-low-vision:before { + content: "\f2a8"; +} +.fa-viadeo:before { + content: "\f2a9"; +} +.fa-viadeo-square:before { + content: "\f2aa"; +} +.fa-snapchat:before { + content: "\f2ab"; +} +.fa-snapchat-ghost:before { + content: "\f2ac"; +} +.fa-snapchat-square:before { + content: "\f2ad"; +} +.fa-pied-piper:before { + content: "\f2ae"; +} +.fa-first-order:before { + content: "\f2b0"; +} +.fa-yoast:before { + content: "\f2b1"; +} +.fa-themeisle:before { + content: "\f2b2"; +} +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: "\f2b3"; +} +.fa-fa:before, +.fa-font-awesome:before { + content: "\f2b4"; +} +.fa-handshake-o:before { + content: "\f2b5"; +} +.fa-envelope-open:before { + content: "\f2b6"; +} +.fa-envelope-open-o:before { + content: "\f2b7"; +} +.fa-linode:before { + content: "\f2b8"; +} +.fa-address-book:before { + content: "\f2b9"; +} +.fa-address-book-o:before { + content: "\f2ba"; +} +.fa-vcard:before, +.fa-address-card:before { + content: "\f2bb"; +} +.fa-vcard-o:before, +.fa-address-card-o:before { + content: "\f2bc"; +} +.fa-user-circle:before { + content: "\f2bd"; +} +.fa-user-circle-o:before { + content: "\f2be"; +} +.fa-user-o:before { + content: "\f2c0"; +} +.fa-id-badge:before { + content: "\f2c1"; +} +.fa-drivers-license:before, +.fa-id-card:before { + content: "\f2c2"; +} +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: "\f2c3"; +} +.fa-quora:before { + content: "\f2c4"; +} +.fa-free-code-camp:before { + content: "\f2c5"; +} +.fa-telegram:before { + content: "\f2c6"; +} +.fa-thermometer-4:before, +.fa-thermometer:before, +.fa-thermometer-full:before { + content: "\f2c7"; +} +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before { + content: "\f2c8"; +} +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: "\f2c9"; +} +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: "\f2ca"; +} +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: "\f2cb"; +} +.fa-shower:before { + content: "\f2cc"; +} +.fa-bathtub:before, +.fa-s15:before, +.fa-bath:before { + content: "\f2cd"; +} +.fa-podcast:before { + content: "\f2ce"; +} +.fa-window-maximize:before { + content: "\f2d0"; +} +.fa-window-minimize:before { + content: "\f2d1"; +} +.fa-window-restore:before { + content: "\f2d2"; +} +.fa-times-rectangle:before, +.fa-window-close:before { + content: "\f2d3"; +} +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: "\f2d4"; +} +.fa-bandcamp:before { + content: "\f2d5"; +} +.fa-grav:before { + content: "\f2d6"; +} +.fa-etsy:before { + content: "\f2d7"; +} +.fa-imdb:before { + content: "\f2d8"; +} +.fa-ravelry:before { + content: "\f2d9"; +} +.fa-eercast:before { + content: "\f2da"; +} +.fa-microchip:before { + content: "\f2db"; +} +.fa-snowflake-o:before { + content: "\f2dc"; +} +.fa-superpowers:before { + content: "\f2dd"; +} +.fa-wpexplorer:before { + content: "\f2de"; +} +.fa-meetup:before { + content: "\f2e0"; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.ttf b/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.ttf new file mode 100644 index 000000000..c7bbd2c15 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.ttf differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.woff b/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.woff new file mode 100644 index 000000000..2836e7e43 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.woff differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.woff2 b/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.woff2 new file mode 100644 index 000000000..e0b0e5755 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-brands-400.woff2 differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.ttf b/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.ttf new file mode 100644 index 000000000..358a5721e Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.ttf differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.woff b/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.woff new file mode 100644 index 000000000..5c8161c26 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.woff differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.woff2 b/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.woff2 new file mode 100644 index 000000000..de7520b12 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-regular-400.woff2 differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.ttf b/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.ttf new file mode 100644 index 000000000..7de8c0280 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.ttf differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.woff b/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.woff new file mode 100644 index 000000000..461106370 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.woff differ diff --git a/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.woff2 b/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.woff2 new file mode 100644 index 000000000..d38acd303 Binary files /dev/null and b/config-tool/pkg/lib/editor/static/webfonts/fa-solid-900.woff2 differ diff --git a/config-tool/pkg/lib/editor/tsconfig.json b/config-tool/pkg/lib/editor/tsconfig.json new file mode 100644 index 000000000..dced1e78e --- /dev/null +++ b/config-tool/pkg/lib/editor/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "module": "esnext", + "moduleResolution": "node", + "outDir": "./build/", + "target": "es5", + "lib": [ + "es2017", + "dom" + ], + "experimentalDecorators": true, + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], + "include": [ + "./js/**/*.ts" + ] +} \ No newline at end of file diff --git a/config-tool/pkg/lib/editor/webpack.config.js b/config-tool/pkg/lib/editor/webpack.config.js new file mode 100644 index 000000000..06bbfc9a0 --- /dev/null +++ b/config-tool/pkg/lib/editor/webpack.config.js @@ -0,0 +1,68 @@ +const webpack = require('webpack'); +const path = require('path'); +const TerserPlugin = require('terser-webpack-plugin'); + +let config = { + entry: { + configapp: "./js/main.ts" + }, + output: { + path: path.resolve(__dirname, "static/build"), + filename: '[name]-quay-editor.bundle.js', + chunkFilename: '[name]-quay-editor.chunk.js' + }, + resolve: { + extensions: [".ts", ".js"], + modules: [ + "node_modules" + ] + }, + module: { + rules: [ + { + test: /\.ts$/, + use: ["ts-loader"], + exclude: /node_modules/ + }, + { + test: /\.css$/, + use: [ + "style-loader", + "css-loader?minimize=true", + ], + }, + { + test: /\.html$/, + use: [ + 'ngtemplate-loader?relativeTo=' + (path.resolve(__dirname)), + 'html-loader', + ] + }, + ] + }, + optimization: {}, + plugins: [ + // Replace references to global variables with associated modules + new webpack.ProvidePlugin({ + $: "jquery", + jQuery: "jquery" + }), + ], + devtool: "cheap-module-source-map", +}; + +/** + * Production settings + */ +if (process.env.NODE_ENV === 'production') { + config.optimization.minimizer = [ + new TerserPlugin({ + // Disable mangle to prevent AngularJS errors + terserOptions: {mangle: false}, + sourceMap: true, + }), + ]; + config.output.filename = '[name]-quay-editor.bundle.js'; +} + +module.exports = config; diff --git a/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings.go b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings.go new file mode 100644 index 000000000..6672682fc --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings.go @@ -0,0 +1,150 @@ +package accesssettings + +import ( + "errors" + + "github.com/creasty/defaults" + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// AccessSettingsFieldGroup represents the AccessSettingsFieldGroup config fields +type AccessSettingsFieldGroup struct { + AuthenticationType string `default:"Database" validate:"" json:"AUTHENTICATION_TYPE,omitempty" yaml:"AUTHENTICATION_TYPE,omitempty"` + FeatureAnonymousAccess bool `default:"true" validate:"" json:"FEATURE_ANONYMOUS_ACCESS" yaml:"FEATURE_ANONYMOUS_ACCESS"` + FeatureDirectLogin bool `default:"true" validate:"" json:"FEATURE_DIRECT_LOGIN" yaml:"FEATURE_DIRECT_LOGIN"` + FeatureGithubLogin bool `default:"false" validate:"" json:"FEATURE_GITHUB_LOGIN" yaml:"FEATURE_GITHUB_LOGIN"` + FeatureGoogleLogin bool `default:"false" validate:"" json:"FEATURE_GOOGLE_LOGIN" yaml:"FEATURE_GOOGLE_LOGIN"` + HasOIDCLogin bool `default:"false" validate:"" json:"-" yaml:"-"` + FeatureInviteOnlyUserCreation bool `default:"false" validate:"" json:"FEATURE_INVITE_ONLY_USER_CREATION" yaml:"FEATURE_INVITE_ONLY_USER_CREATION"` + FeaturePartialUserAutocomplete bool `default:"true" validate:"" json:"FEATURE_PARTIAL_USER_AUTOCOMPLETE" yaml:"FEATURE_PARTIAL_USER_AUTOCOMPLETE"` + FeatureUsernameConfirmation bool `default:"true" validate:"" json:"FEATURE_USERNAME_CONFIRMATION" yaml:"FEATURE_USERNAME_CONFIRMATION"` + FeatureUserCreation bool `default:"true" validate:"" json:"FEATURE_USER_CREATION" yaml:"FEATURE_USER_CREATION"` + FeatureUserLastAccessed bool `default:"true" validate:"" json:"FEATURE_USER_LAST_ACCESSED" yaml:"FEATURE_USER_LAST_ACCESSED"` + FeatureUserLogAccess bool `default:"false" validate:"" json:"FEATURE_USER_LOG_ACCESS" yaml:"FEATURE_USER_LOG_ACCESS"` + FeatureUserMetadata bool `default:"false" validate:"" json:"FEATURE_USER_METADATA" yaml:"FEATURE_USER_METADATA"` + FeatureUserRename bool `default:"false" validate:"" json:"FEATURE_USER_RENAME" yaml:"FEATURE_USER_RENAME"` + FreshLoginTimeout string `default:"10m" validate:"" json:"FRESH_LOGIN_TIMEOUT,omitempty" yaml:"FRESH_LOGIN_TIMEOUT,omitempty"` + UserRecoveryTokenLifetime string `default:"30m" validate:"" json:"USER_RECOVERY_TOKEN_LIFETIME,omitempty" yaml:"USER_RECOVERY_TOKEN_LIFETIME,omitempty"` + FeatureExtendedRepositoryNames bool `default:"true" validate:"" json:"FEATURE_EXTENDED_REPOSITORY_NAMES,omitempty" yaml:"FEATURE_EXTENDED_REPOSITORY_NAMES,omitempty"` + CreateRepositoryOnPushPublic bool `default:"false" validate:"" json:"CREATE_REPOSITORY_ON_PUSH_PUBLIC,omitempty" yaml:"CREATE_REPOSITORY_ON_PUSH_PUBLIC,omitempty"` + FeatureUserInitialize bool `default:"false" validate:"" json:"FEATURE_USER_INITIALIZE,omitempty" yaml:"FEATURE_USER_INITIALIZE,omitempty"` +} + +// NewAccessSettingsFieldGroup creates a new AccessSettingsFieldGroup +func NewAccessSettingsFieldGroup(fullConfig map[string]interface{}) (*AccessSettingsFieldGroup, error) { + newAccessSettingsFieldGroup := &AccessSettingsFieldGroup{} + defaults.Set(newAccessSettingsFieldGroup) + + if value, ok := fullConfig["AUTHENTICATION_TYPE"]; ok { + newAccessSettingsFieldGroup.AuthenticationType, ok = value.(string) + if !ok { + return newAccessSettingsFieldGroup, errors.New("AUTHENTICATION_TYPE must be of type string") + } + } + if value, ok := fullConfig["FEATURE_ANONYMOUS_ACCESS"]; ok { + newAccessSettingsFieldGroup.FeatureAnonymousAccess, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_ANONYMOUS_ACCESS must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_DIRECT_LOGIN"]; ok { + newAccessSettingsFieldGroup.FeatureDirectLogin, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_DIRECT_LOGIN must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_GITHUB_LOGIN"]; ok { + newAccessSettingsFieldGroup.FeatureGithubLogin, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_GITHUB_LOGIN must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_GOOGLE_LOGIN"]; ok { + newAccessSettingsFieldGroup.FeatureGoogleLogin, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_GOOGLE_LOGIN must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_INVITE_ONLY_USER_CREATION"]; ok { + newAccessSettingsFieldGroup.FeatureInviteOnlyUserCreation, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_INVITE_ONLY_USER_CREATION must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_PARTIAL_USER_AUTOCOMPLETE"]; ok { + newAccessSettingsFieldGroup.FeaturePartialUserAutocomplete, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_PARTIAL_USER_AUTOCOMPLETE must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USERNAME_CONFIRMATION"]; ok { + newAccessSettingsFieldGroup.FeatureUsernameConfirmation, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USERNAME_CONFIRMATION must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USER_CREATION"]; ok { + newAccessSettingsFieldGroup.FeatureUserCreation, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USER_CREATION must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USER_LAST_ACCESSED"]; ok { + newAccessSettingsFieldGroup.FeatureUserLastAccessed, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USER_LAST_ACCESSED must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USER_LOG_ACCESS"]; ok { + newAccessSettingsFieldGroup.FeatureUserLogAccess, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USER_LOG_ACCESS must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USER_METADATA"]; ok { + newAccessSettingsFieldGroup.FeatureUserMetadata, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USER_METADATA must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USER_RENAME"]; ok { + newAccessSettingsFieldGroup.FeatureUserRename, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USER_RENAME must be of type bool") + } + } + if value, ok := fullConfig["FRESH_LOGIN_TIMEOUT"]; ok { + newAccessSettingsFieldGroup.FreshLoginTimeout, ok = value.(string) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FRESH_LOGIN_TIMEOUT must be of type string") + } + } + if value, ok := fullConfig["USER_RECOVERY_TOKEN_LIFETIME"]; ok { + newAccessSettingsFieldGroup.UserRecoveryTokenLifetime, ok = value.(string) + if !ok { + return newAccessSettingsFieldGroup, errors.New("USER_RECOVERY_TOKEN_LIFETIME must be of type string") + } + } + if value, ok := fullConfig["FEATURE_EXTENDED_REPOSITORY_NAMES"]; ok { + newAccessSettingsFieldGroup.FeatureExtendedRepositoryNames, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_EXTENDED_REPOSITORY_NAMES must be of type bool") + } + } + if value, ok := fullConfig["CREATE_REPOSITORY_ON_PUSH_PUBLIC"]; ok { + newAccessSettingsFieldGroup.CreateRepositoryOnPushPublic, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("CREATE_REPOSITORY_ON_PUSH_PUBLIC must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_USER_INITIALIZE"]; ok { + newAccessSettingsFieldGroup.FeatureUserInitialize, ok = value.(bool) + if !ok { + return newAccessSettingsFieldGroup, errors.New("FEATURE_USER_INITIALIZE must be of type bool") + } + } + + newAccessSettingsFieldGroup.HasOIDCLogin = shared.HasOIDCProvider(fullConfig) + + return newAccessSettingsFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_fields.go b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_fields.go new file mode 100644 index 000000000..d6783931d --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_fields.go @@ -0,0 +1,6 @@ +package accesssettings + +// Fields returns a list of strings representing the fields in this field group +func (fg *AccessSettingsFieldGroup) Fields() []string { + return []string{"AUTHENTICATION_TYPE", "FEATURE_ANONYMOUS_ACCESS", "FEATURE_DIRECT_LOGIN", "FEATURE_GITHUB_LOGIN", "FEATURE_GOOGLE_LOGIN", "FEATURE_INVITE_ONLY_USER_CREATION", "FEATURE_PARTIAL_USER_AUTOCOMPLETE", "FEATURE_USERNAME_CONFIRMATION", "FEATURE_USER_CREATION", "FEATURE_USER_LAST_ACCESSED", "FEATURE_USER_LOG_ACCESS", "FEATURE_USER_METADATA", "FEATURE_USER_RENAME", "FRESH_LOGIN_TIMEOUT", "USER_RECOVERY_TOKEN_LIFETIME", "FEATURE_EXTENDED_REPOSITORY_NAMES", "CREATE_REPOSITORY_ON_PUSH_PUBLIC", "FEATURE_USER_INITIALIZE"} +} diff --git a/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_test.go b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_test.go new file mode 100644 index 000000000..a6c7d0552 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_test.go @@ -0,0 +1,68 @@ +package accesssettings + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateAccessSettings(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + // Valid + {name: "checkFieldsNotPresent", config: map[string]interface{}{}, want: "valid"}, + {name: "checkNoLoginFeature", config: map[string]interface{}{"FEATURE_DIRECT_LOGIN": false}, want: "invalid"}, + {name: "checkBadAuthType", config: map[string]interface{}{"AUTHENTICATION_TYPE": "notarealauthtype"}, want: "invalid"}, + {name: "checkGithubLogin", config: map[string]interface{}{"FEATURE_DIRECT_LOGIN": false, "FEATURE_GITHUB_LOGIN": true}, want: "valid"}, + {name: "checkGoogleLogin", config: map[string]interface{}{"FEATURE_DIRECT_LOGIN": false, "FEATURE_GOOGLE_LOGIN": true}, want: "valid"}, + {name: "checkOIDC", config: map[string]interface{}{"FEATURE_DIRECT_LOGIN": false, "FEATURE_GOOGLE_LOGIN": false, "AUTH0_LOGIN_CONFIG": map[string]interface{}{}}, want: "valid"}, + {name: "checkInviteUser0", config: map[string]interface{}{"FEATURE_USER_CREATION": true, "FEATURE_INVITE_ONLY_USER_CREATION": false}, want: "valid"}, + {name: "checkInviteUser1", config: map[string]interface{}{"FEATURE_USER_CREATION": true, "FEATURE_INVITE_ONLY_USER_CREATION": true}, want: "valid"}, + {name: "checkInviteUser2", config: map[string]interface{}{"FEATURE_INVITE_ONLY_USER_CREATION": true}, want: "valid"}, + {name: "checkInviteUser3", config: map[string]interface{}{"FEATURE_USER_CREATION": false, "FEATURE_INVITE_ONLY_USER_CREATION": true}, want: "invalid"}, + {name: "checkInviteUser4", config: map[string]interface{}{"FEATURE_USER_CREATION": false, "FEATURE_INVITE_ONLY_USER_CREATION": false}, want: "valid"}, + {name: "checkIncorrectPattern", config: map[string]interface{}{"FEATURE_USER_CREATION": false, "FEATURE_INVITE_ONLY_USER_CREATION": false, "USER_RECOVERY_TOKEN_LIFETIME": "badpattern"}, want: "invalid"}, + {name: "checkCorrectPattern", config: map[string]interface{}{"FEATURE_USER_CREATION": false, "FEATURE_INVITE_ONLY_USER_CREATION": false, "USER_RECOVERY_TOKEN_LIFETIME": "1m"}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewAccessSettingsFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_validator.go b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_validator.go new file mode 100644 index 000000000..9f4f6a3a1 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/accesssettings/accesssettings_validator.go @@ -0,0 +1,49 @@ +package accesssettings + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *AccessSettingsFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // Check that AuthType is one of + if ok, err := shared.ValidateIsOneOfString(fg.AuthenticationType, []string{"Database", "LDAP", "JWT", "Keystone", "OIDC", "AppToken"}, "AUTHENTICATION_TYPE", "AccessSettings"); !ok { + errors = append(errors, err) + } + + // If feature direct login is off, others must be on + if !fg.HasOIDCLogin { + if ok, err := shared.ValidateAtLeastOneOfBool([]bool{fg.FeatureDirectLogin, fg.FeatureGithubLogin, fg.FeatureGoogleLogin}, []string{"FEATURE_DIRECT_LOGIN", "FEATURE_GITHUB_LOGIN", "FEATURE_GOOGLE_LOGIN"}, "AccessSettings"); !ok { + errors = append(errors, err) + } + } + + // Make sure patterns are enforced if fields are present + if fg.FreshLoginTimeout != "" { + if ok, err := shared.ValidateTimePattern(fg.FreshLoginTimeout, "FRESH_LOGIN_TIMEOUT", "AccessSettings"); !ok { + errors = append(errors, err) + } + } + + if fg.UserRecoveryTokenLifetime != "" { + if ok, err := shared.ValidateTimePattern(fg.UserRecoveryTokenLifetime, "USER_RECOVERY_TOKEN_LIFETIME", "AccessSettings"); !ok { + errors = append(errors, err) + } + } + + // Invite only user creation requires user creaiton + if !fg.FeatureUserCreation && fg.FeatureInviteOnlyUserCreation { + + newError := shared.ValidationError{ + Tags: []string{"INVITE_USER_ONLY_CREATION", "FEATURE_USER_CREATION"}, + FieldGroup: "AccessSettings", + Message: "INVITE_USER_ONLY_CREATION requires FEATURE_USER_CREATION to be enabled", + } + errors = append(errors, newError) + } + + // Return errors + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving.go b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving.go new file mode 100644 index 000000000..71546bfc2 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving.go @@ -0,0 +1,62 @@ +package actionlogarchiving + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// ActionLogArchivingFieldGroup represents the ActionLogArchivingFieldGroup config fields +type ActionLogArchivingFieldGroup struct { + ActionLogArchiveLocation string `default:"" validate:"" json:"ACTION_LOG_ARCHIVE_LOCATION,omitempty" yaml:"ACTION_LOG_ARCHIVE_LOCATION,omitempty"` + ActionLogArchivePath string `default:"" validate:"" json:"ACTION_LOG_ARCHIVE_PATH,omitempty" yaml:"ACTION_LOG_ARCHIVE_PATH,omitempty"` + DistributedStorageConfig *DistributedStorageConfigStruct `default:"" validate:"" json:"DISTRIBUTED_STORAGE_CONFIG,omitempty" yaml:"DISTRIBUTED_STORAGE_CONFIG,omitempty"` + FeatureActionLogRotation bool `default:"false" validate:"" json:"FEATURE_ACTION_LOG_ROTATION" yaml:"FEATURE_ACTION_LOG_ROTATION"` +} + +// DistributedStorageConfigStruct represents the DistributedStorageConfig struct +type DistributedStorageConfigStruct map[string]interface{} + +// NewActionLogArchivingFieldGroup creates a new ActionLogArchivingFieldGroup +func NewActionLogArchivingFieldGroup(fullConfig map[string]interface{}) (*ActionLogArchivingFieldGroup, error) { + newActionLogArchivingFieldGroup := &ActionLogArchivingFieldGroup{} + defaults.Set(newActionLogArchivingFieldGroup) + + if value, ok := fullConfig["ACTION_LOG_ARCHIVE_LOCATION"]; ok { + newActionLogArchivingFieldGroup.ActionLogArchiveLocation, ok = value.(string) + if !ok { + return newActionLogArchivingFieldGroup, errors.New("ACTION_LOG_ARCHIVE_LOCATION must be of type string") + } + } + if value, ok := fullConfig["ACTION_LOG_ARCHIVE_PATH"]; ok { + newActionLogArchivingFieldGroup.ActionLogArchivePath, ok = value.(string) + if !ok { + return newActionLogArchivingFieldGroup, errors.New("ACTION_LOG_ARCHIVE_PATH must be of type string") + } + } + if value, ok := fullConfig["DISTRIBUTED_STORAGE_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newActionLogArchivingFieldGroup.DistributedStorageConfig, err = NewDistributedStorageConfigStruct(value) + if err != nil { + return newActionLogArchivingFieldGroup, err + } + } + if value, ok := fullConfig["FEATURE_ACTION_LOG_ROTATION"]; ok { + newActionLogArchivingFieldGroup.FeatureActionLogRotation, ok = value.(bool) + if !ok { + return newActionLogArchivingFieldGroup, errors.New("FEATURE_ACTION_LOG_ROTATION must be of type bool") + } + } + + return newActionLogArchivingFieldGroup, nil +} + +// NewDistributedStorageConfigStruct creates a new DistributedStorageConfigStruct +func NewDistributedStorageConfigStruct(fullConfig map[string]interface{}) (*DistributedStorageConfigStruct, error) { + newDistributedStorageConfigStruct := DistributedStorageConfigStruct{} + for key, value := range fullConfig { + newDistributedStorageConfigStruct[key] = value + } + return &newDistributedStorageConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_fields.go b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_fields.go new file mode 100644 index 000000000..74363fcd9 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_fields.go @@ -0,0 +1,6 @@ +package actionlogarchiving + +// Fields returns a list of strings representing the fields in this field group +func (fg *ActionLogArchivingFieldGroup) Fields() []string { + return []string{"ACTION_LOG_ARCHIVE_LOCATION", "ACTION_LOG_ARCHIVE_PATH", "DISTRIBUTED_STORAGE_CONFIG", "FEATURE_ACTION_LOG_ROTATION"} +} diff --git a/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_test.go b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_test.go new file mode 100644 index 000000000..75040d864 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_test.go @@ -0,0 +1,72 @@ +package actionlogarchiving + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateActionLogArchiving(t *testing.T) { + + distributedStorageConfig := map[string]interface{}{ + "validlocation": []interface{}{ + "LocalStorage", + map[string]interface{}{ + "storage_path": "/some/path", + }, + }, + } + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + // Valid + {name: "checkMissingArchivePath", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true}, want: "invalid"}, + {name: "checkEmptyArchivePath", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true, "ACTION_LOG_ARCHIVE_PATH": ""}, want: "invalid"}, + {name: "checkArchivePathNoLocation", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true, "ACTION_LOG_ARCHIVE_PATH": "some/path"}, want: "invalid"}, + {name: "checkArchivePathEmptyLocation", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true, "ACTION_LOG_ARCHIVE_PATH": "some/path", "ACTION_LOG_ARCHIVE_LOCATION": "", "DISTRIBUTED_STORAGE_CONFIG": distributedStorageConfig}, want: "invalid"}, + {name: "checkArchivePathInValidLocation", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true, "ACTION_LOG_ARCHIVE_PATH": "some/path", "ACTION_LOG_ARCHIVE_LOCATION": "invalidlocation", "DISTRIBUTED_STORAGE_CONFIG": distributedStorageConfig}, want: "invalid"}, + {name: "checkArchivePathNoDistConfig", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true, "ACTION_LOG_ARCHIVE_PATH": "some/path", "ACTION_LOG_ARCHIVE_LOCATION": "invalidlocation"}, want: "invalid"}, + {name: "checkArchivePathValidLocation", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": true, "ACTION_LOG_ARCHIVE_PATH": "some/path", "ACTION_LOG_ARCHIVE_LOCATION": "validlocation", "DISTRIBUTED_STORAGE_CONFIG": distributedStorageConfig}, want: "valid"}, + {name: "checkArchivePathOff", config: map[string]interface{}{"FEATURE_ACTION_LOG_ROTATION": false}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewActionLogArchivingFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_validator.go b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_validator.go new file mode 100644 index 000000000..427658698 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/actionlogarchiving/actionlogarchiving_validator.go @@ -0,0 +1,51 @@ +package actionlogarchiving + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *ActionLogArchivingFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // If feature action log rotation is off, no need to validate + if !fg.FeatureActionLogRotation { + return errors + } + + // Check path is present + if ok, err := shared.ValidateRequiredString(fg.ActionLogArchivePath, "ACTION_LOG_ARCHIVE_PATH", "ActionLogArchiving"); !ok { + errors = append(errors, err) + } + + // Check location is present + if ok, err := shared.ValidateRequiredString(fg.ActionLogArchiveLocation, "ACTION_LOG_ARCHIVE_LOCATION", "ActionLogArchiving"); !ok { + errors = append(errors, err) + } + + // Check config is present + if ok, err := shared.ValidateRequiredObject(fg.DistributedStorageConfig, "DISTRIBUTED_STORAGE_CONFIG", "ActionLogArchiving"); !ok { + errors = append(errors, err) + return errors + } + + validLocation := false + for location := range *fg.DistributedStorageConfig { + if fg.ActionLogArchiveLocation == location { + validLocation = true + break + } + } + if !validLocation { + newError := shared.ValidationError{ + Tags: []string{"ACTION_LOG_ARCHIVE_LOCATION", "DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: "ActionLogArchiving", + Message: "ACTION_LOG_ARCHIVE_LOCATION must be in DISTRIBUTED_STORAGE_CONFIG", + } + errors = append(errors, newError) + } + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication.go b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication.go new file mode 100644 index 000000000..58f9ade00 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication.go @@ -0,0 +1,41 @@ +package apptokenauthentication + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// AppTokenAuthenticationFieldGroup represents the AppTokenAuthenticationFieldGroup config fields +type AppTokenAuthenticationFieldGroup struct { + AuthenticationType string `default:"Database" validate:"" json:"AUTHENTICATION_TYPE,omitempty" yaml:"AUTHENTICATION_TYPE,omitempty"` + FeatureAppSpecificTokens bool `default:"true" validate:"" json:"FEATURE_APP_SPECIFIC_TOKENS" yaml:"FEATURE_APP_SPECIFIC_TOKENS"` + FeatureDirectLogin bool `default:"true" validate:"" json:"FEATURE_DIRECT_LOGIN" yaml:"FEATURE_DIRECT_LOGIN"` +} + +// NewAppTokenAuthenticationFieldGroup creates a new AppTokenAuthenticationFieldGroup +func NewAppTokenAuthenticationFieldGroup(fullConfig map[string]interface{}) (*AppTokenAuthenticationFieldGroup, error) { + newAppTokenAuthenticationFieldGroup := &AppTokenAuthenticationFieldGroup{} + defaults.Set(newAppTokenAuthenticationFieldGroup) + + if value, ok := fullConfig["AUTHENTICATION_TYPE"]; ok { + newAppTokenAuthenticationFieldGroup.AuthenticationType, ok = value.(string) + if !ok { + return newAppTokenAuthenticationFieldGroup, errors.New("AUTHENTICATION_TYPE must be of type string") + } + } + if value, ok := fullConfig["FEATURE_APP_SPECIFIC_TOKENS"]; ok { + newAppTokenAuthenticationFieldGroup.FeatureAppSpecificTokens, ok = value.(bool) + if !ok { + return newAppTokenAuthenticationFieldGroup, errors.New("FEATURE_APP_SPECIFIC_TOKENS must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_DIRECT_LOGIN"]; ok { + newAppTokenAuthenticationFieldGroup.FeatureDirectLogin, ok = value.(bool) + if !ok { + return newAppTokenAuthenticationFieldGroup, errors.New("FEATURE_DIRECT_LOGIN must be of type bool") + } + } + + return newAppTokenAuthenticationFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_fields.go b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_fields.go new file mode 100644 index 000000000..a9326617b --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_fields.go @@ -0,0 +1,6 @@ +package apptokenauthentication + +// Fields returns a list of strings representing the fields in this field group +func (fg *AppTokenAuthenticationFieldGroup) Fields() []string { + return []string{"AUTHENTICATION_TYPE", "FEATURE_APP_SPECIFIC_TOKENS", "FEATURE_DIRECT_LOGIN"} +} diff --git a/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_test.go b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_test.go new file mode 100644 index 000000000..18f2b6882 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_test.go @@ -0,0 +1,60 @@ +package apptokenauthentication + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateAppTokenAuthentication(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + // Valid + {name: "AuthenticationTypeOnMissingOtherValues", config: map[string]interface{}{"AUTHENTICATION_TYPE": "AppToken"}, want: "invalid"}, + {name: "AuthenticationFeatureOff", config: map[string]interface{}{"AUTHENTICATION_TYPE": "AppToken", "FEATURE_APP_SPECIFIC_TOKENS": false}, want: "invalid"}, + {name: "DirectLoginEnabled", config: map[string]interface{}{"AUTHENTICATION_TYPE": "AppToken", "FEATURE_APP_SPECIFIC_TOKENS": true, "FEATURE_DIRECT_LOGIN": true}, want: "invalid"}, + {name: "AuthenticationTypeOnFeatureOnDirectLoginOff", config: map[string]interface{}{"AUTHENTICATION_TYPE": "AppToken", "FEATURE_APP_SPECIFIC_TOKENS": true, "FEATURE_DIRECT_LOGIN": false}, want: "valid"}, + {name: "WrongAuthType", config: map[string]interface{}{"AUTHENTICATION_TYPE": "Database"}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewAppTokenAuthenticationFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_validator.go b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_validator.go new file mode 100644 index 000000000..10803a786 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/apptokenauthentication/apptokenauthentication_validator.go @@ -0,0 +1,40 @@ +package apptokenauthentication + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *AppTokenAuthenticationFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "AppTokenAuthentication" + + // Make empty errors + errors := []shared.ValidationError{} + + // If authentication type is disabled + if fg.AuthenticationType != "AppToken" { + return errors + } + + // Ensure app tokens are enabled + if !fg.FeatureAppSpecificTokens { + newError := shared.ValidationError{ + Tags: []string{"FEATURE_APP_SPECIFIC_TOKENS"}, + FieldGroup: fgName, + Message: "FEATURE_APP_SPECIFIC_TOKENS must be enabled if AUTHENTICATION_TYPE is AppToken", + } + errors = append(errors, newError) + } + + // Ensure direct login is disabled + if fg.FeatureDirectLogin { + newError := shared.ValidationError{ + Tags: []string{"FEATURE_DIRECT_LOGIN"}, + FieldGroup: fgName, + Message: "FEATURE_DIRECT_LOGIN must be disabled if AUTHENTICATION_TYPE is AppToken", + } + errors = append(errors, newError) + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger.go b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger.go new file mode 100644 index 000000000..09460ddb0 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger.go @@ -0,0 +1,70 @@ +package bitbucketbuildtrigger + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// BitbucketBuildTriggerFieldGroup represents the BitbucketBuildTriggerFieldGroup config fields +type BitbucketBuildTriggerFieldGroup struct { + BitbucketTriggerConfig *BitbucketTriggerConfigStruct `default:"" validate:"" json:"BITBUCKET_TRIGGER_CONFIG,omitempty" yaml:"BITBUCKET_TRIGGER_CONFIG,omitempty"` + FeatureBitbucketBuild bool `default:"false" validate:"" json:"FEATURE_BITBUCKET_BUILD" yaml:"FEATURE_BITBUCKET_BUILD"` + FeatureBuildSupport bool `default:"" validate:"" json:"FEATURE_BUILD_SUPPORT" yaml:"FEATURE_BUILD_SUPPORT"` +} + +// BitbucketTriggerConfigStruct represents the BitbucketTriggerConfigStruct config fields +type BitbucketTriggerConfigStruct struct { + ConsumerKey string `default:"" validate:"" json:"CONSUMER_KEY,omitempty" yaml:"CONSUMER_KEY,omitempty"` + ConsumerSecret string `default:"" validate:"" json:"CONSUMER_SECRET,omitempty" yaml:"CONSUMER_SECRET,omitempty"` +} + +// NewBitbucketBuildTriggerFieldGroup creates a new BitbucketBuildTriggerFieldGroup +func NewBitbucketBuildTriggerFieldGroup(fullConfig map[string]interface{}) (*BitbucketBuildTriggerFieldGroup, error) { + newBitbucketBuildTriggerFieldGroup := &BitbucketBuildTriggerFieldGroup{} + defaults.Set(newBitbucketBuildTriggerFieldGroup) + + if value, ok := fullConfig["BITBUCKET_TRIGGER_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newBitbucketBuildTriggerFieldGroup.BitbucketTriggerConfig, err = NewBitbucketTriggerConfigStruct(value) + if err != nil { + return newBitbucketBuildTriggerFieldGroup, err + } + } + if value, ok := fullConfig["FEATURE_BITBUCKET_BUILD"]; ok { + newBitbucketBuildTriggerFieldGroup.FeatureBitbucketBuild, ok = value.(bool) + if !ok { + return newBitbucketBuildTriggerFieldGroup, errors.New("FEATURE_BITBUCKET_BUILD must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_BUILD_SUPPORT"]; ok { + newBitbucketBuildTriggerFieldGroup.FeatureBuildSupport, ok = value.(bool) + if !ok { + return newBitbucketBuildTriggerFieldGroup, errors.New("FEATURE_BUILD_SUPPORT must be of type bool") + } + } + + return newBitbucketBuildTriggerFieldGroup, nil +} + +// NewBitbucketTriggerConfigStruct creates a new BitbucketTriggerConfigStruct +func NewBitbucketTriggerConfigStruct(fullConfig map[string]interface{}) (*BitbucketTriggerConfigStruct, error) { + newBitbucketTriggerConfigStruct := &BitbucketTriggerConfigStruct{} + defaults.Set(newBitbucketTriggerConfigStruct) + + if value, ok := fullConfig["CONSUMER_KEY"]; ok { + newBitbucketTriggerConfigStruct.ConsumerKey, ok = value.(string) + if !ok { + return newBitbucketTriggerConfigStruct, errors.New("CONSUMER_KEY must be of type string") + } + } + if value, ok := fullConfig["CONSUMER_SECRET"]; ok { + newBitbucketTriggerConfigStruct.ConsumerSecret, ok = value.(string) + if !ok { + return newBitbucketTriggerConfigStruct, errors.New("CONSUMER_SECRET must be of type string") + } + } + + return newBitbucketTriggerConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_fields.go b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_fields.go new file mode 100644 index 000000000..c4edb7ba8 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_fields.go @@ -0,0 +1,6 @@ +package bitbucketbuildtrigger + +// Fields returns a list of strings representing the fields in this field group +func (fg *BitbucketBuildTriggerFieldGroup) Fields() []string { + return []string{"BITBUCKET_TRIGGER_CONFIG", "FEATURE_BITBUCKET_BUILD", "FEATURE_BUILD_SUPPORT"} +} diff --git a/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_test.go b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_test.go new file mode 100644 index 000000000..76a7c5a36 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_test.go @@ -0,0 +1,64 @@ +package bitbucketbuildtrigger + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateBitbucketBuildTrigger(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "BuildSupportOff", config: map[string]interface{}{}, want: "valid"}, + {name: "BuildSupportOnBitbucketOff", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true}, want: "valid"}, + {name: "BuildSupportOnBitbucketOnMissingFields", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true}, want: "invalid"}, + {name: "BuildSupportOnBitbucketOnMissingConsumerKey", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true, "BITBUCKET_TRIGGER_CONFIG": map[string]interface{}{}}, want: "invalid"}, + {name: "BuildSupportOnBitbucketOnEmptyConsumerKey", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true, "BITBUCKET_TRIGGER_CONFIG": map[string]interface{}{"CONSUMER_KEY": ""}}, want: "invalid"}, + {name: "BuildSupportOnBitbucketOnMissingConsumerSecret", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true, "BITBUCKET_TRIGGER_CONFIG": map[string]interface{}{"CONSUMER_KEY": ""}}, want: "invalid"}, + {name: "BuildSupportOnBitbucketOnEmptyConsumerSecret", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true, "BITBUCKET_TRIGGER_CONFIG": map[string]interface{}{"CONSUMER_KEY": "", "CONSUMER_SECRET": ""}}, want: "invalid"}, + {name: "BuildSupportOnBitbucketOnInvalidConfig", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true, "BITBUCKET_TRIGGER_CONFIG": map[string]interface{}{"CONSUMER_KEY": "foo", "CONSUMER_SECRET": "bar"}}, want: "invalid"}, + {name: "BuildSupportOnBitbucketOnValidConfig", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_BITBUCKET_BUILD": true, "BITBUCKET_TRIGGER_CONFIG": map[string]interface{}{"CONSUMER_KEY": "gsjCjq84wHsm4sH4BH", "CONSUMER_SECRET": "dVr7BbNHaxVer4mbUVeegJusSYrk4e8J"}}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewBitbucketBuildTriggerFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_validator.go b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_validator.go new file mode 100644 index 000000000..9d81648b8 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/bitbucketbuildtrigger/bitbucketbuildtrigger_validator.go @@ -0,0 +1,69 @@ +package bitbucketbuildtrigger + +import ( + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *BitbucketBuildTriggerFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "BitbucketBuildTrigger" + + // Make empty errors + errors := []shared.ValidationError{} + + // If build suppport is off, dont validate + if !fg.FeatureBuildSupport { + return errors + } + + // If bitbucket build support is off, dont validate + if !fg.FeatureBitbucketBuild { + return errors + } + + // Make sure config is set up correctly + if fg.BitbucketTriggerConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"BITBUCKET_TRIGGER_CONFIG"}, + FieldGroup: fgName, + Message: "BITBUCKET_TRIGGER_CONFIG is required", + } + errors = append(errors, newError) + return errors + } + + // Check for consumer key + if fg.BitbucketTriggerConfig.ConsumerKey == "" { + newError := shared.ValidationError{ + Tags: []string{"BITBUCKET_TRIGGER_CONFIG.CONSUMER_KEY"}, + FieldGroup: fgName, + Message: "BITBUCKET_TRIGGER_CONFIG.CONSUMER_KEY is required", + } + errors = append(errors, newError) + } + + // Check consumer secret + if fg.BitbucketTriggerConfig.ConsumerSecret == "" { + newError := shared.ValidationError{ + Tags: []string{"BITBUCKET_TRIGGER_CONFIG.CONSUMER_SECRET"}, + FieldGroup: fgName, + Message: "BITBUCKET_TRIGGER_CONFIG.CONSUMER_SECRET is required", + } + errors = append(errors, newError) + } + + // Check OAuth credentials + if !shared.ValidateBitbucketOAuth(fg.BitbucketTriggerConfig.ConsumerKey, fg.BitbucketTriggerConfig.ConsumerSecret) { + newError := shared.ValidationError{ + Tags: []string{"BITBUCKET_TRIGGER_CONFIG.CONSUMER_ID", "BITBUCKET_TRIGGER_CONFIG.CONSUMER_SECRET"}, + FieldGroup: fgName, + Message: "Cannot validate BITBUCKET_TRIGGER_CONFIG credentials", + } + errors = append(errors, newError) + } + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager.go b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager.go new file mode 100644 index 000000000..21c717896 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager.go @@ -0,0 +1,131 @@ +package buildmanager + +import ( + "fmt" + + "github.com/creasty/defaults" + "gopkg.in/yaml.v3" +) + +// BuildManagerFieldGroup represents the BuildManager config fields +type BuildManagerFieldGroup struct { + FeatureBuildSupport bool `default:"" validate:"" json:"FEATURE_BUILD_SUPPORT" yaml:"FEATURE_BUILD_SUPPORT"` + BuildManagerConfig *BuildManagerDefinition `default:"" validate:"" json:"BUILD_MANAGER,omitempty" yaml:"BUILD_MANAGER,omitempty"` +} + +// BuildManagerDefinition represents a single storage configuration as a tuple (Name, Arguments) +type BuildManagerDefinition struct { + Name string `default:"" validate:"" json:",inline" yaml:",inline"` + Args *BuildManagerArgs `default:"" validate:"" json:",inline" yaml:",inline"` +} + +// BuildManagerArgs represents the arguments in the second value of a definition tuple +type BuildManagerArgs struct { + // Args for ephemeral + AllowedWorkerCount int `default:"" validate:"" json:"ALLOWED_WORKER_COUNT,omitempty" yaml:"ALLOWED_WORKER_COUNT,omitempty"` + OrchestratorPrefix string `default:"" validate:"" json:"ORCHESTRATOR_PREFIX,omitempty" yaml:"ORCHESTRATOR_PREFIX,omitempty"` + Orchestrator *OrchestratorArgs `default:"" validate:"" json:"ORCHESTRATOR,omitempty" yaml:"ORCHESTRATOR,omitempty"` + Executors []*ExecutorArgs `default:"" validate:"" json:"EXECUTORS,omitempty" yaml:"EXECUTORS,omitempty"` +} + +// OrchestratorArgs represents the arguments in the orchestrator object +type OrchestratorArgs struct { + RedisHost string `default:"" validate:"" json:"REDIS_HOST,omitempty" yaml:"REDIS_HOST,omitempty"` + RedisPassword string `default:"" validate:"" json:"REDIS_PASSWORD,omitempty" yaml:"REDIS_PASSWORD,omitempty"` + RedisSSL bool `default:"" validate:"" json:"REDIS_SSL" yaml:"REDIS_SSL"` + RedisSkipKeyspaceEventSetup bool `default:"" validate:"" json:"REDIS_SKIP_KEYSPACE_EVENT_SETUP" yaml:"REDIS_SKIP_KEYSPACE_EVENT_SETUP"` +} + +// ExecutorArgs represents the arguments in an executor object +type ExecutorArgs struct { + Executor string `default:"" validate:"" json:"EXECUTOR,omitempty" yaml:"EXECUTOR,omitempty"` + BuilderNamespace string `default:"" validate:"" json:"BUILDER_NAMESPACE,omitempty" yaml:"BUILDER_NAMESPACE,omitempty"` + K8sAPIServer string `default:"" validate:"" json:"K8S_API_SERVER,omitempty" yaml:"K8S_API_SERVER,omitempty"` + VolumeSize string `default:"" validate:"" json:"VOLUME_SIZE,omitempty" yaml:"VOLUME_SIZE,omitempty"` + KubernetesDistribution string `default:"" validate:"" json:"KUBERNETES_DISTRIBUTION,omitempty" yaml:"KUBERNETES_DISTRIBUTION,omitempty"` + ContainerMemoryLimits string `default:"" validate:"" json:"CONTAINER_MEMORY_LIMITS,omitempty" yaml:"CONTAINER_MEMORY_LIMITS,omitempty"` + ContainerCPULimits string `default:"" validate:"" json:"CONTAINER_CPU_LIMITS,omitempty" yaml:"CONTAINER_CPU_LIMITS,omitempty"` + ContainerMemoryRequest string `default:"" validate:"" json:"CONTAINER_MEMORY_REQUEST,omitempty" yaml:"CONTAINER_MEMORY_REQUEST,omitempty"` + ContainerCPURequest string `default:"" validate:"" json:"CONTAINER_CPU_REQUEST,omitempty" yaml:"CONTAINER_CPU_REQUEST,omitempty"` + NodeSelectorLabelKey string `default:"" validate:"" json:"NODE_SELECTOR_LABEL_KEY,omitempty" yaml:"NODE_SELECTOR_LABEL_KEY,omitempty"` + NodeSelectorLabelValue string `default:"" validate:"" json:"NODE_SELECTOR_LABEL_VALUE,omitempty" yaml:"NODE_SELECTOR_LABEL_VALUE,omitempty"` + ContainerRuntime string `default:"" validate:"" json:"CONTAINER_RUNTIME,omitempty" yaml:"CONTAINER_RUNTIME,omitempty"` + ServiceAccountName string `default:"" validate:"" json:"SERVICE_ACCOUNT_NAME,omitempty" yaml:"SERVICE_ACCOUNT_NAME,omitempty"` + ServiceAccountToken string `default:"" validate:"" json:"SERVICE_ACCOUNT_TOKEN,omitempty" yaml:"SERVICE_ACCOUNT_TOKEN,omitempty"` + QuayUsername string `default:"" validate:"" json:"QUAY_USERNAME,omitempty" yaml:"QUAY_USERNAME,omitempty"` + QuayPassword string `default:"" validate:"" json:"QUAY_PASSWORD,omitempty" yaml:"QUAY_PASSWORD,omitempty"` + WorkerImage string `default:"" validate:"" json:"WORKER_IMAGE,omitempty" yaml:"WORKER_IMAGE,omitempty"` + WorkerTag string `default:"" validate:"" json:"WORKER_TAG,omitempty" yaml:"WORKER_TAG,omitempty"` + BuilderVMContainerImage string `default:"" validate:"" json:"BUILDER_VM_CONTAINER_IMAGE,omitempty" yaml:"BUILDER_VM_CONTAINER_IMAGE,omitempty"` + SetupTime int `default:"" validate:"" json:"SETUP_TIME,omitempty" yaml:"SETUP_TIME,omitempty"` + MinimumRetryThreshold int `default:"" validate:"" json:"MINIMUM_RETRY_THRESHOLD" yaml:"MINIMUM_RETRY_THRESHOLD"` + SSHAuthorizedKeys []interface{} `default:"" validate:"" json:"SSH_AUTHORIZED_KEYS,omitempty" yaml:"SSH_AUTHORIZED_KEYS,omitempty"` + // ec2 fields + EC2Region string `default:"" validate:"" json:"EC2_REGION,omitempty" yaml:"EC2_REGION,omitempty"` + CoreOSAMI string `default:"" validate:"" json:"COREOS_AMI,omitempty" yaml:"COREOS_AMI,omitempty"` + AwsAccessKey string `default:"" validate:"" json:"AWS_ACCESS_KEY,omitempty" yaml:"AWS_ACCESS_KEY,omitempty"` + AwsSecretKey string `default:"" validate:"" json:"AWS_SECRET_KEY,omitempty" yaml:"AWS_SECRET_KEY,omitempty"` + EC2InstanceType string `default:"" validate:"" json:"EC2_INSTANCE_TYPE,omitempty" yaml:"EC2_INSTANCE_TYPE,omitempty"` + EC2VPCSubnetID string `default:"" validate:"" json:"EC2_VPC_SUBNET_ID,omitempty" yaml:"EC2_VPC_SUBNET_ID,omitempty"` + EC2SecurityGroupIDs []interface{} `default:"" validate:"" json:"EC2_SECURITY_GROUP_IDS,omitempty" yaml:"EC2_SECURITY_GROUP_IDS,omitempty"` + EC2KeyName string `default:"" validate:"" json:"EC2_KEY_NAME,omitempty" yaml:"EC2_KEY_NAME,omitempty"` + BlockDeviceSize int `default:"" validate:"" json:"BLOCK_DEVICE_SIZE,omitempty" yaml:"BLOCK_DEVICE_SIZE,omitempty"` +} + +// NewBuildManagerFieldGroup creates a new BitbucketBuildTriggerFieldGroup +func NewBuildManagerFieldGroup(fullConfig map[string]interface{}) (*BuildManagerFieldGroup, error) { + newBuildManagerFieldGroup := &BuildManagerFieldGroup{} + + bytes, err := yaml.Marshal(fullConfig) + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(bytes, newBuildManagerFieldGroup) + if err != nil { + return nil, err + } + + defaults.Set(newBuildManagerFieldGroup) + + return newBuildManagerFieldGroup, nil +} + +func (bm *BuildManagerDefinition) UnmarshalYAML(value *yaml.Node) error { + + // Ensure correct shape + if len(value.Content) != 2 || value.Content[0].Tag != "!!str" || value.Content[1].Tag != "!!map" { + return fmt.Errorf("Incorrect format for value BUILD_MANAGER") + } + + bm.Name = value.Content[0].Value + err := value.Content[1].Decode(&bm.Args) + if err != nil { + return err + } + + return nil + +} + +func (bm *BuildManagerDefinition) MarshalYAML() (interface{}, error) { + + name := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: bm.Name, + } + + args := &yaml.Node{} + err := args.Encode(bm.Args) + if err != nil { + return nil, err + } + + node := &yaml.Node{ + Kind: yaml.SequenceNode, + Content: []*yaml.Node{name, args}, + } + + return node, nil + +} diff --git a/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_fields.go b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_fields.go new file mode 100644 index 000000000..03e98fdff --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_fields.go @@ -0,0 +1,6 @@ +package buildmanager + +// Fields returns a list of strings representing the fields in this field group +func (fg *BuildManagerFieldGroup) Fields() []string { + return []string{"FEATURE_BUILD_SUPPORT", "BUILD_MANAGER"} +} diff --git a/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_test.go b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_test.go new file mode 100644 index 000000000..886d84083 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_test.go @@ -0,0 +1,128 @@ +package buildmanager + +import ( + "fmt" + "reflect" + "testing" + + "github.com/jojomi/go-spew/spew" + "gopkg.in/yaml.v3" +) + +func TestValidateBuildManager(t *testing.T) { + + before := []byte(` +FEATURE_BUILD_SUPPORT: true +BUILD_MANAGER: + - ephemeral + - ALLOWED_WORKER_COUNT: 1 + ORCHESTRATOR_PREFIX: buildman/production/ + ORCHESTRATOR: + REDIS_HOST: quay-redis-host + REDIS_PASSWORD: quay-redis-password + REDIS_SSL: true + REDIS_SKIP_KEYSPACE_EVENT_SETUP: false + EXECUTORS: + - EXECUTOR: kubernetes + BUILDER_NAMESPACE: builder + K8S_API_SERVER: api.openshift.somehost.org:6443 + VOLUME_SIZE: 8G + KUBERNETES_DISTRIBUTION: openshift + CONTAINER_MEMORY_LIMITS: 5120Mi + CONTAINER_CPU_LIMITS: 1000m + CONTAINER_MEMORY_REQUEST: 3968Mi + CONTAINER_CPU_REQUEST: 500m + NODE_SELECTOR_LABEL_KEY: beta.kubernetes.io/instance-type + NODE_SELECTOR_LABEL_VALUE: n1-standard-4 + CONTAINER_RUNTIME: podman + SERVICE_ACCOUNT_NAME: name + SERVICE_ACCOUNT_TOKEN: name + QUAY_USERNAME: brew-username + QUAY_PASSWORD: brew-password + WORKER_IMAGE: /quay-quay-builder + WORKER_TAG: some_tag + BUILDER_VM_CONTAINER_IMAGE: '/quay-quay-builder-qemu-rhcos:v3.4.0' + SETUP_TIME: 180 + MINIMUM_RETRY_THRESHOLD: 1 + SSH_AUTHORIZED_KEYS: + - ssh-rsa 12345 someuser@email.com + - ssh-rsa 67890 someuser2@email.com +`) + + var confMap BuildManagerFieldGroup + err := yaml.Unmarshal(before, &confMap) + if err != nil { + t.Errorf(err.Error()) + return + } + + after, err := yaml.Marshal(confMap) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Println(string(after)) + + var confMap2 BuildManagerFieldGroup + err = yaml.Unmarshal(after, &confMap2) + if err != nil { + t.Errorf("fail" + err.Error()) + return + } + + if !reflect.DeepEqual(confMap, confMap2) { + t.Errorf("unequal") + spew.Dump(confMap) + spew.Dump(confMap2) + return + } + + // // Define test data + // var tests = []struct { + // name string + // config map[string]interface{} + // want string + // }{{name: "my_test", + // config: confMap, + // want: "invalid"}, + // } + + // // Iterate through tests + // for _, tt := range tests { + + // // Run specific test + // t.Run(tt.name, func(t *testing.T) { + + // // Get validation result + // fg, err := NewBuildManagerFieldGroup(tt.config) + // if err != nil && tt.want != "typeError" { + // t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + // } + + // opts := shared.Options{ + // Mode: "testing", + // } + + // validationErrors := fg.Validate(opts) + + // // Get result type + // received := "" + // if len(validationErrors) == 0 { + // received = "valid" + // } else { + // received = "invalid" + // } + + // // Compare with expected + // if tt.want != received { + // t.Errorf("Expected %s. Received %s", tt.want, received) + // for _, err := range validationErrors { + // t.Errorf(err.Message) + // } + // } + + // }) + // } + +} diff --git a/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_validator.go b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_validator.go new file mode 100644 index 000000000..0a1f3408b --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/buildmanager/buildmanager_validator.go @@ -0,0 +1,19 @@ +package buildmanager + +import ( + + //mysql driver + _ "github.com/lib/pq" // postgres driver + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *BuildManagerFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/database/database.go b/config-tool/pkg/lib/fieldgroups/database/database.go new file mode 100644 index 000000000..0dee090e9 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/database/database.go @@ -0,0 +1,109 @@ +package database + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// DatabaseFieldGroup represents the DatabaseFieldGroup config fields +type DatabaseFieldGroup struct { + DbConnectionArgs *DbConnectionArgsStruct `default:"{}" json:"DB_CONNECTION_ARGS,omitempty" yaml:"DB_CONNECTION_ARGS,omitempty"` + DbUri string `default:"" validate:"" json:"DB_URI,omitempty" yaml:"DB_URI,omitempty"` +} + +// DbConnectionArgsStruct represents the DbConnectionArgsStruct config fields +type DbConnectionArgsStruct struct { + // MySQL arguments + Ssl *SslStruct `default:"" json:"ssl,omitempty" yaml:"ssl,omitempty"` + Threadlocals bool `default:"" json:"threadlocals,omitempty" yaml:"threadlocals,omitempty"` + Autorollback bool `default:"" json:"autorollback,omitempty" yaml:"autorollback,omitempty"` + + // Postgres arguments + SslRootCert string `default:"" json:"sslrootcert,omitempty" yaml:"sslrootcert,omitempty"` + SslMode string `default:"" json:"sslmode,omitempty" yaml:"sslmode,omitempty"` +} + +// SslStruct represents the SslStruct config fields +type SslStruct struct { + Ca string `default:"" validate:"" json:"ca,omitempty" yaml:"ca,omitempty"` +} + +// NewDatabaseFieldGroup creates a new DatabaseFieldGroup +func NewDatabaseFieldGroup(fullConfig map[string]interface{}) (*DatabaseFieldGroup, error) { + newDatabaseFieldGroup := &DatabaseFieldGroup{} + defaults.Set(newDatabaseFieldGroup) + + if value, ok := fullConfig["DB_CONNECTION_ARGS"]; ok { + var err error + value := value.(map[string]interface{}) + newDatabaseFieldGroup.DbConnectionArgs, err = NewDbConnectionArgsStruct(value) + if err != nil { + return newDatabaseFieldGroup, err + } + } + if value, ok := fullConfig["DB_URI"]; ok { + newDatabaseFieldGroup.DbUri, ok = value.(string) + if !ok { + return newDatabaseFieldGroup, errors.New("DB_URI must be of type string") + } + } + + return newDatabaseFieldGroup, nil +} + +// NewDbConnectionArgsStruct creates a new DbConnectionArgsStruct +func NewDbConnectionArgsStruct(fullConfig map[string]interface{}) (*DbConnectionArgsStruct, error) { + newDbConnectionArgsStruct := &DbConnectionArgsStruct{} + defaults.Set(newDbConnectionArgsStruct) + + if value, ok := fullConfig["ssl"]; ok { + var err error + value := value.(map[string]interface{}) + newDbConnectionArgsStruct.Ssl, err = NewSslStruct(value) + if err != nil { + return newDbConnectionArgsStruct, err + } + } + if value, ok := fullConfig["threadlocals"]; ok { + newDbConnectionArgsStruct.Threadlocals, ok = value.(bool) + if !ok { + return newDbConnectionArgsStruct, errors.New("threadlocals must be of type bool") + } + } + if value, ok := fullConfig["autorollback"]; ok { + newDbConnectionArgsStruct.Autorollback, ok = value.(bool) + if !ok { + return newDbConnectionArgsStruct, errors.New("autorollback must be of type bool") + } + } + if value, ok := fullConfig["sslmode"]; ok { + newDbConnectionArgsStruct.SslMode, ok = value.(string) + if !ok { + return newDbConnectionArgsStruct, errors.New("sslmode must be of type string") + } + } + if value, ok := fullConfig["sslrootcert"]; ok { + newDbConnectionArgsStruct.SslRootCert, ok = value.(string) + if !ok { + return newDbConnectionArgsStruct, errors.New("sslrootcert must be of type string") + } + } + + return newDbConnectionArgsStruct, nil +} + +// NewSslStruct creates a new SslStruct +func NewSslStruct(fullConfig map[string]interface{}) (*SslStruct, error) { + newSslStruct := &SslStruct{} + defaults.Set(newSslStruct) + + if value, ok := fullConfig["ca"]; ok { + newSslStruct.Ca, ok = value.(string) + if !ok { + return newSslStruct, errors.New("ca must be of type string") + } + } + + return newSslStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/database/database_fields.go b/config-tool/pkg/lib/fieldgroups/database/database_fields.go new file mode 100644 index 000000000..4692d439a --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/database/database_fields.go @@ -0,0 +1,6 @@ +package database + +// Fields returns a list of strings representing the fields in this field group +func (fg *DatabaseFieldGroup) Fields() []string { + return []string{"DB_CONNECTION_ARGS", "DB_URI"} +} diff --git a/config-tool/pkg/lib/fieldgroups/database/database_test.go b/config-tool/pkg/lib/fieldgroups/database/database_test.go new file mode 100644 index 000000000..6157842e4 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/database/database_test.go @@ -0,0 +1,66 @@ +package database + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateDatabase(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "dbURIMissing", config: map[string]interface{}{}, want: "invalid"}, + {name: "dbURIMissing", config: map[string]interface{}{"DB_URI": ""}, want: "invalid"}, + {name: "dbURIInvalid", config: map[string]interface{}{"DB_URI": "not a url"}, want: "invalid"}, + {name: "dbURIPostgres", config: map[string]interface{}{"DB_URI": "postgresql://user:password@postgres:5432/quay"}, want: "invalid"}, + + // Quay currently requires the pymysql library to be specified in the DB_URI + {name: "ValidMysqlURI", config: map[string]interface{}{"DB_URI": "mysql+pymysql://root:password@mysql:3306/quay"}, want: "valid"}, + {name: "MissingPyMysql", config: map[string]interface{}{"DB_URI": "mysql://root:password@mysql:3306/quay"}, want: "invalid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewDatabaseFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + for _, err := range validationErrors { + t.Errorf(err.Message) + } + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/database/database_validator.go b/config-tool/pkg/lib/fieldgroups/database/database_validator.go new file mode 100644 index 000000000..9a0bf30ee --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/database/database_validator.go @@ -0,0 +1,81 @@ +package database + +import ( + "io/ioutil" + "os" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *DatabaseFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "Database" + + // Make empty errors + errors := []shared.ValidationError{} + + // Check field is not empty + if fg.DbUri == "" { + newError := shared.ValidationError{ + Tags: []string{"DB_URI"}, + FieldGroup: fgName, + Message: "DB_URI is required.", + } + errors = append(errors, newError) + return errors + } + + ca := "" + if fg.DbConnectionArgs.Ssl != nil { + ca = fg.DbConnectionArgs.Ssl.Ca + } + + sslrootcertTmpPath := fg.DbConnectionArgs.SslRootCert + if fg.DbConnectionArgs.SslMode == "verify-full" || fg.DbConnectionArgs.SslMode == "verify-ca" { + // Write database.pem needed for db validation, if any, to a temp file + tmpCert, err := ioutil.TempFile("/tmp", "database.*.pem") + if err != nil { + newError := shared.ValidationError{ + Tags: []string{"DB_URI"}, + FieldGroup: fgName, + Message: "Could write database certificate to temporary file. Error: " + err.Error(), + } + errors = append(errors, newError) + return errors + } + + defer func() { + tmpCert.Close() + os.Remove(tmpCert.Name()) + }() + + if _, err := tmpCert.Write(opts.Certificates["database.pem"]); err != nil { + newError := shared.ValidationError{ + Tags: []string{"DB_URI"}, + FieldGroup: fgName, + Message: "Could write database certificate to temporary file. Error: " + err.Error(), + } + errors = append(errors, newError) + return errors + } + + sslrootcertTmpPath = tmpCert.Name() + } + + // Connect to database + err := shared.ValidateDatabaseConnection(opts, fg.DbUri, ca, fg.DbConnectionArgs.Threadlocals, fg.DbConnectionArgs.Autorollback, fg.DbConnectionArgs.SslMode, sslrootcertTmpPath, fgName) + if err != nil { + newError := shared.ValidationError{ + Tags: []string{"DB_URI"}, + FieldGroup: fgName, + Message: "Could not connect to database. Error: " + err.Error(), + } + errors = append(errors, newError) + return errors + } + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage.go b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage.go new file mode 100644 index 000000000..57626f978 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage.go @@ -0,0 +1,378 @@ +package distributedstorage + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + + "github.com/creasty/defaults" + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// DistributedStorageFieldGroup represents the DistributedStorageFieldGroup config fields +type DistributedStorageFieldGroup struct { + DistributedStorageConfig map[string]*DistributedStorageDefinition `default:"{}" validate:"" json:"DISTRIBUTED_STORAGE_CONFIG,omitempty" yaml:"DISTRIBUTED_STORAGE_CONFIG,omitempty"` + DistributedStoragePreference []string `default:"[]" validate:"" json:"DISTRIBUTED_STORAGE_PREFERENCE,omitempty" yaml:"DISTRIBUTED_STORAGE_PREFERENCE,omitempty"` + DistributedStorageDefaultLocations []string `default:"[]" validate:"" json:"DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS,omitempty" yaml:"DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS,omitempty"` + FeatureStorageReplication bool `default:"false" validate:"" json:"FEATURE_STORAGE_REPLICATION" yaml:"FEATURE_STORAGE_REPLICATION"` + FeatureProxyStorage bool `default:"false" validate:"" json:"FEATURE_PROXY_STORAGE" yaml:"FEATURE_PROXY_STORAGE"` +} + +// DistributedStorageDefinition represents a single storage configuration as a tuple (Name, Arguments) +type DistributedStorageDefinition struct { + Name string `default:"" validate:"" json:",inline" yaml:",inline"` + Args *shared.DistributedStorageArgs `default:"" validate:"" json:",inline" yaml:",inline"` +} + +// NewDistributedStorageFieldGroup creates a new DistributedStorageFieldGroup +func NewDistributedStorageFieldGroup(fullConfig map[string]interface{}) (*DistributedStorageFieldGroup, error) { + newDistributedStorageFieldGroup := &DistributedStorageFieldGroup{} + defaults.Set(newDistributedStorageFieldGroup) + + if value, ok := fullConfig["FEATURE_STORAGE_REPLICATION"]; ok { + newDistributedStorageFieldGroup.FeatureStorageReplication, ok = value.(bool) + if !ok { + return newDistributedStorageFieldGroup, errors.New("FEATURE_STORAGE_REPLICATION must be of type boolean") + } + } + + if value, ok := fullConfig["DISTRIBUTED_STORAGE_PREFERENCE"]; ok { + for _, element := range value.([]interface{}) { + strElement, ok := element.(string) + if !ok { + return newDistributedStorageFieldGroup, errors.New("DISTRIBUTED_STORAGE_PREFERENCE must be of type []string") + } + + newDistributedStorageFieldGroup.DistributedStoragePreference = append( + newDistributedStorageFieldGroup.DistributedStoragePreference, + strElement, + ) + } + } + + if value, ok := fullConfig["DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS"]; ok { + for _, element := range value.([]interface{}) { + strElement, ok := element.(string) + if !ok { + return newDistributedStorageFieldGroup, errors.New("DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS must be of type []string") + } + + newDistributedStorageFieldGroup.DistributedStorageDefaultLocations = append( + newDistributedStorageFieldGroup.DistributedStorageDefaultLocations, + strElement, + ) + } + } + + if value, ok := fullConfig["DISTRIBUTED_STORAGE_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + for k, v := range value { + if _, ok := v.([]interface{}); !ok { + return newDistributedStorageFieldGroup, errors.New("DISTRIBUTED_STORAGE_CONFIG object values must be of form (Name, Args)") + } + + newDistributedStorageFieldGroup.DistributedStorageConfig[k], err = NewDistributedStorageDefinition(v.([]interface{})) + if err != nil { + return newDistributedStorageFieldGroup, err + } + } + + } + + return newDistributedStorageFieldGroup, nil +} + +// NewDistributedStorageDefinition creates a new DistributedStorageDefinition +func NewDistributedStorageDefinition(storageDef []interface{}) (*DistributedStorageDefinition, error) { + newDistributedStorageDefinition := &DistributedStorageDefinition{} + defaults.Set(newDistributedStorageDefinition) + + var ok bool + var err error + newDistributedStorageDefinition.Name, ok = storageDef[0].(string) + if !ok { + return newDistributedStorageDefinition, errors.New("First index of value in DISTRIBUTED_STORAGE_CONFIG must be a string") + } + + storageArgs, ok := storageDef[1].(map[string]interface{}) + if !ok { + return newDistributedStorageDefinition, errors.New("Second index of value in DISTRIBUTED_STORAGE_CONFIG must be an object") + } + + newDistributedStorageDefinition.Args, err = NewDistributedStorageArgs(storageArgs) + if err != nil { + return newDistributedStorageDefinition, err + } + + return newDistributedStorageDefinition, nil +} + +// NewDistributedStorageArgs creates a new DistributedStorageArgs type +func NewDistributedStorageArgs(storageArgs map[string]interface{}) (*shared.DistributedStorageArgs, error) { + newDistributedStorageArgs := &shared.DistributedStorageArgs{} + defaults.Set(newDistributedStorageArgs) + + if value, ok := storageArgs["access_key"]; ok { + newDistributedStorageArgs.AccessKey, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("access_key must be of type string") + } + } + + if value, ok := storageArgs["bucket_name"]; ok { + newDistributedStorageArgs.BucketName, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("bucket_name must be of type string") + } + } + + if value, ok := storageArgs["hostname"]; ok { + newDistributedStorageArgs.Hostname, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("hostname must be of type string") + } + } + + if value, ok := storageArgs["is_secure"]; ok { + newDistributedStorageArgs.IsSecure, ok = value.(bool) + if !ok { + return newDistributedStorageArgs, errors.New("is_secure must be of type boolean") + } + } + + if value, ok := storageArgs["port"]; ok { + + switch t := value.(type) { + case int: + newDistributedStorageArgs.Port = t + case string: + if len(t) == 0 { + newDistributedStorageArgs.Port = 0 + } else { + v, err := strconv.Atoi(t) + if err != nil { + return newDistributedStorageArgs, errors.New("port must be of type integer") + } + newDistributedStorageArgs.Port = v + } + case float64: + newDistributedStorageArgs.Port = int(t) + default: + return newDistributedStorageArgs, errors.New("port must be of type integer") + } + + } + + if value, ok := storageArgs["secret_key"]; ok { + newDistributedStorageArgs.SecretKey, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("secret_key must be of type string") + } + } + + if value, ok := storageArgs["s3_secret_key"]; ok { + newDistributedStorageArgs.S3SecretKey, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("s3_secret_key must be of type string") + } + } + + if value, ok := storageArgs["s3_access_key"]; ok { + newDistributedStorageArgs.S3AccessKey, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("s3_access_key must be of type string") + } + } + + if value, ok := storageArgs["host"]; ok { + newDistributedStorageArgs.Host, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("host must be of type string") + } + } + + if value, ok := storageArgs["s3_bucket"]; ok { + newDistributedStorageArgs.S3Bucket, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("s3_bucket must be of type string") + } + } + + if value, ok := storageArgs["azure_container"]; ok { + newDistributedStorageArgs.AzureContainer, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("azure_container must be of type string") + } + } + + if value, ok := storageArgs["azure_account_name"]; ok { + newDistributedStorageArgs.AzureAccountName, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("azure_container must be of type string") + } + } + + if value, ok := storageArgs["azure_account_key"]; ok { + newDistributedStorageArgs.AzureAccountKey, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("azure_account_key must be of type string") + } + } + + if value, ok := storageArgs["sas_token"]; ok { + newDistributedStorageArgs.SASToken, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("sas_token must be of type string") + } + } + + if value, ok := storageArgs["endpoint_url"]; ok { + newDistributedStorageArgs.EndpointURL, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("endpoint_url must be of type string") + } + } + + if value, ok := storageArgs["storage_path"]; ok { + newDistributedStorageArgs.StoragePath, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("storage_path must be of type string") + } + } + + if value, ok := storageArgs["cloudfront_distribution_domain"]; ok { + newDistributedStorageArgs.CloudfrontDistributionDomain, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("cloudfront_distribution_domain must be of type string") + } + } + + if value, ok := storageArgs["cloudfront_key_id"]; ok { + newDistributedStorageArgs.CloudfrontKeyID, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("cloudfront_key_id must be of type string") + } + } + + // CloudFlare provider + if value, ok := storageArgs["cloudflare_domain"]; ok { + newDistributedStorageArgs.CloudflareDomain, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("cloudflare_domain must be of type string") + } + } + + // Multi CDN provider + if value, ok := storageArgs["storage_config"]; ok { + newDistributedStorageArgs.StorageConfig, ok = value.(map[string]interface{}) + if !ok { + return newDistributedStorageArgs, errors.New("storage_config must be of type map[string]string") + } + } + + if value, ok := storageArgs["providers"]; ok { + newDistributedStorageArgs.Providers, ok = value.(map[string]interface{}) + if !ok { + return newDistributedStorageArgs, errors.New("providers must be of type map[string]string") + } + } + + if value, ok := storageArgs["default_provider"]; ok { + newDistributedStorageArgs.DefaultProvider, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("default_provider must be of type string") + } + } + + if value, ok := storageArgs["auth_version"]; ok { + + switch t := value.(type) { + case int: + newDistributedStorageArgs.SwiftAuthVersion = t + case string: + if len(t) == 0 { + newDistributedStorageArgs.SwiftAuthVersion = 0 + } else { + v, err := strconv.Atoi(t) + if err != nil { + return newDistributedStorageArgs, errors.New("auth_version must be of type integer") + } + newDistributedStorageArgs.SwiftAuthVersion = v + } + case float64: + newDistributedStorageArgs.SwiftAuthVersion = int(t) + default: + return newDistributedStorageArgs, errors.New("auth_version must be of type integer") + } + + } + + if value, ok := storageArgs["auth_url"]; ok { + newDistributedStorageArgs.SwiftAuthURL, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("auth_url must be of type string") + } + } + + if value, ok := storageArgs["swift_container"]; ok { + newDistributedStorageArgs.SwiftContainer, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("swift_container must be of type string") + } + } + + if value, ok := storageArgs["swift_user"]; ok { + newDistributedStorageArgs.SwiftUser, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("swift_user must be of type string") + } + } + + if value, ok := storageArgs["swift_password"]; ok { + newDistributedStorageArgs.SwiftPassword, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("swift_password must be of type string") + } + } + + if value, ok := storageArgs["ca_cert_path"]; ok { + newDistributedStorageArgs.SwiftCaCertPath, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("ca_cert_path must be of type string") + } + } + + if value, ok := storageArgs["temp_url_key"]; ok { + newDistributedStorageArgs.SwiftTempURLKey, ok = value.(string) + if !ok { + return newDistributedStorageArgs, errors.New("temp_url_key must be of type string") + } + } + + if value, ok := storageArgs["os_options"]; ok { + newDistributedStorageArgs.SwiftOsOptions, ok = value.(map[string]interface{}) + if !ok { + return newDistributedStorageArgs, errors.New("os_options must be an object") + } + } + + return newDistributedStorageArgs, nil +} + +func (ds DistributedStorageDefinition) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&ds.Name, &ds.Args} + wantLen := len(tmp) + if err := json.Unmarshal(buf, &tmp); err != nil { + return err + } + if g, e := len(tmp), wantLen; g != e { + return fmt.Errorf("wrong number of fields in DistributedStorage: %d != %d", g, e) + } + return nil +} + +func (ds DistributedStorageDefinition) MarshalJSON() ([]byte, error) { + return json.Marshal([]interface{}{&ds.Name, &ds.Args}) +} diff --git a/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_fields.go b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_fields.go new file mode 100644 index 000000000..c06bcecc8 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_fields.go @@ -0,0 +1,6 @@ +package distributedstorage + +// Fields returns a list of strings representing the fields in this field group +func (fg *DistributedStorageFieldGroup) Fields() []string { + return []string{"DISTRIBUTED_STORAGE_CONFIG", "DISTRIBUTED_STORAGE_PREFERENCE", "DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS", "FEATURE_STORAGE_REPLICATION"} +} diff --git a/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_test.go b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_test.go new file mode 100644 index 000000000..e26dafded --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_test.go @@ -0,0 +1,78 @@ +package distributedstorage + +import ( + "fmt" + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" + "gopkg.in/yaml.v3" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateDistributedStorage(t *testing.T) { + + var config = []byte(`DISTRIBUTED_STORAGE_CONFIG: + local_us: + - RadosGWStorage + - access_key: X + bucket_name: quay-datastore + hostname: jonathan-registry.com + is_secure: true + port: 443 + secret_key: X + storage_path: /datastorage/registry`) + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "MissingStorageConfig", config: map[string]interface{}{}, want: "invalid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Load config into struct + var conf map[string]interface{} + if err := yaml.Unmarshal(config, &conf); err != nil { + fmt.Println(err.Error()) + } + + // Get validation result + fg, err := NewDistributedStorageFieldGroup(conf) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + for _, err := range validationErrors { + t.Errorf(err.Message) + } + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_validator.go b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_validator.go new file mode 100644 index 000000000..1e1d1c171 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/distributedstorage/distributedstorage_validator.go @@ -0,0 +1,51 @@ +package distributedstorage + +import ( + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *DistributedStorageFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "DistributedStorage" + + // Make empty errors + errors := []shared.ValidationError{} + + if ok, err := shared.ValidateRequiredObject(fg.DistributedStorageConfig, "DISTRIBUTED_STORAGE_CONFIG", "DistributedStorage"); !ok { + errors = append(errors, err) + return errors + } + + // If no storage locations + if len(fg.DistributedStorageConfig) == 0 { + newError := shared.ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: "DISTRIBUTED_STORAGE_CONFIG must contain at least one storage location.", + } + errors = append(errors, newError) + return errors + } + + for storageName, storageConf := range fg.DistributedStorageConfig { + + if storageConf.Name == "LocalStorage" && fg.FeatureStorageReplication { + newError := shared.ValidationError{ + Tags: []string{"FEATURE_STORAGE_REPLICATION"}, + FieldGroup: fgName, + Message: "FEATURE_STORAGE_REPLICATION is not supported by LocalStorage.", + } + errors = append(errors, newError) + } + + if ok, errs := shared.ValidateStorage(opts, storageName, storageConf.Name, storageConf.Args, "DistributedStorage"); !ok { + errors = append(errors, errs...) + } + + } + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch.go b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch.go new file mode 100644 index 000000000..89df3d906 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch.go @@ -0,0 +1,269 @@ +package elasticsearch + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// ElasticSearchFieldGroup represents the ElasticSearchFieldGroup config fields +type ElasticSearchFieldGroup struct { + LogsModel string `default:"database" validate:"" json:"LOGS_MODEL,omitempty" yaml:"LOGS_MODEL,omitempty"` + LogsModelConfig *LogsModelConfigStruct `default:"" validate:"" json:"LOGS_MODEL_CONFIG,omitempty" yaml:"LOGS_MODEL_CONFIG,omitempty"` +} + +// LogsModelConfigStruct represents the LogsModelConfigStruct config fields +type LogsModelConfigStruct struct { + KafkaConfig *KafkaConfigStruct `default:"" validate:"" json:"kafka_config,omitempty" yaml:"kafka_config,omitempty"` + ElasticsearchConfig *ElasticsearchConfigStruct `default:"" validate:"" json:"elasticsearch_config,omitempty" yaml:"elasticsearch_config,omitempty"` + KinesisStreamConfig *KinesisStreamConfigStruct `default:"" validate:"" json:"kinesis_stream_config,omitempty" yaml:"kinesis_stream_config,omitempty"` + Producer string `default:"" validate:"" json:"producer,omitempty" yaml:"producer,omitempty"` +} + +// KinesisStreamConfigStruct represents the KinesisStreamConfigStruct config fields +type KinesisStreamConfigStruct struct { + Retries int `default:"" validate:"" json:"retries,omitempty" yaml:"retries,omitempty"` + ReadTimeout int `default:"" validate:"" json:"read_timeout,omitempty" yaml:"read_timeout,omitempty"` + MaxPoolConnections int `default:"" validate:"" json:"max_pool_connections,omitempty" yaml:"max_pool_connections,omitempty"` + AwsRegion string `default:"" validate:"" json:"aws_region,omitempty" yaml:"aws_region,omitempty"` + ConnectTimeout int `default:"" validate:"" json:"connect_timeout,omitempty" yaml:"connect_timeout,omitempty"` + AwsSecretKey string `default:"" validate:"" json:"aws_secret_key,omitempty" yaml:"aws_secret_key,omitempty"` + StreamName string `default:"" validate:"" json:"stream_name,omitempty" yaml:"stream_name,omitempty"` + AwsAccessKey string `default:"" validate:"" json:"aws_access_key,omitempty" yaml:"aws_access_key,omitempty"` +} + +// ElasticsearchConfigStruct represents the ElasticsearchConfigStruct config fields +type ElasticsearchConfigStruct struct { + AwsRegion string `default:"" validate:"" json:"aws_region,omitempty" yaml:"aws_region,omitempty"` + Port int `default:"" validate:"" json:"port,omitempty" yaml:"port,omitempty"` + AccessKey string `default:"" validate:"" json:"access_key,omitempty" yaml:"access_key,omitempty"` + Host string `default:"" validate:"" json:"host,omitempty" yaml:"host,omitempty"` + IndexPrefix string `default:"logentry_" validate:"" json:"index_prefix,omitempty" yaml:"index_prefix,omitempty"` + IndexSettings *IndexSettingsStruct `default:"" validate:"" json:"index_settings,omitempty" yaml:"index_settings,omitempty"` + UseSsl bool `default:"true" validate:"" json:"use_ssl" yaml:"use_ssl"` + SecretKey string `default:"" validate:"" json:"secret_key,omitempty" yaml:"secret_key,omitempty"` +} + +// IndexSettingsStruct represents the IndexSettings struct +type IndexSettingsStruct map[string]interface{} + +// KafkaConfigStruct represents the KafkaConfigStruct config fields +type KafkaConfigStruct struct { + Topic string `default:"" validate:"" json:"topic,omitempty" yaml:"topic,omitempty"` + BootstrapServers []interface{} `default:"" validate:"" json:"bootstrap_servers,omitempty" yaml:"bootstrap_servers,omitempty"` + MaxBlockSeconds int `default:"" validate:"" json:"max_block_seconds,omitempty" yaml:"max_block_seconds,omitempty"` +} + +// NewElasticSearchFieldGroup creates a new ElasticSearchFieldGroup +func NewElasticSearchFieldGroup(fullConfig map[string]interface{}) (*ElasticSearchFieldGroup, error) { + newElasticSearchFieldGroup := &ElasticSearchFieldGroup{} + defaults.Set(newElasticSearchFieldGroup) + + if value, ok := fullConfig["LOGS_MODEL"]; ok { + newElasticSearchFieldGroup.LogsModel, ok = value.(string) + if !ok { + return newElasticSearchFieldGroup, errors.New("LOGS_MODEL must be of type string") + } + } + if value, ok := fullConfig["LOGS_MODEL_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newElasticSearchFieldGroup.LogsModelConfig, err = NewLogsModelConfigStruct(value) + if err != nil { + return newElasticSearchFieldGroup, err + } + } + + return newElasticSearchFieldGroup, nil +} + +// NewLogsModelConfigStruct creates a new LogsModelConfigStruct +func NewLogsModelConfigStruct(fullConfig map[string]interface{}) (*LogsModelConfigStruct, error) { + newLogsModelConfigStruct := &LogsModelConfigStruct{} + defaults.Set(newLogsModelConfigStruct) + + if value, ok := fullConfig["kafka_config"]; ok { + var err error + value := value.(map[string]interface{}) + newLogsModelConfigStruct.KafkaConfig, err = NewKafkaConfigStruct(value) + if err != nil { + return newLogsModelConfigStruct, err + } + } + if value, ok := fullConfig["elasticsearch_config"]; ok { + var err error + value := value.(map[string]interface{}) + newLogsModelConfigStruct.ElasticsearchConfig, err = NewElasticsearchConfigStruct(value) + if err != nil { + return newLogsModelConfigStruct, err + } + } + if value, ok := fullConfig["kinesis_stream_config"]; ok { + var err error + value := value.(map[string]interface{}) + newLogsModelConfigStruct.KinesisStreamConfig, err = NewKinesisStreamConfigStruct(value) + if err != nil { + return newLogsModelConfigStruct, err + } + } + if value, ok := fullConfig["producer"]; ok { + newLogsModelConfigStruct.Producer, ok = value.(string) + if !ok { + return newLogsModelConfigStruct, errors.New("producer must be of type string") + } + } + + return newLogsModelConfigStruct, nil +} + +// NewKinesisStreamConfigStruct creates a new KinesisStreamConfigStruct +func NewKinesisStreamConfigStruct(fullConfig map[string]interface{}) (*KinesisStreamConfigStruct, error) { + newKinesisStreamConfigStruct := &KinesisStreamConfigStruct{} + defaults.Set(newKinesisStreamConfigStruct) + + if value, ok := fullConfig["retries"]; ok { + newKinesisStreamConfigStruct.Retries, ok = value.(int) + if !ok { + return newKinesisStreamConfigStruct, errors.New("retries must be of type int") + } + } + if value, ok := fullConfig["read_timeout"]; ok { + newKinesisStreamConfigStruct.ReadTimeout, ok = value.(int) + if !ok { + return newKinesisStreamConfigStruct, errors.New("read_timeout must be of type int") + } + } + if value, ok := fullConfig["max_pool_connections"]; ok { + newKinesisStreamConfigStruct.MaxPoolConnections, ok = value.(int) + if !ok { + return newKinesisStreamConfigStruct, errors.New("max_pool_connections must be of type int") + } + } + if value, ok := fullConfig["aws_region"]; ok { + newKinesisStreamConfigStruct.AwsRegion, ok = value.(string) + if !ok { + return newKinesisStreamConfigStruct, errors.New("aws_region must be of type string") + } + } + if value, ok := fullConfig["connect_timeout"]; ok { + newKinesisStreamConfigStruct.ConnectTimeout, ok = value.(int) + if !ok { + return newKinesisStreamConfigStruct, errors.New("connect_timeout must be of type int") + } + } + if value, ok := fullConfig["aws_secret_key"]; ok { + newKinesisStreamConfigStruct.AwsSecretKey, ok = value.(string) + if !ok { + return newKinesisStreamConfigStruct, errors.New("aws_secret_key must be of type string") + } + } + if value, ok := fullConfig["stream_name"]; ok { + newKinesisStreamConfigStruct.StreamName, ok = value.(string) + if !ok { + return newKinesisStreamConfigStruct, errors.New("stream_name must be of type string") + } + } + if value, ok := fullConfig["aws_access_key"]; ok { + newKinesisStreamConfigStruct.AwsAccessKey, ok = value.(string) + if !ok { + return newKinesisStreamConfigStruct, errors.New("aws_access_key must be of type string") + } + } + + return newKinesisStreamConfigStruct, nil +} + +// NewElasticsearchConfigStruct creates a new ElasticsearchConfigStruct +func NewElasticsearchConfigStruct(fullConfig map[string]interface{}) (*ElasticsearchConfigStruct, error) { + newElasticsearchConfigStruct := &ElasticsearchConfigStruct{} + defaults.Set(newElasticsearchConfigStruct) + + if value, ok := fullConfig["aws_region"]; ok { + newElasticsearchConfigStruct.AwsRegion, ok = value.(string) + if !ok { + return newElasticsearchConfigStruct, errors.New("aws_region must be of type string") + } + } + if value, ok := fullConfig["port"]; ok { + newElasticsearchConfigStruct.Port, ok = value.(int) + if !ok { + return newElasticsearchConfigStruct, errors.New("port must be of type int") + } + } + if value, ok := fullConfig["access_key"]; ok { + newElasticsearchConfigStruct.AccessKey, ok = value.(string) + if !ok { + return newElasticsearchConfigStruct, errors.New("access_key must be of type string") + } + } + if value, ok := fullConfig["host"]; ok { + newElasticsearchConfigStruct.Host, ok = value.(string) + if !ok { + return newElasticsearchConfigStruct, errors.New("host must be of type string") + } + } + if value, ok := fullConfig["index_prefix"]; ok { + newElasticsearchConfigStruct.IndexPrefix, ok = value.(string) + if !ok { + return newElasticsearchConfigStruct, errors.New("index_prefix must be of type string") + } + } + if value, ok := fullConfig["index_settings"]; ok { + var err error + value := value.(map[string]interface{}) + newElasticsearchConfigStruct.IndexSettings, err = NewIndexSettingsStruct(value) + if err != nil { + return newElasticsearchConfigStruct, err + } + } + if value, ok := fullConfig["use_ssl"]; ok { + newElasticsearchConfigStruct.UseSsl, ok = value.(bool) + if !ok { + return newElasticsearchConfigStruct, errors.New("use_ssl must be of type bool") + } + } + if value, ok := fullConfig["secret_key"]; ok { + newElasticsearchConfigStruct.SecretKey, ok = value.(string) + if !ok { + return newElasticsearchConfigStruct, errors.New("secret_key must be of type string") + } + } + + return newElasticsearchConfigStruct, nil +} + +// NewIndexSettingsStruct creates a new IndexSettingsStruct +func NewIndexSettingsStruct(fullConfig map[string]interface{}) (*IndexSettingsStruct, error) { + newIndexSettingsStruct := IndexSettingsStruct{} + for key, value := range fullConfig { + newIndexSettingsStruct[key] = value + } + return &newIndexSettingsStruct, nil +} + +// NewKafkaConfigStruct creates a new KafkaConfigStruct +func NewKafkaConfigStruct(fullConfig map[string]interface{}) (*KafkaConfigStruct, error) { + newKafkaConfigStruct := &KafkaConfigStruct{} + defaults.Set(newKafkaConfigStruct) + + if value, ok := fullConfig["topic"]; ok { + newKafkaConfigStruct.Topic, ok = value.(string) + if !ok { + return newKafkaConfigStruct, errors.New("topic must be of type string") + } + } + if value, ok := fullConfig["bootstrap_servers"]; ok { + newKafkaConfigStruct.BootstrapServers, ok = value.([]interface{}) + if !ok { + return newKafkaConfigStruct, errors.New("bootstrap_servers must be of type []interface{}") + } + } + if value, ok := fullConfig["max_block_seconds"]; ok { + newKafkaConfigStruct.MaxBlockSeconds, ok = value.(int) + if !ok { + return newKafkaConfigStruct, errors.New("max_block_seconds must be of type int") + } + } + + return newKafkaConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_fields.go b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_fields.go new file mode 100644 index 000000000..94a7b3d3f --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_fields.go @@ -0,0 +1,6 @@ +package elasticsearch + +// Fields returns a list of strings representing the fields in this field group +func (fg *ElasticSearchFieldGroup) Fields() []string { + return []string{"LOGS_MODEL", "LOGS_MODEL_CONFIG"} +} diff --git a/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_test.go b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_test.go new file mode 100644 index 000000000..ebd02b5c7 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_test.go @@ -0,0 +1,68 @@ +package elasticsearch + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateElasticSearch(t *testing.T) { + + // Valid config + logsModelConfig := map[string]interface{}{ + "elasticsearch_config": map[string]interface{}{ + "access_key": "test_client_key", + "secret_key": "test_secret_key", + "host": "bfd70499058e4485854f8bacf06af627.us-central1.gcp.cloud.es.io", + "port": 9243, + }, + } + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "Valid", config: map[string]interface{}{"LOGS_MODEL": "database"}, want: "valid"}, + {name: "MissingConfig", config: map[string]interface{}{"LOGS_MODEL": "elasticsearch"}, want: "invalid"}, + {name: "ValidConfig", config: map[string]interface{}{"LOGS_MODEL": "elasticsearch", "LOGS_MODEL_CONFIG": logsModelConfig}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewElasticSearchFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_validator.go b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_validator.go new file mode 100644 index 000000000..0b3ff673c --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/elasticsearch/elasticsearch_validator.go @@ -0,0 +1,111 @@ +package elasticsearch + +import ( + "fmt" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *ElasticSearchFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "ElasticSearch" + + // Make empty errors + errors := []shared.ValidationError{} + + // If not set, skip + if fg.LogsModel != "elasticsearch" { + return errors + } + + // If log model config is missing + if fg.LogsModelConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG"}, + FieldGroup: fgName, + Message: "LOGS_MODEL_CONFIG is required for Elasticsearch", + } + errors = append(errors, newError) + return errors + } + + // Check for elastic search config + if fg.LogsModelConfig.ElasticsearchConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG"}, + FieldGroup: fgName, + Message: "LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG is required for Elasticsearch", + } + errors = append(errors, newError) + return errors + } + + // Check that host is available + if fg.LogsModelConfig.ElasticsearchConfig.Host == "" { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.HOST"}, + FieldGroup: fgName, + Message: "LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.HOST is required for Elasticsearch", + } + errors = append(errors, newError) + } + + // Check for port + if fg.LogsModelConfig.ElasticsearchConfig.Port == 0 { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.PORT"}, + FieldGroup: fgName, + Message: "LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.PORT is required for Elasticsearch", + } + errors = append(errors, newError) + } + + // Check for access key + if fg.LogsModelConfig.ElasticsearchConfig.AccessKey == "" { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.ACCESS_KEY"}, + FieldGroup: fgName, + Message: "LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.ACCESS_KEY is required for Elasticsearch", + } + errors = append(errors, newError) + } + + if fg.LogsModelConfig.ElasticsearchConfig.SecretKey == "" { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.SECRET_KEY"}, + FieldGroup: fgName, + Message: "LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.SECRET_KEY is required for Elasticsearch", + } + errors = append(errors, newError) + } + + // Validate OAuth + var success bool + if opts.Mode != "testing" { + // Get parameters to build url + host := fg.LogsModelConfig.ElasticsearchConfig.Host + port := fmt.Sprintf("%d", fg.LogsModelConfig.ElasticsearchConfig.Port) + //indexPrefix := fg.LogsModelConfig.ElasticsearchConfig.IndexPrefix + + // Build url + url := "https://" + host + ":" + port + "/" + fg.LogsModelConfig.ElasticsearchConfig.IndexPrefix + "*" + success = shared.ValidateElasticSearchCredentials(url, fg.LogsModelConfig.ElasticsearchConfig.AccessKey, fg.LogsModelConfig.ElasticsearchConfig.SecretKey) + + } else { + // Mock test + success = (fg.LogsModelConfig.ElasticsearchConfig.AccessKey == "test_client_key") && (fg.LogsModelConfig.ElasticsearchConfig.SecretKey == "test_secret_key") + } + + if !success { + newError := shared.ValidationError{ + Tags: []string{"LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.SECRET_KEY", "LOGS_MODEL_CONFIG.ELASTIC_SEARCH_CONFIG.SECRET_KEY"}, + FieldGroup: fgName, + Message: "Could not validate Elasticsearch credentials", + } + errors = append(errors, newError) + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/email/email.go b/config-tool/pkg/lib/fieldgroups/email/email.go new file mode 100644 index 000000000..34d8f960a --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/email/email.go @@ -0,0 +1,91 @@ +package email + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// EmailFieldGroup represents the EmailFieldGroup config fields +type EmailFieldGroup struct { + BlacklistedEmailDomains []interface{} `default:"[]" validate:"" yaml:"BLACKLISTED_EMAIL_DOMAINS,omitempty" json:"BLACKLISTED_EMAIL_DOMAINS,omitempty"` + FeatureBlacklistedEmails bool `default:"false" validate:"" yaml:"FEATURE_BLACKLISTED_EMAILS" json:"FEATURE_BLACKLISTED_EMAILS"` + FeatureMailing bool `default:"false" validate:"" yaml:"FEATURE_MAILING" json:"FEATURE_MAILING"` + MailDefaultSender string `default:"support@quay.io" validate:"" yaml:"MAIL_DEFAULT_SENDER,omitempty" json:"MAIL_DEFAULT_SENDER,omitempty"` + MailPassword string `default:"" validate:"" yaml:"MAIL_PASSWORD,omitempty" json:"MAIL_PASSWORD,omitempty"` + MailPort int `default:"587" validate:"" yaml:"MAIL_PORT,omitempty" json:"MAIL_PORT,omitempty"` + MailServer string `default:"" validate:"" yaml:"MAIL_SERVER,omitempty" json:"MAIL_SERVER,omitempty"` + MailUseAuth bool `default:"false" validate:"" yaml:"MAIL_USE_AUTH" json:"MAIL_USE_AUTH"` + MailUsername string `default:"" validate:"" yaml:"MAIL_USERNAME,omitempty" json:"MAIL_USERNAME,omitempty"` + MailUseTls bool `default:"false" validate:"" yaml:"MAIL_USE_TLS" json:"MAIL_USE_TLS"` + FeatureFIPS bool `default:"false" validate:"" yaml:"FEATURE_FIPS" json:"FEATURE_FIPS"` +} + +// NewEmailFieldGroup creates a new EmailFieldGroup +func NewEmailFieldGroup(fullConfig map[string]interface{}) (*EmailFieldGroup, error) { + newEmailFieldGroup := &EmailFieldGroup{} + defaults.Set(newEmailFieldGroup) + + if value, ok := fullConfig["BLACKLISTED_EMAIL_DOMAINS"]; ok { + newEmailFieldGroup.BlacklistedEmailDomains, ok = value.([]interface{}) + if !ok { + return newEmailFieldGroup, errors.New("BLACKLISTED_EMAIL_DOMAINS must be of type []interface{}") + } + } + if value, ok := fullConfig["FEATURE_BLACKLISTED_EMAILS"]; ok { + newEmailFieldGroup.FeatureBlacklistedEmails, ok = value.(bool) + if !ok { + return newEmailFieldGroup, errors.New("FEATURE_BLACKLISTED_EMAILS must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_MAILING"]; ok { + newEmailFieldGroup.FeatureMailing, ok = value.(bool) + if !ok { + return newEmailFieldGroup, errors.New("FEATURE_MAILING must be of type bool") + } + } + if value, ok := fullConfig["MAIL_DEFAULT_SENDER"]; ok { + newEmailFieldGroup.MailDefaultSender, ok = value.(string) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_DEFAULT_SENDER must be of type string") + } + } + if value, ok := fullConfig["MAIL_PASSWORD"]; ok { + newEmailFieldGroup.MailPassword, ok = value.(string) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_PASSWORD must be of type string") + } + } + if value, ok := fullConfig["MAIL_PORT"]; ok { + newEmailFieldGroup.MailPort, ok = value.(int) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_PORT must be of type int") + } + } + if value, ok := fullConfig["MAIL_SERVER"]; ok { + newEmailFieldGroup.MailServer, ok = value.(string) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_SERVER must be of type string") + } + } + if value, ok := fullConfig["MAIL_USERNAME"]; ok { + newEmailFieldGroup.MailUsername, ok = value.(string) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_USERNAME must be of type string") + } + } + if value, ok := fullConfig["MAIL_USE_AUTH"]; ok { + newEmailFieldGroup.MailUseAuth, ok = value.(bool) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_USE_AUTH must be of type bool") + } + } + if value, ok := fullConfig["MAIL_USE_TLS"]; ok { + newEmailFieldGroup.MailUseTls, ok = value.(bool) + if !ok { + return newEmailFieldGroup, errors.New("MAIL_USE_TLS must be of type bool") + } + } + + return newEmailFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/email/email_fields.go b/config-tool/pkg/lib/fieldgroups/email/email_fields.go new file mode 100644 index 000000000..685e8c229 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/email/email_fields.go @@ -0,0 +1,6 @@ +package email + +// Fields returns a list of strings representing the fields in this field group +func (fg *EmailFieldGroup) Fields() []string { + return []string{"BLACKLISTED_EMAIL_DOMAINS", "FEATURE_BLACKLISTED_EMAILS", "FEATURE_MAILING", "MAIL_DEFAULT_SENDER", "MAIL_PASSWORD", "MAIL_PORT", "MAIL_SERVER", "MAIL_USERNAME", "MAIL_USE_AUTH", "MAIL_USE_TLS"} +} diff --git a/config-tool/pkg/lib/fieldgroups/email/email_test.go b/config-tool/pkg/lib/fieldgroups/email/email_test.go new file mode 100644 index 000000000..8bd4829d6 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/email/email_test.go @@ -0,0 +1,17 @@ +package email + +// // TestValidateEmail tests the Validate function +// func TestValidateEmail(t testing.T) { +// var tests = []struct { +// name string +// config map[string]interface{} +// want string +// }{{ +// config: map[string]interface{}{}, +// name: "testOne", +// want: "valid", +// }} +// // for _, tt := range tests { +// // fmt.Println("hello") +// // } +// } diff --git a/config-tool/pkg/lib/fieldgroups/email/email_validator.go b/config-tool/pkg/lib/fieldgroups/email/email_validator.go new file mode 100644 index 000000000..4a09921aa --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/email/email_validator.go @@ -0,0 +1,43 @@ +package email + +import ( + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *EmailFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "Email" + + // Make empty errors + errors := []shared.ValidationError{} + + // Only validate if feature is on + if fg.FeatureMailing == false { + return errors + } + + // Check for mail server + if ok, err := shared.ValidateRequiredString(fg.MailServer, "MAIL_SERVER", fgName); !ok { + errors = append(errors, err) + return errors + } + + // If FIPS is enabled, ensure mail tls is enabled + if fg.FeatureFIPS && !fg.MailUseTls { + newError := shared.ValidationError{ + Tags: []string{"MAIL_USE_TLS", "FEATURE_FIPS"}, + FieldGroup: fgName, + Message: "MAIL_USE_TLS must be enabled when running in FIPS mode.", + } + errors = append(errors, newError) + return errors + } + + if ok, err := shared.ValidateEmailServer(opts, fg.MailServer, fg.MailPort, fg.MailUseTls, fg.MailUseAuth, fg.MailUsername, fg.MailPassword, fgName); !ok { + errors = append(errors, err) + return errors + } + + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger.go b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger.go new file mode 100644 index 000000000..90449535e --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger.go @@ -0,0 +1,98 @@ +package githubbuildtrigger + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// GitHubBuildTriggerFieldGroup represents the GitHubBuildTriggerFieldGroup config fields +type GitHubBuildTriggerFieldGroup struct { + FeatureBuildSupport bool `default:"false" validate:"" json:"FEATURE_BUILD_SUPPORT" yaml:"FEATURE_BUILD_SUPPORT"` + FeatureGithubBuild bool `default:"false" validate:"" json:"FEATURE_GITHUB_BUILD" yaml:"FEATURE_GITHUB_BUILD"` + GithubTriggerConfig *GithubTriggerConfigStruct `default:"" validate:"" json:"GITHUB_TRIGGER_CONFIG,omitempty" yaml:"GITHUB_TRIGGER_CONFIG,omitempty"` +} + +// GithubTriggerConfigStruct represents the GithubTriggerConfigStruct config fields +type GithubTriggerConfigStruct struct { + AllowedOrganizations []interface{} `default:"[]" validate:"" json:"ALLOWED_ORGANIZATIONS,omitempty" yaml:"ALLOWED_ORGANIZATIONS,omitempty"` + OrgRestrict bool `default:"false" validate:"" json:"ORG_RESTRICT" yaml:"ORG_RESTRICT"` + ApiEndpoint string `default:"" validate:"" json:"API_ENDPOINT,omitempty" yaml:"API_ENDPOINT,omitempty"` + ClientSecret string `default:"" validate:"" json:"CLIENT_SECRET,omitempty" yaml:"CLIENT_SECRET,omitempty"` + GithubEndpoint string `default:"" validate:"" json:"GITHUB_ENDPOINT,omitempty" yaml:"GITHUB_ENDPOINT,omitempty"` + ClientId string `default:"" validate:"" json:"CLIENT_ID,omitempty" yaml:"CLIENT_ID,omitempty"` +} + +// NewGitHubBuildTriggerFieldGroup creates a new GitHubBuildTriggerFieldGroup +func NewGitHubBuildTriggerFieldGroup(fullConfig map[string]interface{}) (*GitHubBuildTriggerFieldGroup, error) { + newGitHubBuildTriggerFieldGroup := &GitHubBuildTriggerFieldGroup{} + defaults.Set(newGitHubBuildTriggerFieldGroup) + + if value, ok := fullConfig["FEATURE_BUILD_SUPPORT"]; ok { + newGitHubBuildTriggerFieldGroup.FeatureBuildSupport, ok = value.(bool) + if !ok { + return newGitHubBuildTriggerFieldGroup, errors.New("FEATURE_BUILD_SUPPORT must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_GITHUB_BUILD"]; ok { + newGitHubBuildTriggerFieldGroup.FeatureGithubBuild, ok = value.(bool) + if !ok { + return newGitHubBuildTriggerFieldGroup, errors.New("FEATURE_GITHUB_BUILD must be of type bool") + } + } + if value, ok := fullConfig["GITHUB_TRIGGER_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newGitHubBuildTriggerFieldGroup.GithubTriggerConfig, err = NewGithubTriggerConfigStruct(value) + if err != nil { + return newGitHubBuildTriggerFieldGroup, err + } + } + + return newGitHubBuildTriggerFieldGroup, nil +} + +// NewGithubTriggerConfigStruct creates a new GithubTriggerConfigStruct +func NewGithubTriggerConfigStruct(fullConfig map[string]interface{}) (*GithubTriggerConfigStruct, error) { + newGithubTriggerConfigStruct := &GithubTriggerConfigStruct{} + defaults.Set(newGithubTriggerConfigStruct) + + if value, ok := fullConfig["ALLOWED_ORGANIZATIONS"]; ok { + newGithubTriggerConfigStruct.AllowedOrganizations, ok = value.([]interface{}) + if !ok { + return newGithubTriggerConfigStruct, errors.New("ALLOWED_ORGANIZATIONS must be of type []interface{}") + } + } + if value, ok := fullConfig["ORG_RESTRICT"]; ok { + newGithubTriggerConfigStruct.OrgRestrict, ok = value.(bool) + if !ok { + return newGithubTriggerConfigStruct, errors.New("ORG_RESTRICT must be of type bool") + } + } + if value, ok := fullConfig["API_ENDPOINT"]; ok { + newGithubTriggerConfigStruct.ApiEndpoint, ok = value.(string) + if !ok { + return newGithubTriggerConfigStruct, errors.New("API_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["CLIENT_SECRET"]; ok { + newGithubTriggerConfigStruct.ClientSecret, ok = value.(string) + if !ok { + return newGithubTriggerConfigStruct, errors.New("CLIENT_SECRET must be of type string") + } + } + if value, ok := fullConfig["GITHUB_ENDPOINT"]; ok { + newGithubTriggerConfigStruct.GithubEndpoint, ok = value.(string) + if !ok { + return newGithubTriggerConfigStruct, errors.New("GITHUB_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["CLIENT_ID"]; ok { + newGithubTriggerConfigStruct.ClientId, ok = value.(string) + if !ok { + return newGithubTriggerConfigStruct, errors.New("CLIENT_ID must be of type string") + } + } + + return newGithubTriggerConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_fields.go b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_fields.go new file mode 100644 index 000000000..5ce5ba548 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_fields.go @@ -0,0 +1,6 @@ +package githubbuildtrigger + +// Fields returns a list of strings representing the fields in this field group +func (fg *GitHubBuildTriggerFieldGroup) Fields() []string { + return []string{"FEATURE_BUILD_SUPPORT", "FEATURE_GITHUB_BUILD", "GITHUB_TRIGGER_CONFIG"} +} diff --git a/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_test.go b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_test.go new file mode 100644 index 000000000..6dc5b2d00 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_test.go @@ -0,0 +1,94 @@ +package githubbuildtrigger + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateGitHubBuildTrigger(t *testing.T) { + + // Valid config + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "FeatureBuildOff", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": false}, want: "valid"}, + {name: "FeatureGithubBuildOff", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true}, want: "invalid"}, + {name: "Valid", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "test_client_key", + "CLIENT_SECRET": "test_secret_key", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "valid"}, + {name: "NoClientSecret", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoClientID", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoGitHubEndpoint", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + }}, want: "invalid"}, + {name: "InvalidGitHubEndpoint", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "not_a_valid_endpoint", + }}, want: "invalid"}, + {name: "NoneInOrgList", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "https://endpoint.com", + "ORG_RESTRICT": true, + }}, want: "invalid"}, + {name: "ValidWithOrgList", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITHUB_BUILD": true, "GITHUB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "test_client_key", + "CLIENT_SECRET": "test_secret_key", + "GITHUB_ENDPOINT": "https://endpoint.com", + "ORG_RESTRICT": true, + "ALLOWED_ORGANIZATIONS": []interface{}{"Org1", "Org2"}, + }}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewGitHubBuildTriggerFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_validator.go b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_validator.go new file mode 100644 index 000000000..3fdde866a --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githubbuildtrigger/githubbuildtrigger_validator.go @@ -0,0 +1,106 @@ +package githubbuildtrigger + +import ( + "cuelang.org/go/pkg/strings" + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *GitHubBuildTriggerFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "GitHubBuildTrigger" + + // Make empty errors + errors := []shared.ValidationError{} + + // If build support is off, return false + if fg.FeatureBuildSupport == false { + return errors + } + + // If github trigger is off + if fg.FeatureGithubBuild == false { + return errors + } + + // Check for config + if fg.GithubTriggerConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG"}, + FieldGroup: fgName, + Message: "GITHUB_TRIGGER_CONFIG is required for GitHubBuildTrigger", + } + errors = append(errors, newError) + return errors + } + + // Check for endpoint + if fg.GithubTriggerConfig.GithubEndpoint == "" { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT"}, + FieldGroup: fgName, + Message: "GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT is required for GitHubBuildTrigger", + } + errors = append(errors, newError) + } + + // Check for endpoint + if !strings.HasPrefix(fg.GithubTriggerConfig.GithubEndpoint, "http://") && !strings.HasPrefix(fg.GithubTriggerConfig.GithubEndpoint, "https://") { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT"}, + FieldGroup: fgName, + Message: "GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT must be a url", + } + errors = append(errors, newError) + } + + // Check for client id + if fg.GithubTriggerConfig.ClientId == "" { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.CLIENT_ID"}, + FieldGroup: fgName, + Message: "GITHUB_TRIGGER_CONFIG.CLIENT_ID is required for GitHubBuildTrigger", + } + errors = append(errors, newError) + } + + // Check for endpoint + if fg.GithubTriggerConfig.ClientSecret == "" { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "GITHUB_TRIGGER_CONFIG.CLIENT_SECRET is required for GitHubBuildTrigger", + } + errors = append(errors, newError) + } + + // If restricted orgs, make sure + if fg.GithubTriggerConfig.OrgRestrict && len(fg.GithubTriggerConfig.AllowedOrganizations) == 0 { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.ORG_RESTRICT", "GITHUB_TRIGGER_CONFIG.ALLOWED_ORGANIZATIONS"}, + FieldGroup: fgName, + Message: "GITHUB_TRIGGER_CONFIG.ALLOWED_ORGANIZATIONS must contain values if GITHUB_TRIGGER_CONFIG.ORG_RESTRICT is true", + } + errors = append(errors, newError) + } + + // Check OAuth endpoint + var success bool + if opts.Mode != "testing" { + success = shared.ValidateGitHubOAuth(opts, fg.GithubTriggerConfig.ClientId, fg.GithubTriggerConfig.ClientSecret, fg.GithubTriggerConfig.GithubEndpoint) + } else { + success = (fg.GithubTriggerConfig.ClientId == "test_client_key") && (fg.GithubTriggerConfig.ClientSecret == "test_secret_key") + } + + if !success { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.CLIENT_ID", "GITHUB_TRIGGER_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "Could not verify GitHub OAuth credentials", + } + errors = append(errors, newError) + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/githublogin/githublogin.go b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin.go new file mode 100644 index 000000000..b21b1a355 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin.go @@ -0,0 +1,91 @@ +package githublogin + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// GitHubLoginFieldGroup represents the GitHubLoginFieldGroup config fields +type GitHubLoginFieldGroup struct { + FeatureGithubLogin bool `default:"false" validate:"" json:"FEATURE_GITHUB_LOGIN" yaml:"FEATURE_GITHUB_LOGIN"` + GithubLoginConfig *GithubLoginConfigStruct `default:"" validate:"" json:"GITHUB_LOGIN_CONFIG,omitempty" yaml:"GITHUB_LOGIN_CONFIG,omitempty"` +} + +// GithubLoginConfigStruct represents the GithubLoginConfigStruct config fields +type GithubLoginConfigStruct struct { + AllowedOrganizations []interface{} `default:"[]" validate:"" json:"ALLOWED_ORGANIZATIONS,omitempty" yaml:"ALLOWED_ORGANIZATIONS,omitempty"` + OrgRestrict bool `default:"false" validate:"" json:"ORG_RESTRICT,omitempty" yaml:"ORG_RESTRICT,omitempty"` + ApiEndpoint string `default:"" validate:"" json:"API_ENDPOINT,omitempty" yaml:"API_ENDPOINT,omitempty"` + GithubEndpoint string `default:"" validate:"" json:"GITHUB_ENDPOINT,omitempty" yaml:"GITHUB_ENDPOINT,omitempty"` + ClientId string `default:"" validate:"" json:"CLIENT_ID,omitempty" yaml:"CLIENT_ID,omitempty"` + ClientSecret string `default:"" validate:"" json:"CLIENT_SECRET,omitempty" yaml:"CLIENT_SECRET,omitempty"` +} + +// NewGitHubLoginFieldGroup creates a new GitHubLoginFieldGroup +func NewGitHubLoginFieldGroup(fullConfig map[string]interface{}) (*GitHubLoginFieldGroup, error) { + newGitHubLoginFieldGroup := &GitHubLoginFieldGroup{} + defaults.Set(newGitHubLoginFieldGroup) + + if value, ok := fullConfig["FEATURE_GITHUB_LOGIN"]; ok { + newGitHubLoginFieldGroup.FeatureGithubLogin, ok = value.(bool) + if !ok { + return newGitHubLoginFieldGroup, errors.New("FEATURE_GITHUB_LOGIN must be of type bool") + } + } + if value, ok := fullConfig["GITHUB_LOGIN_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newGitHubLoginFieldGroup.GithubLoginConfig, err = NewGithubLoginConfigStruct(value) + if err != nil { + return newGitHubLoginFieldGroup, err + } + } + + return newGitHubLoginFieldGroup, nil +} + +// NewGithubLoginConfigStruct creates a new GithubLoginConfigStruct +func NewGithubLoginConfigStruct(fullConfig map[string]interface{}) (*GithubLoginConfigStruct, error) { + newGithubLoginConfigStruct := &GithubLoginConfigStruct{} + defaults.Set(newGithubLoginConfigStruct) + + if value, ok := fullConfig["ALLOWED_ORGANIZATIONS"]; ok { + newGithubLoginConfigStruct.AllowedOrganizations, ok = value.([]interface{}) + if !ok { + return newGithubLoginConfigStruct, errors.New("ALLOWED_ORGANIZATIONS must be of type []interface{}") + } + } + if value, ok := fullConfig["ORG_RESTRICT"]; ok { + newGithubLoginConfigStruct.OrgRestrict, ok = value.(bool) + if !ok { + return newGithubLoginConfigStruct, errors.New("ORG_RESTRICT must be of type bool") + } + } + if value, ok := fullConfig["API_ENDPOINT"]; ok { + newGithubLoginConfigStruct.ApiEndpoint, ok = value.(string) + if !ok { + return newGithubLoginConfigStruct, errors.New("API_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["GITHUB_ENDPOINT"]; ok { + newGithubLoginConfigStruct.GithubEndpoint, ok = value.(string) + if !ok { + return newGithubLoginConfigStruct, errors.New("GITHUB_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["CLIENT_ID"]; ok { + newGithubLoginConfigStruct.ClientId, ok = value.(string) + if !ok { + return newGithubLoginConfigStruct, errors.New("CLIENT_ID must be of type string") + } + } + if value, ok := fullConfig["CLIENT_SECRET"]; ok { + newGithubLoginConfigStruct.ClientSecret, ok = value.(string) + if !ok { + return newGithubLoginConfigStruct, errors.New("CLIENT_SECRET must be of type string") + } + } + + return newGithubLoginConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_fields.go b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_fields.go new file mode 100644 index 000000000..7baa5e94a --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_fields.go @@ -0,0 +1,6 @@ +package githublogin + +// Fields returns a list of strings representing the fields in this field group +func (fg *GitHubLoginFieldGroup) Fields() []string { + return []string{"FEATURE_GITHUB_LOGIN", "GITHUB_LOGIN_CONFIG"} +} diff --git a/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_test.go b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_test.go new file mode 100644 index 000000000..43f3b252b --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_test.go @@ -0,0 +1,93 @@ +package githublogin + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateGitHubLoginTrigger(t *testing.T) { + + // Valid config + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "FeatureGithubBuildOff", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true}, want: "invalid"}, + {name: "Valid", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_ID": "test_client_key", + "CLIENT_SECRET": "test_secret_key", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "valid"}, + {name: "NoClientSecret", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoClientID", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoGitHubEndpoint", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + }}, want: "invalid"}, + {name: "InvalidGitHubEndpoint", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "not_a_valid_endpoint", + }}, want: "invalid"}, + {name: "NoneInOrgList", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "https://endpoint.com", + "ORG_RESTRICT": true, + }}, want: "invalid"}, + {name: "ValidWithOrgList", config: map[string]interface{}{"FEATURE_GITHUB_LOGIN": true, "GITHUB_LOGIN_CONFIG": map[string]interface{}{ + "CLIENT_ID": "test_client_key", + "CLIENT_SECRET": "test_secret_key", + "GITHUB_ENDPOINT": "https://endpoint.com", + "ORG_RESTRICT": true, + "ALLOWED_ORGANIZATIONS": []interface{}{"Org1", "Org2"}, + }}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewGitHubLoginFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_validator.go b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_validator.go new file mode 100644 index 000000000..5b61064a8 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/githublogin/githublogin_validator.go @@ -0,0 +1,99 @@ +package githublogin + +import ( + "cuelang.org/go/pkg/strings" + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *GitHubLoginFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "GitHubLogin" + + // Make empty errors + errors := []shared.ValidationError{} + + // If github trigger is off + if !fg.FeatureGithubLogin { + return errors + } + + // Check for config + if fg.GithubLoginConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_LOGIN_CONFIG"}, + FieldGroup: fgName, + Message: "GITHUB_LOGIN_CONFIG is required for GitHubLogin", + } + errors = append(errors, newError) + return errors + } + + // Check for endpoint + if fg.GithubLoginConfig.GithubEndpoint == "" { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT"}, + FieldGroup: fgName, + Message: "GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT is required for GitHubLogin", + } + errors = append(errors, newError) + } + + // Check for endpoint + if !strings.HasPrefix(fg.GithubLoginConfig.GithubEndpoint, "http://") && !strings.HasPrefix(fg.GithubLoginConfig.GithubEndpoint, "https://") { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT"}, + FieldGroup: fgName, + Message: "GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT must be a url", + } + errors = append(errors, newError) + } + + // Check for client id + if fg.GithubLoginConfig.ClientId == "" { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_LOGIN_CONFIG.CLIENT_ID"}, + FieldGroup: fgName, + Message: "GITHUB_LOGIN_CONFIG.CLIENT_ID is required for GitHubLogin", + } + errors = append(errors, newError) + } + + // Check for endpoint + if fg.GithubLoginConfig.ClientSecret == "" { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_LOGIN_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "GITHUB_LOGIN_CONFIG.CLIENT_SECRET is required for GitHubLogin", + } + errors = append(errors, newError) + } + + // If restricted orgs, make sure + if fg.GithubLoginConfig.OrgRestrict && len(fg.GithubLoginConfig.AllowedOrganizations) == 0 { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_LOGIN_CONFIG.ORG_RESTRICT", "GITHUB_LOGIN_CONFIG.ALLOWED_ORGANIZATIONS"}, + FieldGroup: fgName, + Message: "GITHUB_LOGIN_CONFIG.ALLOWED_ORGANIZATIONS must contain values if GITHUB_LOGIN_CONFIG.ORG_RESTRICT is true", + } + errors = append(errors, newError) + } + + // Check OAuth endpoint + var success bool + if opts.Mode != "testing" { + success = shared.ValidateGitHubOAuth(opts, fg.GithubLoginConfig.ClientId, fg.GithubLoginConfig.ClientSecret, fg.GithubLoginConfig.GithubEndpoint) + } else { + success = (fg.GithubLoginConfig.ClientId == "test_client_key") && (fg.GithubLoginConfig.ClientSecret == "test_secret_key") + } + if !success { + newError := shared.ValidationError{ + Tags: []string{"GITHUB_TRIGGER_CONFIG.CLIENT_ID", "GITHUB_TRIGGER_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "Could not verify GitHub OAuth credentials", + } + errors = append(errors, newError) + } + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger.go b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger.go new file mode 100644 index 000000000..c0f1f1093 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger.go @@ -0,0 +1,77 @@ +package gitlabbuildtrigger + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// GitLabBuildTriggerFieldGroup represents the GitLabBuildTriggerFieldGroup config fields +type GitLabBuildTriggerFieldGroup struct { + FeatureBuildSupport bool `default:"false" validate:"" json:"FEATURE_BUILD_SUPPORT" yaml:"FEATURE_BUILD_SUPPORT"` + FeatureGitlabBuild bool `default:"false" validate:"" json:"FEATURE_GITLAB_BUILD" yaml:"FEATURE_GITLAB_BUILD"` + GitlabTriggerConfig *GitlabTriggerConfigStruct `default:"" validate:"" json:"GITLAB_TRIGGER_CONFIG,omitempty" yaml:"GITLAB_TRIGGER_CONFIG,omitempty"` +} + +// GitlabTriggerConfigStruct represents the GitlabTriggerConfigStruct config fields +type GitlabTriggerConfigStruct struct { + GitlabEndpoint string `default:"https://gitlab.com/" validate:"" json:"GITLAB_ENDPOINT,omitempty" yaml:"GITLAB_ENDPOINT,omitempty"` + ClientId string `default:"" validate:"" json:"CLIENT_ID,omitempty" yaml:"CLIENT_ID,omitempty"` + ClientSecret string `default:"" validate:"" json:"CLIENT_SECRET,omitempty" yaml:"CLIENT_SECRET,omitempty"` +} + +// NewGitLabBuildTriggerFieldGroup creates a new GitLabBuildTriggerFieldGroup +func NewGitLabBuildTriggerFieldGroup(fullConfig map[string]interface{}) (*GitLabBuildTriggerFieldGroup, error) { + newGitLabBuildTriggerFieldGroup := &GitLabBuildTriggerFieldGroup{} + defaults.Set(newGitLabBuildTriggerFieldGroup) + + if value, ok := fullConfig["FEATURE_BUILD_SUPPORT"]; ok { + newGitLabBuildTriggerFieldGroup.FeatureBuildSupport, ok = value.(bool) + if !ok { + return newGitLabBuildTriggerFieldGroup, errors.New("FEATURE_BUILD_SUPPORT must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_GITLAB_BUILD"]; ok { + newGitLabBuildTriggerFieldGroup.FeatureGitlabBuild, ok = value.(bool) + if !ok { + return newGitLabBuildTriggerFieldGroup, errors.New("FEATURE_GITLAB_BUILD must be of type bool") + } + } + if value, ok := fullConfig["GITLAB_TRIGGER_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newGitLabBuildTriggerFieldGroup.GitlabTriggerConfig, err = NewGitlabTriggerConfigStruct(value) + if err != nil { + return newGitLabBuildTriggerFieldGroup, err + } + } + + return newGitLabBuildTriggerFieldGroup, nil +} + +// NewGitlabTriggerConfigStruct creates a new GitlabTriggerConfigStruct +func NewGitlabTriggerConfigStruct(fullConfig map[string]interface{}) (*GitlabTriggerConfigStruct, error) { + newGitlabTriggerConfigStruct := &GitlabTriggerConfigStruct{} + defaults.Set(newGitlabTriggerConfigStruct) + + if value, ok := fullConfig["GITLAB_ENDPOINT"]; ok { + newGitlabTriggerConfigStruct.GitlabEndpoint, ok = value.(string) + if !ok { + return newGitlabTriggerConfigStruct, errors.New("GITLAB_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["CLIENT_ID"]; ok { + newGitlabTriggerConfigStruct.ClientId, ok = value.(string) + if !ok { + return newGitlabTriggerConfigStruct, errors.New("CLIENT_ID must be of type string") + } + } + if value, ok := fullConfig["CLIENT_SECRET"]; ok { + newGitlabTriggerConfigStruct.ClientSecret, ok = value.(string) + if !ok { + return newGitlabTriggerConfigStruct, errors.New("CLIENT_SECRET must be of type string") + } + } + + return newGitlabTriggerConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_fields.go b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_fields.go new file mode 100644 index 000000000..a6a5de14f --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_fields.go @@ -0,0 +1,6 @@ +package gitlabbuildtrigger + +// Fields returns a list of strings representing the fields in this field group +func (fg *GitLabBuildTriggerFieldGroup) Fields() []string { + return []string{"FEATURE_BUILD_SUPPORT", "FEATURE_GITLAB_BUILD", "GITLAB_TRIGGER_CONFIG"} +} diff --git a/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_test.go b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_test.go new file mode 100644 index 000000000..a97d2cc18 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_test.go @@ -0,0 +1,83 @@ +package gitlabbuildtrigger + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateGitLabBuildTrigger tests the Validate function +func TestValidateGitLabBuildTrigger(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "FeatureBuildOff", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": false}, want: "valid"}, + {name: "FeatureGitlabBuildOff", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true}, want: "invalid"}, + {name: "Valid", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true, "GITLAB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "304c96b0015a469f6cdc907a22acbc5692d8ac2958b19a19a2585811e0c1019f", + "CLIENT_SECRET": "45060b331c39c30bd532eb71c720739d177f1a22238da470eab6a5e19f26057a", + "GITLAB_ENDPOINT": "https://gitlab.com", + }}, want: "valid"}, + {name: "BadCredentials", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true, "GITLAB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "bad_client_id", + "CLIENT_SECRET": "bad_cluent_secret", + "GITLAB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoClientSecret", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true, "GITLAB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoClientID", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true, "GITLAB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_SECRET": "clientsecret", + "GITHUB_ENDPOINT": "https://endpoint.com", + }}, want: "invalid"}, + {name: "NoGitlabEndpoint", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true, "GITLAB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + }}, want: "invalid"}, + {name: "InvalidGitlabEndpoint", config: map[string]interface{}{"FEATURE_BUILD_SUPPORT": true, "FEATURE_GITLAB_BUILD": true, "GITLAB_TRIGGER_CONFIG": map[string]interface{}{ + "CLIENT_ID": "clientid", + "CLIENT_SECRET": "clientsecret", + "GITLAB_ENDPOINT": "not_a_valid_endpoint", + }}, want: "invalid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewGitLabBuildTriggerFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_validator.go b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_validator.go new file mode 100644 index 000000000..914b9052b --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/gitlabbuildtrigger/gitlabbuildtrigger_validator.go @@ -0,0 +1,90 @@ +package gitlabbuildtrigger + +import ( + "strings" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *GitLabBuildTriggerFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "GitLabBuildTrigger" + + // Make empty errors + errors := []shared.ValidationError{} + + // If build support is off, return false + if fg.FeatureBuildSupport == false { + return errors + } + + // If github trigger is off + if fg.FeatureGitlabBuild == false { + return errors + } + + // Check for config + if fg.GitlabTriggerConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"GITLAB_TRIGGER_CONFIG"}, + FieldGroup: fgName, + Message: "GITLAB_TRIGGER_CONFIG is required for GitLabBuildTrigger", + } + errors = append(errors, newError) + return errors + } + + // Check for endpoint + if fg.GitlabTriggerConfig.GitlabEndpoint == "" { + newError := shared.ValidationError{ + Tags: []string{"GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT"}, + FieldGroup: fgName, + Message: "GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT is required for GitLabBuildTrigger", + } + errors = append(errors, newError) + } + + // Check for endpoint + if !strings.HasPrefix(fg.GitlabTriggerConfig.GitlabEndpoint, "http://") && !strings.HasPrefix(fg.GitlabTriggerConfig.GitlabEndpoint, "https://") { + newError := shared.ValidationError{ + Tags: []string{"GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT"}, + FieldGroup: fgName, + Message: "GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT must be a url", + } + errors = append(errors, newError) + } + + // Check for client id + if fg.GitlabTriggerConfig.ClientId == "" { + newError := shared.ValidationError{ + Tags: []string{"GITLAB_TRIGGER_CONFIG.CLIENT_ID"}, + FieldGroup: fgName, + Message: "GITLAB_TRIGGER_CONFIG.CLIENT_ID is required for GitLabBuildTrigger", + } + errors = append(errors, newError) + } + + // Check for endpoint + if fg.GitlabTriggerConfig.ClientSecret == "" { + newError := shared.ValidationError{ + Tags: []string{"GITLAB_TRIGGER_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "GITLAB_TRIGGER_CONFIG.CLIENT_SECRET is required for GitLabBuildTrigger", + } + errors = append(errors, newError) + } + + // Check OAuth endpoint + success := shared.ValidateGitLabOAuth(fg.GitlabTriggerConfig.ClientId, fg.GitlabTriggerConfig.ClientSecret, fg.GitlabTriggerConfig.GitlabEndpoint) + if !success { + newError := shared.ValidationError{ + Tags: []string{"GITLAB_TRIGGER_CONFIG.CLIENT_ID", "GITLAB_TRIGGER_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "Could not verify GitLab OAuth credentials", + } + errors = append(errors, newError) + } + + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin.go b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin.go new file mode 100644 index 000000000..c7158cabe --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin.go @@ -0,0 +1,63 @@ +package googlelogin + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// GoogleLoginFieldGroup represents the GoogleLoginFieldGroup config fields +type GoogleLoginFieldGroup struct { + FeatureGoogleLogin bool `default:"false" validate:"" json:"FEATURE_GOOGLE_LOGIN" yaml:"FEATURE_GOOGLE_LOGIN"` + GoogleLoginConfig *GoogleLoginConfigStruct `default:"" validate:"" json:"GOOGLE_LOGIN_CONFIG,omitempty" yaml:"GOOGLE_LOGIN_CONFIG,omitempty"` +} + +// GoogleLoginConfigStruct represents the GoogleLoginConfigStruct config fields +type GoogleLoginConfigStruct struct { + ClientSecret string `default:"" validate:"" json:"CLIENT_SECRET,omitempty" yaml:"CLIENT_SECRET,omitempty"` + ClientId string `default:"" validate:"" json:"CLIENT_ID,omitempty" yaml:"CLIENT_ID,omitempty"` +} + +// NewGoogleLoginFieldGroup creates a new GoogleLoginFieldGroup +func NewGoogleLoginFieldGroup(fullConfig map[string]interface{}) (*GoogleLoginFieldGroup, error) { + newGoogleLoginFieldGroup := &GoogleLoginFieldGroup{} + defaults.Set(newGoogleLoginFieldGroup) + + if value, ok := fullConfig["FEATURE_GOOGLE_LOGIN"]; ok { + newGoogleLoginFieldGroup.FeatureGoogleLogin, ok = value.(bool) + if !ok { + return newGoogleLoginFieldGroup, errors.New("FEATURE_GOOGLE_LOGIN must be of type bool") + } + } + if value, ok := fullConfig["GOOGLE_LOGIN_CONFIG"]; ok { + var err error + value := value.(map[string]interface{}) + newGoogleLoginFieldGroup.GoogleLoginConfig, err = NewGoogleLoginConfigStruct(value) + if err != nil { + return newGoogleLoginFieldGroup, err + } + } + + return newGoogleLoginFieldGroup, nil +} + +// NewGoogleLoginConfigStruct creates a new GoogleLoginConfigStruct +func NewGoogleLoginConfigStruct(fullConfig map[string]interface{}) (*GoogleLoginConfigStruct, error) { + newGoogleLoginConfigStruct := &GoogleLoginConfigStruct{} + defaults.Set(newGoogleLoginConfigStruct) + + if value, ok := fullConfig["CLIENT_SECRET"]; ok { + newGoogleLoginConfigStruct.ClientSecret, ok = value.(string) + if !ok { + return newGoogleLoginConfigStruct, errors.New("CLIENT_SECRET must be of type string") + } + } + if value, ok := fullConfig["CLIENT_ID"]; ok { + newGoogleLoginConfigStruct.ClientId, ok = value.(string) + if !ok { + return newGoogleLoginConfigStruct, errors.New("CLIENT_ID must be of type string") + } + } + + return newGoogleLoginConfigStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_fields.go b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_fields.go new file mode 100644 index 000000000..1ea43769a --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_fields.go @@ -0,0 +1,6 @@ +package googlelogin + +// Fields returns a list of strings representing the fields in this field group +func (fg *GoogleLoginFieldGroup) Fields() []string { + return []string{"FEATURE_GOOGLE_LOGIN", "GOOGLE_LOGIN_CONFIG"} +} diff --git a/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_test.go b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_test.go new file mode 100644 index 000000000..51e175992 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_test.go @@ -0,0 +1,59 @@ +package googlelogin + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateGoogleLogin tests the Validate function +func TestValidateGoogleLogin(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "GoogleLoginNotSpecified", config: map[string]interface{}{}, want: "valid"}, + {name: "GoogleLoginOnMissingConfig", config: map[string]interface{}{"FEATURE_GOOGLE_LOGIN": true}, want: "invalid"}, + {name: "GoogleLoginMissingFields", config: map[string]interface{}{"FEATURE_GOOGLE_LOGIN": true, "GOOGLE_LOGIN_CONFIG": map[string]interface{}{}}, want: "invalid"}, + {name: "GoogleLoginBadCredentials", config: map[string]interface{}{"FEATURE_GOOGLE_LOGIN": true, "GOOGLE_LOGIN_CONFIG": map[string]interface{}{"CLIENT_ID": "bad_id", "CLIENT_SECRET": "bad_secret"}}, want: "invalid"}, + {name: "GoogleLoginGoodCredentials", config: map[string]interface{}{"FEATURE_GOOGLE_LOGIN": true, "GOOGLE_LOGIN_CONFIG": map[string]interface{}{"CLIENT_ID": "511815388398-ng379ngbt3ivpno3all76540eh11ebu7.apps.googleusercontent.com", "CLIENT_SECRET": "0mQogdczWFnNemnVp5esDuas"}}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewGoogleLoginFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_validator.go b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_validator.go new file mode 100644 index 000000000..42207aae6 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/googlelogin/googlelogin_validator.go @@ -0,0 +1,62 @@ +package googlelogin + +import ( + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *GoogleLoginFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "GoogleLogin" + + var errors []shared.ValidationError + + // If google login is off, return false + if fg.FeatureGoogleLogin == false { + return errors + } + + // Check for config + if fg.GoogleLoginConfig == nil { + newError := shared.ValidationError{ + Tags: []string{"GOOGLE_LOGIN_CONFIG"}, + FieldGroup: fgName, + Message: "GOOGLE_LOGIN_CONFIG is required for GoogleLogin", + } + errors = append(errors, newError) + return errors + } + + // Check for client id + if fg.GoogleLoginConfig.ClientId == "" { + newError := shared.ValidationError{ + Tags: []string{"GOOGLE_LOGIN_CONFIG.CLIENT_ID"}, + FieldGroup: fgName, + Message: "GOOGLE_LOGIN_CONFIG.CLIENT_ID is required for GoogleLogin", + } + errors = append(errors, newError) + } + + // Check for endpoint + if fg.GoogleLoginConfig.ClientSecret == "" { + newError := shared.ValidationError{ + Tags: []string{"GOOGLE_LOGIN_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "GOOGLE_LOGIN_CONFIG.CLIENT_SECRET is required for GoogleLogin", + } + errors = append(errors, newError) + } + + // Check OAuth endpoint + success := shared.ValidateGoogleOAuth(fg.GoogleLoginConfig.ClientId, fg.GoogleLoginConfig.ClientSecret) + if !success { + newError := shared.ValidationError{ + Tags: []string{"GOOGLE_LOGIN_CONFIG.CLIENT_ID", "GOOGLE_LOGIN_CONFIG.CLIENT_SECRET"}, + FieldGroup: fgName, + Message: "Could not verify Google OAuth credentials", + } + errors = append(errors, newError) + } + + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings.go b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings.go new file mode 100644 index 000000000..7a9cbb0e5 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings.go @@ -0,0 +1,41 @@ +package hostsettings + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// HostSettingsFieldGroup represents the HostSettingsFieldGroup config fields +type HostSettingsFieldGroup struct { + ExternalTlsTermination bool `default:"false" validate:"" json:"EXTERNAL_TLS_TERMINATION" yaml:"EXTERNAL_TLS_TERMINATION"` + PreferredUrlScheme string `default:"http" validate:"" json:"PREFERRED_URL_SCHEME,omitempty" yaml:"PREFERRED_URL_SCHEME,omitempty"` + ServerHostname string `default:"" validate:"" json:"SERVER_HOSTNAME,omitempty" yaml:"SERVER_HOSTNAME,omitempty"` +} + +// NewHostSettingsFieldGroup creates a new HostSettingsFieldGroup +func NewHostSettingsFieldGroup(fullConfig map[string]interface{}) (*HostSettingsFieldGroup, error) { + newHostSettingsFieldGroup := &HostSettingsFieldGroup{} + defaults.Set(newHostSettingsFieldGroup) + + if value, ok := fullConfig["EXTERNAL_TLS_TERMINATION"]; ok { + newHostSettingsFieldGroup.ExternalTlsTermination, ok = value.(bool) + if !ok { + return newHostSettingsFieldGroup, errors.New("EXTERNAL_TLS_TERMINATION must be of type bool") + } + } + if value, ok := fullConfig["PREFERRED_URL_SCHEME"]; ok { + newHostSettingsFieldGroup.PreferredUrlScheme, ok = value.(string) + if !ok { + return newHostSettingsFieldGroup, errors.New("PREFERRED_URL_SCHEME must be of type string") + } + } + if value, ok := fullConfig["SERVER_HOSTNAME"]; ok { + newHostSettingsFieldGroup.ServerHostname, ok = value.(string) + if !ok { + return newHostSettingsFieldGroup, errors.New("SERVER_HOSTNAME must be of type string") + } + } + + return newHostSettingsFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_fields.go b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_fields.go new file mode 100644 index 000000000..4548abd9f --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_fields.go @@ -0,0 +1,6 @@ +package hostsettings + +// Fields returns a list of strings representing the fields in this field group +func (fg *HostSettingsFieldGroup) Fields() []string { + return []string{"EXTERNAL_TLS_TERMINATION", "PREFERRED_URL_SCHEME", "SERVER_HOSTNAME"} +} diff --git a/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_test.go b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_test.go new file mode 100644 index 000000000..1d863fed5 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_test.go @@ -0,0 +1,197 @@ +package hostsettings + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateHostSettings tests the Validate function +func TestValidateHostSettings(t *testing.T) { + + validOpts := shared.Options{ + Mode: "testing", + Certificates: map[string][]byte{ + "ssl.key": []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAv1iWzjlGIxtjAJ/1oDZvWQQDyAMBFpy79/A5VbeIBDpcv/Ve +PuaG8oTYCYAktSRuwAYbiTxN0K7HZ2njAdY4BxyD3yCNLQF+KVn3Q/eYSZh3OJLO +EXgcQvEzopiridg975rzm3ksTtOIlLOneUSj6ONinqOpkaYe+D8p+ANHv7YN7s5V +qQnxJn6cSixOigT9zUGEyOHeyfyIcZLfmLxpCw0t5QfjCTFLShTz0V4hWajfOp3z +bvl4O8YzvfUKYO72JHioqa75dwh0eBtJM12jckbHh9JYx/EoV//zaNxP2qrYEvtB +uStgTST84rTzx+xrkiaWfGxs/Moeme8cPy5zfQIDAQABAoIBAGSCa0zOJvpf82Qr +ogFTNrACfN3+Pf8bu1zkkall64uVAI1QnP3bZ71SbIypBB8mkQpK6wHubE2W0WWP +6E9ZsDqEDv0QgzfF1fhwqoLINvVJoi5UZuwkNGwxeNcK7OhOb1JCCX58avrJALBj +ojAADz1Q28fK3lKEeTYbL7d4OaMIXMXMu1036+Exr8N1R5R6mXlb4vLIWUfnersJ +GDqC5qkFhkODcDwULrftrxij2dvOPamJlCLpduXfLDhZ3UQqBhaS4dr0saLfgYBF +16a4NE+pPMCC9wFOUo1JNYiY7ME/ntqLhmLL0sWc7gwQUOFDGbVkTNYJ6qMBjx/6 +ZlTaSd0CgYEA4EU7a76WFQbTkGauoy/ihKKmsX+jAxNy5EeoI6XPEWx9vGz4rJzi +TXLEQAiTcgX3IhLa7dAs5iade+Hk/WyKZbkmJRhmxfn4RLj/u+WYs3WC6hU9hyvK +m9Cxh623+TJC+Y+jJlKlaGCxls0eBCgRL7m/PG56vyL/ERaOc7Fr2o8CgYEA2mrh +fHGygPtMtx6Q1xpBETZnXN7Vx9kvlqfCCFEb3vLWGEpskl0TWkpPQmnE416rdYOf +X78bJUjoqO5GIaC3NgZQKxMrp9XJWfrTxp6r68Wa1cSA4LgFHZ3gtGbJbNq3z3IR +A9jKU9t310KCscVpHx9AMne6v5g/LfRXQ4zIBzMCgYAvt4tFCW/1WVZ6St6taerQ +Pasp6PZOGT1AxN5Jd2XvVx4JkUX3tAmSYPDQjwKQKCTE4y4hm0FyVpT7XrzSDt4D +drle+yoixWTFencvC1LKHB6Wn55PvEmHjYe4ToXuR3tojd8wsDTxWGFwrIPObpf5 +h5Pgz8DeGhwbDqmQhBdmkQKBgQC4VxyX+x283luQ8ass4GuqK1BxgWDMmvEfJdcN +TedH84veVHHt1cBPpAfg9YPGok/zjnMkTBaNEUvLx85I82utnQZsVHGz5StbVecG +60QOaWiUopRjFOy8YlMT7uxxguc/nfXeWUnqHIC4nNnRT9u4+JcmAQcMTWKFVoOP +73GjIQKBgQC96LM1Vr4/FRb3OiaULsefr9VrVw/XOTsLLVgfspUI6jmrxpjxGNxz +FBoETbL3x6nPf1jv6c8rL6rdflF7Bj1Qduw+K/MiOGtGHVcycgQLr75JZXTZd6ZG +xZrZ1RNMio7dF5BCORdZQP9bO45+GwcKf551AJbm7Go0CxL+ULL/AQ== +-----END RSA PRIVATE KEY-----`), + "ssl.cert": []byte(`-----BEGIN CERTIFICATE----- +MIIDUzCCAjsCFFsU6n8cHfw0saeTp+atDOWXDzicMA0GCSqGSIb3DQEBCwUAMGYx +CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOSjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRUwEwYDVQQDDAxmYWtlaG9z +dC5jb20wHhcNMjAwODEwMTY0MTM4WhcNMjExMjIzMTY0MTM4WjBmMQswCQYDVQQG +EwJVUzELMAkGA1UECAwCTkoxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDEVMBMGA1UEAwwMZmFrZWhvc3QuY29tMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1iWzjlGIxtjAJ/1oDZvWQQD +yAMBFpy79/A5VbeIBDpcv/VePuaG8oTYCYAktSRuwAYbiTxN0K7HZ2njAdY4BxyD +3yCNLQF+KVn3Q/eYSZh3OJLOEXgcQvEzopiridg975rzm3ksTtOIlLOneUSj6ONi +nqOpkaYe+D8p+ANHv7YN7s5VqQnxJn6cSixOigT9zUGEyOHeyfyIcZLfmLxpCw0t +5QfjCTFLShTz0V4hWajfOp3zbvl4O8YzvfUKYO72JHioqa75dwh0eBtJM12jckbH +h9JYx/EoV//zaNxP2qrYEvtBuStgTST84rTzx+xrkiaWfGxs/Moeme8cPy5zfQID +AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfinJu6bpCiCkSsbMv2dmZltwFroHbUzRE +bFuUGmqsOSm/3B3UqH5Vp7bm4RhHuinAwtb6FzcVQQY2jYQGsvvpjuh8fWC1Rr6L +/AcGC0ihUZv9towdLQbDuQZDnxgbMBZbh1OIAeN2JvKz6yGvmzSPdSYr8rr6nvDO +6ZTU0IGmctm4YXXtq5wnPZw40GRTNxLP/dRjz30dH4cikt+r/W/kefT5Ue6eQ5da +bWD+dzQSYs0v6rDp+e/V39maCcNjeFuoK2Wq5ldhKdzAZWmVH3Lx2ION/6Fr4iL5 +WHgsCLsLspQZqpi5o9pKqo1WH8b+uaqm5LDQvvrk3kkQLB/M5VMQ +-----END CERTIFICATE-----`), + }, + } + + missingCertsOpts := shared.Options{} + + invalidCertPairOpts := shared.Options{ + Mode: "testing", + Certificates: map[string][]byte{ + "ssl.key": []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAv1iWzjlGIxtjAJ/1oDZvWQQDyAMBFpy79/A5VbeIBDpcv/Ve +PuaG8oTYCYAktSRuwAYbiTxN0K7HZ2njAdY4BxyD3yCNLQF+KVn3Q/eYSZh3OJLO +EXgcQvEzopiridg975rzm3ksTtOIlLOneUSj6ONinqOpkaYe+D8p+ANHv7YN7s5V +qQnxJn6cSixOigT9zUGEyOHeyfyIcZLfmLxpCw0t5QfjCTFLShTz0V4hWajfOp3z +bvl4O8YzvfUKYO72JHioqa75dwh0eBtJM12jckbHh9JYx/EoV//zaNxP2qrYEvtB +uStgTST84rTzx+xrkiaWfGxs/Moeme8cPy5zfQIDAQABAoIBAGSCa0zOJvpf82Qr +ogFTNrACfN3+Pf8bu1zkkall64uVAI1QnP3bZ71SbIypBB8mkQpK6wHubE2W0WWP +6E9ZsDqEDv0QgzfF1fhwqoLINvVJoi5UZuwkNGwxeNcK7OhOb1JCCX58avrJALBj +ojAADz1Q28fK3lKEeTYbL7d4OaMIXMXMu1036+Exr8N1R5R6mXlb4vLIWUfnersJ +GDqC5qkFhkODcDwULrftrxij2dvOPamJlCLpduXfLDhZ3UQqBhaS4dr0saLfgYBF +16a4NE+pPMCC9wFOUo1JNYiY7ME/ntqLhmLL0sWc7gwQUOFDGbVkTNYJ6qMBjx/6 +ZlTaSd0CgYEA4EU7a76WFQbTkGauoy/ihKKmsX+jAxNy5EeoI6XPEWx9vGz4rJzi +TXLEQAiTcgX3IhLa7dAs5iade+Hk/WyKZbkmJRhmxfn4RLj/u+WYs3WC6hU9hyvK +m9Cxh623+TJC+Y+jJlKlaGCxls0eBCgRL7m/PG56vyL/ERaOc7Fr2o8CgYEA2mrh +fHGygPtMtx6Q1xpBETZnXN7Vx9kvlqfCCFEb3vLWGEpskl0TWkpPQmnE416rdYOf +X78bJUjoqO5GIaC3NgZQKxMrp9XJWfrTxp6r68Wa1cSA4LgFHZ3gtGbJbNq3z3IR +A9jKU9t310KCscVpHx9AMne6v5g/LfRXQ4zIBzMCgYAvt4tFCW/1WVZ6St6taerQ +Pasp6PZOGT1AxN5Jd2XvVx4JkUX3tAmSYPDQjwKQKCTE4y4hm0FyVpT7XrzSDt4D +drle+yoixWTFencvC1LKHB6Wn55PvEmHjYe4ToXuR3tojd8wsDTxWGFwrIPObpf5 +h5Pgz8DeGhwbDqmQhBdmkQKBgQC4VxyX+x283luQ8ass4GuqK1BxgWDMmvEfJdcN +TedH84veVHHt1cBPpAfg9YPGok/zjnMkTBaNEUvLx85I82utnQZsVHGz5StbVecG +60QOaWiUopRjFOy8YlMT7uxxguc/nfXeWUnqHIC4nNnRT9u4+JcmAQcMTWKFVoOP +73GjIQKBgQC96LM1Vr4/FRb3OiaULsefr9VrVw/XOTsLLVgfspUI6jmrxpjxGNxz +FBoETbL3x6nPf1jv6c8rL6rdflF7Bj1Qduw+K/MiOGtGHVcycgQLr75JZXTZd6ZG +xZrZ1RNMio7dF5BCORdZQP9bO45+GwcKf551AJbm7Go0CxL+ULL/AQ== +-----END RSA PRIVATE KEY-----`), + "ssl.cert": []byte(`-----BEGIN CERTIFICATE----- +MIIDUzCCAjsCFFsU6n8cHfw0saeTp+atDOWXDzicMA0GCSqGSIb3DQEBCwUAMGYx +CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOSjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRUwEwYDVQQDDAxmYWtlaG9z +dC5jb20wHhcNMjAwODEwMTY0MTM4WhcNMjExMjIzMTY0MTM4WjBmMQswCQYDVQQG +EwJVUzELMAkGA1UECAwCTkoxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDEVMBMGA1UEAwwMZmFrZWhvc3QuY29tMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1iWzjlGIxtjAJ/1oDZvWQQD +yAMBFpy79/A5VbeIBDpcv/VePuaG8oTYCYAktSRuwAYbiTxN0K7HZ2njAdY4BxyD +3yCNLQF+KVn3Q/eYSZh3OJLOEXgcQvEzopiridg975rzm3ksTtOIlLOneUSj6ONi +nqOpkaYe+D8p+ANHv7YN7s5VqQnxJn6cSixOigT9zUGEyOHeyfyIcZLfmLxpCw0t +5QfjCTFLShTz0V4hWajfOp3zbvl4O8YzvfUKYO72JHioqa75dwh0eBtJM12jckbH +h9JYx/EoV//zaNxP2qrYEvtBuStgTST84rTzx+xrkiaWfGxs/Moeme8cPy5zfQID +AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfinJu6bpCiCkSsbMv2dmZltwFroHbUzRE +bFuUGmqsOSm/3B3UqH5Vp7bm4RhHuinAwtb6FzcVQQY2jYQGsvvpjuh8fWC1Rr6L +/AcGC0ihUZv9towdLQbDuQZDnxgbMBZbh1OIAeN2JvKz6yGvmzSPdSYr8rr6nvDO +6ZTU0IGmctm4YXXtq5wnPZw40GRTNxLP/dRjz30dH4cikt+r/W/kefT5Ue6eQ5da +bWD+dzQSYs0v6rDp+e/V39maCcNjeFuoK2Wq5ldhKdzAZWmVH3Lx2ION/6Fr4iL5 +WHgsCLsLspQZqpi5o9pKqo1WH8b+uaqm5LDQvvrk3kkQLB/M5VMI +-----END CERTIFICATE-----`), + }, + } + + var tests = []struct { + name string + config map[string]interface{} + opts shared.Options + want string + }{ + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "badURLScheme"}, + name: "BadURLScheme", + want: "invalid", + }, + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "http", "SERVER_HOSTNAME": "fakehost.com"}, + name: "GoodScheme", + want: "valid", + }, + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "https", "SERVER_HOSTNAME": "fakehost.com", "EXTERNAL_TLS_TERMINATION": true}, + name: "httpsTLSTermination", + want: "valid", + }, + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "https", "SERVER_HOSTNAME": "fakehost.com"}, + name: "missingCerts", + want: "invalid", + opts: missingCertsOpts, + }, + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "https", "SERVER_HOSTNAME": "fakehost.com"}, + name: "invalidCertPair->FIX_THIS_TEST", + want: "invalid", + opts: invalidCertPairOpts, + }, + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "https", "SERVER_HOSTNAME": "wronghost.com"}, + name: "wrongHostname", + want: "invalid", + opts: validOpts, + }, + { + config: map[string]interface{}{"PREFERRED_URL_SCHEME": "https", "SERVER_HOSTNAME": "fakehost.com"}, + name: "certPairAndHostnameWithLegacyCN", + want: "invalid", + opts: validOpts, + }, + } + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewHostSettingsFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + validationErrors := fg.Validate(tt.opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + for _, e := range validationErrors { + t.Log(e) + } + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_validator.go b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_validator.go new file mode 100644 index 000000000..e5ab76969 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/hostsettings/hostsettings_validator.go @@ -0,0 +1,44 @@ +package hostsettings + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *HostSettingsFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // check that hostname is present + if ok, err := shared.ValidateRequiredString(fg.ServerHostname, "SERVER_HOSTNAME", "HostSettings"); !ok { + errors = append(errors, err) + } + + // check that hostname is url + if ok, err := shared.ValidateIsHostname(fg.ServerHostname, "SERVER_HOSTNAME", "HostSettings"); !ok { + errors = append(errors, err) + } + + // Check that url scheme is one of http or https + if ok, err := shared.ValidateIsOneOfString(fg.PreferredUrlScheme, []string{"http", "https"}, "PREFERRED_URL_SCHEME", "HostSettings"); !ok { + errors = append(errors, err) + } + + // // If SSL is enabled + if fg.PreferredUrlScheme == "https" && !fg.ExternalTlsTermination { + + // Validate certs are present + if ok, err := shared.ValidateCertsPresent(opts, []string{"ssl.cert", "ssl.key"}, "HostSettings"); !ok { + errors = append(errors, err) + return errors + } + + // Validate cert pair and hostname + if ok, err := shared.ValidateCertPairWithHostname(opts.Certificates["ssl.cert"], opts.Certificates["ssl.key"], fg.ServerHostname, "HostSettigns"); !ok { + errors = append(errors, err) + return errors + } + + } + + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication.go b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication.go new file mode 100644 index 000000000..872ce2a5e --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication.go @@ -0,0 +1,62 @@ +package jwtauthentication + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// JWTAuthenticationFieldGroup represents the JWTAuthenticationFieldGroup config fields +type JWTAuthenticationFieldGroup struct { + AuthenticationType string `default:"Database" validate:"" json:"AUTHENTICATION_TYPE,omitempty" yaml:"AUTHENTICATION_TYPE,omitempty"` + FeatureMailing bool `default:"false" validate:"" json:"FEATURE_MAILING" yaml:"FEATURE_MAILING"` + JwtAuthIssuer string `default:"" validate:"" json:"JWT_AUTH_ISSUER,omitempty" yaml:"JWT_AUTH_ISSUER,omitempty"` + JwtGetuserEndpoint string `default:"" validate:"" json:"JWT_GETUSER_ENDPOINT,omitempty" yaml:"JWT_GETUSER_ENDPOINT,omitempty"` + JwtQueryEndpoint string `default:"" validate:"" json:"JWT_QUERY_ENDPOINT,omitempty" yaml:"JWT_QUERY_ENDPOINT,omitempty"` + JwtVerifyEndpoint string `default:"" validate:"" json:"JWT_VERIFY_ENDPOINT,omitempty" yaml:"JWT_VERIFY_ENDPOINT,omitempty"` +} + +// NewJWTAuthenticationFieldGroup creates a new JWTAuthenticationFieldGroup +func NewJWTAuthenticationFieldGroup(fullConfig map[string]interface{}) (*JWTAuthenticationFieldGroup, error) { + newJWTAuthenticationFieldGroup := &JWTAuthenticationFieldGroup{} + defaults.Set(newJWTAuthenticationFieldGroup) + + if value, ok := fullConfig["AUTHENTICATION_TYPE"]; ok { + newJWTAuthenticationFieldGroup.AuthenticationType, ok = value.(string) + if !ok { + return newJWTAuthenticationFieldGroup, errors.New("AUTHENTICATION_TYPE must be of type string") + } + } + if value, ok := fullConfig["FEATURE_MAILING"]; ok { + newJWTAuthenticationFieldGroup.FeatureMailing, ok = value.(bool) + if !ok { + return newJWTAuthenticationFieldGroup, errors.New("FEATURE_MAILING must be of type bool") + } + } + if value, ok := fullConfig["JWT_AUTH_ISSUER"]; ok { + newJWTAuthenticationFieldGroup.JwtAuthIssuer, ok = value.(string) + if !ok { + return newJWTAuthenticationFieldGroup, errors.New("JWT_AUTH_ISSUER must be of type string") + } + } + if value, ok := fullConfig["JWT_GETUSER_ENDPOINT"]; ok { + newJWTAuthenticationFieldGroup.JwtGetuserEndpoint, ok = value.(string) + if !ok { + return newJWTAuthenticationFieldGroup, errors.New("JWT_GETUSER_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["JWT_QUERY_ENDPOINT"]; ok { + newJWTAuthenticationFieldGroup.JwtQueryEndpoint, ok = value.(string) + if !ok { + return newJWTAuthenticationFieldGroup, errors.New("JWT_QUERY_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["JWT_VERIFY_ENDPOINT"]; ok { + newJWTAuthenticationFieldGroup.JwtVerifyEndpoint, ok = value.(string) + if !ok { + return newJWTAuthenticationFieldGroup, errors.New("JWT_VERIFY_ENDPOINT must be of type string") + } + } + + return newJWTAuthenticationFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_fields.go b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_fields.go new file mode 100644 index 000000000..00121dcbe --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_fields.go @@ -0,0 +1,6 @@ +package jwtauthentication + +// Fields returns a list of strings representing the fields in this field group +func (fg *JWTAuthenticationFieldGroup) Fields() []string { + return []string{"AUTHENTICATION_TYPE", "FEATURE_MAILING", "JWT_AUTH_ISSUER", "JWT_GETUSER_ENDPOINT", "JWT_QUERY_ENDPOINT", "JWT_VERIFY_ENDPOINT"} +} diff --git a/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_test.go b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_test.go new file mode 100644 index 000000000..f23328453 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_test.go @@ -0,0 +1,59 @@ +package jwtauthentication + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateJWTAuthentication tests the Validate function +func TestValidateJWTAuthentication(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "WrongAuthType", config: map[string]interface{}{"AUTHENTICATION_TYPE": "Database"}, want: "valid"}, + {name: "MissingVerifyEndpoint", config: map[string]interface{}{"AUTHENTICATION_TYPE": "JWT"}, want: "invalid"}, + {name: "VerifyEndpointGood", config: map[string]interface{}{"AUTHENTICATION_TYPE": "JWT", "JWT_AUTH_ISSUER": "one", "JWT_VERIFY_ENDPOINT": "https://google.com"}, want: "valid"}, + {name: "VerifyEndpointBad", config: map[string]interface{}{"AUTHENTICATION_TYPE": "JWT", "JWT_AUTH_ISSUER": "one", "JWT_VERIFY_ENDPOINT": "notagoodendpoint"}, want: "invalid"}, + {name: "MissingAuthIssuer", config: map[string]interface{}{"AUTHENTICATION_TYPE": "JWT", "JWT_VERIFY_ENDPOINT": "https://google.com"}, want: "invalid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewJWTAuthenticationFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_validator.go b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_validator.go new file mode 100644 index 000000000..8b2e7242e --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/jwtauthentication/jwtauthentication_validator.go @@ -0,0 +1,76 @@ +package jwtauthentication + +import ( + "strings" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *JWTAuthenticationFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "JWTAuthentication" + + var errors []shared.ValidationError + + // If auth type is not JWT, return + if fg.AuthenticationType != "JWT" { + return errors + } + + // Check for verify endpoint + if fg.JwtVerifyEndpoint == "" { + newError := shared.ValidationError{ + Tags: []string{"JWT_VERIFY_ENDPOINT"}, + FieldGroup: fgName, + Message: "JWT_VERIFY_ENDPOINT is required", + } + errors = append(errors, newError) + } + // Check verify endpoint has right form endpoint + if !strings.HasPrefix(fg.JwtVerifyEndpoint, "http://") && !strings.HasPrefix(fg.JwtVerifyEndpoint, "https://") { + newError := shared.ValidationError{ + Tags: []string{"JWT_VERIFY_ENDPOINT"}, + FieldGroup: fgName, + Message: "JWT_VERIFY_ENDPOINT must be a url", + } + errors = append(errors, newError) + } + + // If get user endpoint, make sure it is right form + if fg.JwtGetuserEndpoint != "" { + if !strings.HasPrefix(fg.JwtGetuserEndpoint, "http://") && !strings.HasPrefix(fg.JwtGetuserEndpoint, "https://") { + newError := shared.ValidationError{ + Tags: []string{"JWT_GETUSER_ENDPOINT"}, + FieldGroup: fgName, + Message: "JWT_GETUSER_ENDPOINT must be a url", + } + errors = append(errors, newError) + } + } + + // If get user endpoint, make sure it is right form + if fg.JwtQueryEndpoint != "" { + if !strings.HasPrefix(fg.JwtQueryEndpoint, "http://") && !strings.HasPrefix(fg.JwtQueryEndpoint, "https://") { + newError := shared.ValidationError{ + Tags: []string{"JWT_QUERY_ENDPOINT"}, + FieldGroup: fgName, + Message: "JWT_QUERY_ENDPOINT must be a url", + } + errors = append(errors, newError) + } + } + + // Check for config + if fg.JwtAuthIssuer == "" { + newError := shared.ValidationError{ + Tags: []string{"JWT_AUTH_ISSUER"}, + FieldGroup: fgName, + Message: "JWT_AUTH_ISSUER is required for JWT", + } + errors = append(errors, newError) + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/ldap/ldap.go b/config-tool/pkg/lib/fieldgroups/ldap/ldap.go new file mode 100644 index 000000000..6a440ec27 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/ldap/ldap.go @@ -0,0 +1,91 @@ +package ldap + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// LDAPFieldGroup represents the LDAPFieldGroup config fields +type LDAPFieldGroup struct { + AuthenticationType string `default:"Database" validate:"" json:"AUTHENTICATION_TYPE,omitempty" yaml:"AUTHENTICATION_TYPE,omitempty"` + LdapAdminDn string `default:"" validate:"" json:"LDAP_ADMIN_DN,omitempty" yaml:"LDAP_ADMIN_DN,omitempty"` + LdapAdminPasswd string `default:"" validate:"" json:"LDAP_ADMIN_PASSWD,omitempty" yaml:"LDAP_ADMIN_PASSWD,omitempty"` + LdapAllowInsecureFallback bool `default:"false" validate:"" json:"LDAP_ALLOW_INSECURE_FALLBACK" yaml:"LDAP_ALLOW_INSECURE_FALLBACK"` + LdapBaseDn []interface{} `default:"" validate:"" json:"LDAP_BASE_DN,omitempty" yaml:"LDAP_BASE_DN,omitempty"` + LdapEmailAttr string `default:"mail" validate:"" json:"LDAP_EMAIL_ATTR,omitempty" yaml:"LDAP_EMAIL_ATTR,omitempty"` + LdapUidAttr string `default:"uid" validate:"" json:"LDAP_UID_ATTR,omitempty" yaml:"LDAP_UID_ATTR,omitempty"` + LdapUri string `default:"ldap://localhost" validate:"" json:"LDAP_URI,omitempty" yaml:"LDAP_URI,omitempty"` + LdapUserFilter string `default:"" validate:"" json:"LDAP_USER_FILTER,omitempty" yaml:"LDAP_USER_FILTER,omitempty"` + LdapUserRdn []interface{} `default:"[]" validate:"" json:"LDAP_USER_RDN,omitempty" yaml:"LDAP_USER_RDN,omitempty"` +} + +// NewLDAPFieldGroup creates a new LDAPFieldGroup +func NewLDAPFieldGroup(fullConfig map[string]interface{}) (*LDAPFieldGroup, error) { + newLDAPFieldGroup := &LDAPFieldGroup{} + defaults.Set(newLDAPFieldGroup) + + if value, ok := fullConfig["AUTHENTICATION_TYPE"]; ok { + newLDAPFieldGroup.AuthenticationType, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("AUTHENTICATION_TYPE must be of type string") + } + } + + if value, ok := fullConfig["LDAP_ADMIN_DN"]; ok { + newLDAPFieldGroup.LdapAdminDn, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_ADMIN_DN must be of type string") + } + } + if value, ok := fullConfig["LDAP_ADMIN_PASSWD"]; ok { + newLDAPFieldGroup.LdapAdminPasswd, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_ADMIN_PASSWD must be of type string") + } + } + if value, ok := fullConfig["LDAP_ALLOW_INSECURE_FALLBACK"]; ok { + newLDAPFieldGroup.LdapAllowInsecureFallback, ok = value.(bool) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_ALLOW_INSECURE_FALLBACK must be of type bool") + } + } + if value, ok := fullConfig["LDAP_BASE_DN"]; ok { + newLDAPFieldGroup.LdapBaseDn, ok = value.([]interface{}) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_BASE_DN must be of type array") + } + } + if value, ok := fullConfig["LDAP_EMAIL_ATTR"]; ok { + newLDAPFieldGroup.LdapEmailAttr, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_EMAIL_ATTR must be of type string") + } + } + if value, ok := fullConfig["LDAP_UID_ATTR"]; ok { + newLDAPFieldGroup.LdapUidAttr, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_UID_ATTR must be of type string") + } + } + if value, ok := fullConfig["LDAP_URI"]; ok { + newLDAPFieldGroup.LdapUri, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_URI must be of type string") + } + } + if value, ok := fullConfig["LDAP_USER_FILTER"]; ok { + newLDAPFieldGroup.LdapUserFilter, ok = value.(string) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_USER_FILTER must be of type string") + } + } + if value, ok := fullConfig["LDAP_USER_RDN"]; ok { + newLDAPFieldGroup.LdapUserRdn, ok = value.([]interface{}) + if !ok { + return newLDAPFieldGroup, errors.New("LDAP_USER_RDN must be of type []interface{}") + } + } + + return newLDAPFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/ldap/ldap_fields.go b/config-tool/pkg/lib/fieldgroups/ldap/ldap_fields.go new file mode 100644 index 000000000..60adae80b --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/ldap/ldap_fields.go @@ -0,0 +1,6 @@ +package ldap + +// Fields returns a list of strings representing the fields in this field group +func (fg *LDAPFieldGroup) Fields() []string { + return []string{"LDAP_ADMIN_DN", "LDAP_ADMIN_PASSWD", "LDAP_ALLOW_INSECURE_FALLBACK", "LDAP_BASE_DN", "LDAP_EMAIL_ATTR", "LDAP_UID_ATTR", "LDAP_URI", "LDAP_USER_FILTER", "LDAP_USER_RDN"} +} diff --git a/config-tool/pkg/lib/fieldgroups/ldap/ldap_test.go b/config-tool/pkg/lib/fieldgroups/ldap/ldap_test.go new file mode 100644 index 000000000..c96c514ab --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/ldap/ldap_test.go @@ -0,0 +1,97 @@ +package ldap + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateLDAP tests the Validate function +func TestValidateLDAP(t *testing.T) { + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + { + name: "wrongAuthType", + config: map[string]interface{}{"AUTHENTICATION_TYPE": "Database"}, + want: "valid", + }, + { + name: "noValidAuthSettings", + config: map[string]interface{}{"AUTHENTICATION_TYPE": "LDAP"}, + want: "invalid", + }, + { + name: "validAuthSettings", + config: map[string]interface{}{"AUTHENTICATION_TYPE": "LDAP", + "LDAP_URI": "ldap://ldap.forumsys.com", + "LDAP_BASE_DN": []interface{}{"dc=example", "dc=com"}, + "LDAP_ADMIN_DN": "cn=read-only-admin,dc=example,dc=com", + "LDAP_ADMIN_PASSWD": "password", + "LDAP_USER_RDN": []interface{}{}, + "LDAP_USER_FILTER": "(uid=newton)", + }, + want: "valid", + }, + { + name: "invalidPassword", + config: map[string]interface{}{"AUTHENTICATION_TYPE": "LDAP", + "LDAP_URI": "ldap://ldap.forumsys.com", + "LDAP_BASE_DN": []interface{}{"dc=example", "dc=com"}, + "LDAP_ADMIN_DN": "cn=read-only-admin,dc=example,dc=com", + "LDAP_ADMIN_PASSWD": "passwo", + "LDAP_USER_RDN": []interface{}{}, + "LDAP_USER_FILTER": "(CN=hey)", + }, + want: "invalid", + }, + { + name: "userExists", + config: map[string]interface{}{"AUTHENTICATION_TYPE": "LDAP", + "LDAP_URI": "ldap://ldap.forumsys.com", + "LDAP_BASE_DN": []interface{}{"dc=example", "dc=com"}, + "LDAP_ADMIN_DN": "cn=read-only-admin,dc=example,dc=com", + "LDAP_ADMIN_PASSWD": "password", + "LDAP_USER_RDN": []interface{}{}, + "LDAP_USER_FILTER": "", + }, + want: "valid", + }, + } + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewLDAPFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{} + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + for _, e := range validationErrors { + t.Log(e) + } + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + + } +} diff --git a/config-tool/pkg/lib/fieldgroups/ldap/ldap_validator.go b/config-tool/pkg/lib/fieldgroups/ldap/ldap_validator.go new file mode 100644 index 000000000..4b74b10d3 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/ldap/ldap_validator.go @@ -0,0 +1,55 @@ +package ldap + +import ( + "net/url" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *LDAPFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "LDAP" + + // Make empty errors + errors := []shared.ValidationError{} + + // Check authentication type + if fg.AuthenticationType != "LDAP" { + return errors + } + + // check that admin dn is present + if ok, err := shared.ValidateRequiredString(fg.LdapAdminDn, "LDAP_ADMIN_DN", fgName); !ok { + errors = append(errors, err) + } + + // check that admin pass is present + if ok, err := shared.ValidateRequiredString(fg.LdapAdminPasswd, "LDAP_ADMIN_PASSWD", fgName); !ok { + errors = append(errors, err) + } + + // Parse url + uri, err := url.Parse(fg.LdapUri) + if err != nil { + newError := shared.ValidationError{ + Tags: []string{"LDAP"}, + FieldGroup: fgName, + Message: err.Error(), + } + errors = append(errors, newError) + return errors + } + + if ok, err := shared.ValidateIsOneOfString(uri.Scheme, []string{"ldap", "ldaps"}, "LDAP_URI", fgName); !ok { + errors = append(errors, err) + return errors + } + + if ok, err := shared.ValidateLDAPServer(opts, fg.LdapUri, fg.LdapAdminDn, fg.LdapAdminPasswd, fg.LdapUidAttr, fg.LdapEmailAttr, fg.LdapUserFilter, fg.LdapBaseDn, fgName); !ok { + errors = append(errors, err) + return errors + } + + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/oidc/oidc.go b/config-tool/pkg/lib/fieldgroups/oidc/oidc.go new file mode 100644 index 000000000..682d6f3de --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/oidc/oidc.go @@ -0,0 +1,105 @@ +package oidc + +import ( + "errors" + "strings" + + "github.com/creasty/defaults" +) + +// OIDC represents the OIDC config fields +type OIDCFieldGroup struct { + OIDCProviders []*OIDCProvider `default:"[]" validate:"" json:"-" yaml:"-"` +} + +type OIDCProvider struct { + _Prefix string `default:"" validate:"" json:"-" yaml:"-"` + OIDCServer string `default:"" validate:"" json:"OIDC_SERVER,omitempty" yaml:"OIDC_SERVER,omitempty"` + ClientID string `default:"" validate:"" json:"CLIENT_ID,omitempty" yaml:"CLIENT_ID,omitempty"` + ClientSecret string `default:"" validate:"" json:"CLIENT_SECRET,omitempty" yaml:"CLIENT_SECRET,omitempty"` + ServiceIcon string `default:"" validate:"" json:"SERVICE_ICON,omitempty" yaml:"SERVICE_ICON,omitempty"` + VerifiedEmailClaimName string `default:"" validate:"" json:"VERIFIED_EMAIL_CLAIM_NAME,omitempty" yaml:"VERIFIED_EMAIL_CLAIM_NAME,omitempty"` + PreferredUsernameClaimName string `default:"" validate:"" json:"PREFERRED_USERNAME_CLAIM_NAME,omitempty" yaml:"PREFERRED_USERNAME_CLAIM_NAME,omitempty"` + LoginScopes []interface{} `default:"" validate:"" json:"LOGIN_SCOPES,omitempty" yaml:"LOGIN_SCOPES,omitempty"` + ServiceName string `default:"" validate:"" json:"SERVICE_NAME,omitempty" yaml:"SERVICE_NAME,omitempty"` +} + +// NewOIDCFieldGroup creates a new OIDCFieldGroup +func NewOIDCFieldGroup(fullConfig map[string]interface{}) (*OIDCFieldGroup, error) { + newOIDCFieldGroup := &OIDCFieldGroup{} + defaults.Set(newOIDCFieldGroup) + + for key, value := range fullConfig { + if providerConf, ok := value.(map[string]interface{}); ok { + if strings.HasSuffix(key, "_LOGIN_CONFIG") && key != "GOOGLE_LOGIN_CONFIG" && key != "GITHUB_LOGIN_CONFIG" { + prefix := strings.TrimSuffix(key, "_LOGIN_CONFIG") + newProvider, err := NewOIDCProvider(prefix, providerConf) + if err != nil { + return nil, err + } + newOIDCFieldGroup.OIDCProviders = append(newOIDCFieldGroup.OIDCProviders, newProvider) + } + } + } + + return newOIDCFieldGroup, nil +} + +// NewOIDCProvider creates a new OIDCProvider +func NewOIDCProvider(prefix string, providerConfig map[string]interface{}) (*OIDCProvider, error) { + newOIDCProvider := &OIDCProvider{} + + // Set dynamic prefix + newOIDCProvider._Prefix = prefix + + if value, ok := providerConfig["OIDC_SERVER"]; ok { + newOIDCProvider.OIDCServer, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("OIDC_SERVER must be of type string") + } + } + if value, ok := providerConfig["CLIENT_ID"]; ok { + newOIDCProvider.ClientID, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("CLIENT_ID must be of type string") + } + } + if value, ok := providerConfig["CLIENT_SECRET"]; ok { + newOIDCProvider.ClientSecret, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("CLIENT_SECRET must be of type bool") + } + } + if value, ok := providerConfig["SERVICE_NAME"]; ok { + newOIDCProvider.ServiceName, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("SERVICE_NAME must be of type string") + } + } + if value, ok := providerConfig["SERVICE_ICON"]; ok { + newOIDCProvider.ServiceIcon, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("SERVICE_ICON must be of type string") + } + } + if value, ok := providerConfig["VERIFIED_EMAIL_CLAIM_NAME"]; ok { + newOIDCProvider.VerifiedEmailClaimName, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("VERIFIED_EMAIL_CLAIM_NAME must be of type string") + } + } + if value, ok := providerConfig["PREFERRED_USERNAME_CLAIM_NAME"]; ok { + newOIDCProvider.PreferredUsernameClaimName, ok = value.(string) + if !ok { + return newOIDCProvider, errors.New("PREFERRED_USERNAME_CLAIM_NAME must be of type string") + } + } + if value, ok := providerConfig["LOGIN_SCOPES"]; ok { + newOIDCProvider.LoginScopes, ok = value.([]interface{}) + if !ok { + return newOIDCProvider, errors.New("LOGIN_SCOPES must be of type string") + } + } + + return newOIDCProvider, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/oidc/oidc_fields.go b/config-tool/pkg/lib/fieldgroups/oidc/oidc_fields.go new file mode 100644 index 000000000..454ed2cae --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/oidc/oidc_fields.go @@ -0,0 +1,6 @@ +package oidc + +// Fields returns a list of strings representing the fields in this field group +func (fg *OIDCFieldGroup) Fields() []string { + return []string{} +} diff --git a/config-tool/pkg/lib/fieldgroups/oidc/oidc_test.go b/config-tool/pkg/lib/fieldgroups/oidc/oidc_test.go new file mode 100644 index 000000000..1df929b00 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/oidc/oidc_test.go @@ -0,0 +1 @@ +package oidc diff --git a/config-tool/pkg/lib/fieldgroups/oidc/oidc_validator.go b/config-tool/pkg/lib/fieldgroups/oidc/oidc_validator.go new file mode 100644 index 000000000..f3e3f0dd5 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/oidc/oidc_validator.go @@ -0,0 +1,58 @@ +package oidc + +import ( + "fmt" + "strings" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *OIDCFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "OIDC" + + var errors []shared.ValidationError + + // If there are no providers, return no errors + if len(fg.OIDCProviders) == 0 { + return errors + } + + // Loop through providers + for _, provider := range fg.OIDCProviders { + + schemaErrors := 0 + // Check required fields are present + if ok, err := shared.ValidateRequiredString(provider.OIDCServer, fmt.Sprintf("%s_LOGIN_CONFIG.OIDC_SERVER", strings.ToUpper(provider._Prefix)), fgName); !ok { + errors = append(errors, err) + schemaErrors++ + } + if ok, err := shared.ValidateRequiredString(provider.ClientID, fmt.Sprintf("%s_LOGIN_CONFIG.CLIENT_ID", strings.ToUpper(provider._Prefix)), fgName); !ok { + errors = append(errors, err) + schemaErrors++ + + } + if ok, err := shared.ValidateRequiredString(provider.ClientSecret, fmt.Sprintf("%s_LOGIN_CONFIG.CLIENT_SECRET", strings.ToUpper(provider._Prefix)), fgName); !ok { + errors = append(errors, err) + schemaErrors++ + + } + if ok, err := shared.ValidateRequiredString(provider.ServiceName, fmt.Sprintf("%s_LOGIN_CONFIG.SERVICE_NAME", strings.ToUpper(provider._Prefix)), fgName); !ok { + errors = append(errors, err) + schemaErrors++ + } + + if schemaErrors > 0 { + continue + } + + if ok, err := shared.ValidateOIDCServer(opts, provider.OIDCServer, provider.ClientID, provider.ClientSecret, provider.ServiceName, provider.LoginScopes, fgName); !ok { + errors = append(errors, err) + } + + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation.go b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation.go new file mode 100644 index 000000000..8f01869db --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation.go @@ -0,0 +1,27 @@ +package quaydocumentation + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// QuayDocumentationFieldGroup represents the QuayDocumentationFieldGroup config fields +type QuayDocumentationFieldGroup struct { + DocumentationRoot string `default:"" validate:"" json:"DOCUMENTATION_ROOT,omitempty" yaml:"DOCUMENTATION_ROOT,omitempty"` +} + +// NewQuayDocumentationFieldGroup creates a new QuayDocumentationFieldGroup +func NewQuayDocumentationFieldGroup(fullConfig map[string]interface{}) (*QuayDocumentationFieldGroup, error) { + newQuayDocumentationFieldGroup := &QuayDocumentationFieldGroup{} + defaults.Set(newQuayDocumentationFieldGroup) + + if value, ok := fullConfig["DOCUMENTATION_ROOT"]; ok { + newQuayDocumentationFieldGroup.DocumentationRoot, ok = value.(string) + if !ok { + return newQuayDocumentationFieldGroup, errors.New("DOCUMENTATION_ROOT must be of type string") + } + } + + return newQuayDocumentationFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_fields.go b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_fields.go new file mode 100644 index 000000000..34e0928d0 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_fields.go @@ -0,0 +1,6 @@ +package quaydocumentation + +// Fields returns a list of strings representing the fields in this field group +func (fg *QuayDocumentationFieldGroup) Fields() []string { + return []string{"DOCUMENTATION_ROOT"} +} diff --git a/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_test.go b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_test.go new file mode 100644 index 000000000..629dcde56 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_test.go @@ -0,0 +1,59 @@ +package quaydocumentation + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateQuayDocumentation tests the Validate function +func TestValidateQuayDocumentation(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "NotSpecified", config: map[string]interface{}{}, want: "valid"}, + {name: "ValidURL", config: map[string]interface{}{"DOCUMENTATION_ROOT": "https://www.fakewebsite.com/docs"}, want: "valid"}, + {name: "ValidPathURL", config: map[string]interface{}{"DOCUMENTATION_ROOT": "good/path"}, want: "invalid"}, + {name: "InvalidURL", config: map[string]interface{}{"DOCUMENTATION_ROOT": "not a url"}, want: "invalid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewQuayDocumentationFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_validator.go b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_validator.go new file mode 100644 index 000000000..b6a2c381e --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/quaydocumentation/quaydocumentation_validator.go @@ -0,0 +1,34 @@ +package quaydocumentation + +import ( + "net/url" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *QuayDocumentationFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "QuayDocumentation" + + // Make empty errors + errors := []shared.ValidationError{} + + // If not provided, skip + if fg.DocumentationRoot == "" { + return errors + } + + // Make sure documentation root is valid url + if _, err := url.ParseRequestURI(fg.DocumentationRoot); err != nil { + newError := shared.ValidationError{ + Tags: []string{"DOCUMENTATION_ROOT"}, + FieldGroup: fgName, + Message: "Documentation root must be a valid url.", + } + errors = append(errors, newError) + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/redis/redis.go b/config-tool/pkg/lib/fieldgroups/redis/redis.go new file mode 100644 index 000000000..10aae2adc --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/redis/redis.go @@ -0,0 +1,122 @@ +package redis + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// RedisFieldGroup represents the RedisFieldGroup config fields +type RedisFieldGroup struct { + BuildlogsRedis *BuildlogsRedisStruct `default:"" validate:"" json:"BUILDLOGS_REDIS,omitempty" yaml:"BUILDLOGS_REDIS,omitempty"` + UserEventsRedis *UserEventsRedisStruct `default:"" validate:"" json:"USER_EVENTS_REDIS,omitempty" yaml:"USER_EVENTS_REDIS,omitempty"` +} + +// UserEventsRedisStruct represents the UserEventsRedisStruct config fields +type UserEventsRedisStruct struct { + Password string `default:"" validate:"" json:"password,omitempty" yaml:"password,omitempty"` + Port int `default:"" validate:"" json:"port,omitempty" yaml:"port,omitempty"` + Host string `default:"" validate:"" json:"host,omitempty" yaml:"host,omitempty"` + Ssl bool `default:"false" validate:"" json:"ssl,omitempty" yaml:"ssl,omitempty"` +} + +// BuildlogsRedisStruct represents the BuildlogsRedisStruct config fields +type BuildlogsRedisStruct struct { + Password string `default:"" validate:"" json:"password,omitempty" yaml:"password,omitempty"` + Port int `default:"" validate:"" json:"port,omitempty" yaml:"port,omitempty"` + Host string `default:"" validate:"" json:"host,omitempty" yaml:"host,omitempty"` + Ssl bool `default:"false" validate:"" json:"ssl,omitempty" yaml:"ssl,omitempty"` +} + +// NewRedisFieldGroup creates a new RedisFieldGroup +func NewRedisFieldGroup(fullConfig map[string]interface{}) (*RedisFieldGroup, error) { + newRedisFieldGroup := &RedisFieldGroup{} + defaults.Set(newRedisFieldGroup) + + if value, ok := fullConfig["BUILDLOGS_REDIS"]; ok { + var err error + value := value.(map[string]interface{}) + newRedisFieldGroup.BuildlogsRedis, err = NewBuildlogsRedisStruct(value) + if err != nil { + return newRedisFieldGroup, err + } + } + if value, ok := fullConfig["USER_EVENTS_REDIS"]; ok { + var err error + value := value.(map[string]interface{}) + newRedisFieldGroup.UserEventsRedis, err = NewUserEventsRedisStruct(value) + if err != nil { + return newRedisFieldGroup, err + } + } + + return newRedisFieldGroup, nil +} + +// NewUserEventsRedisStruct creates a new UserEventsRedisStruct +func NewUserEventsRedisStruct(fullConfig map[string]interface{}) (*UserEventsRedisStruct, error) { + newUserEventsRedisStruct := &UserEventsRedisStruct{} + defaults.Set(newUserEventsRedisStruct) + + if value, ok := fullConfig["password"]; ok { + newUserEventsRedisStruct.Password, ok = value.(string) + if !ok { + return newUserEventsRedisStruct, errors.New("password must be of type string") + } + } + if value, ok := fullConfig["port"]; ok { + newUserEventsRedisStruct.Port, ok = value.(int) + if !ok { + return newUserEventsRedisStruct, errors.New("port must be of type int") + } + } + if value, ok := fullConfig["host"]; ok { + newUserEventsRedisStruct.Host, ok = value.(string) + if !ok { + return newUserEventsRedisStruct, errors.New("host must be of type string") + } + } + + if value, ok := fullConfig["ssl"]; ok { + newUserEventsRedisStruct.Ssl, ok = value.(bool) + if !ok { + return newUserEventsRedisStruct, errors.New("ssl must be of type bool") + } + } + + return newUserEventsRedisStruct, nil +} + +// NewBuildlogsRedisStruct creates a new BuildlogsRedisStruct +func NewBuildlogsRedisStruct(fullConfig map[string]interface{}) (*BuildlogsRedisStruct, error) { + newBuildlogsRedisStruct := &BuildlogsRedisStruct{} + defaults.Set(newBuildlogsRedisStruct) + + if value, ok := fullConfig["password"]; ok { + newBuildlogsRedisStruct.Password, ok = value.(string) + if !ok { + return newBuildlogsRedisStruct, errors.New("password must be of type string") + } + } + if value, ok := fullConfig["port"]; ok { + newBuildlogsRedisStruct.Port, ok = value.(int) + if !ok { + return newBuildlogsRedisStruct, errors.New("port must be of type int") + } + } + if value, ok := fullConfig["host"]; ok { + newBuildlogsRedisStruct.Host, ok = value.(string) + if !ok { + return newBuildlogsRedisStruct, errors.New("host must be of type string") + } + } + + if value, ok := fullConfig["ssl"]; ok { + newBuildlogsRedisStruct.Ssl, ok = value.(bool) + if !ok { + return newBuildlogsRedisStruct, errors.New("ssl must be of type bool") + } + } + + return newBuildlogsRedisStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/redis/redis_fields.go b/config-tool/pkg/lib/fieldgroups/redis/redis_fields.go new file mode 100644 index 000000000..8df3cc1be --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/redis/redis_fields.go @@ -0,0 +1,6 @@ +package redis + +// Fields returns a list of strings representing the fields in this field group +func (fg *RedisFieldGroup) Fields() []string { + return []string{"BUILDLOGS_REDIS", "USER_EVENTS_REDIS"} +} diff --git a/config-tool/pkg/lib/fieldgroups/redis/redis_test.go b/config-tool/pkg/lib/fieldgroups/redis/redis_test.go new file mode 100644 index 000000000..f1bce4ef9 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/redis/redis_test.go @@ -0,0 +1,58 @@ +package redis + +import ( + "fmt" + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateRedis tests the Validate function +func TestValidateRedis(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + + {name: "NotSpecified", config: map[string]interface{}{}, want: "invalid"}, + {name: "Works", config: map[string]interface{}{"BUILDLOGS_REDIS": map[string]interface{}{"host": "redis", "port": 6379}, "USER_EVENTS_REDIS": map[string]interface{}{"host": "redis", "port": 6379}}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewRedisFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + fmt.Println(validationErrors[0].Message) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/redis/redis_validator.go b/config-tool/pkg/lib/fieldgroups/redis/redis_validator.go new file mode 100644 index 000000000..f0afcf2aa --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/redis/redis_validator.go @@ -0,0 +1,87 @@ +package redis + +import ( + "crypto/tls" + "fmt" + + "github.com/go-redis/redis/v8" + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *RedisFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // Check for build logs config + if ok, err := shared.ValidateRequiredObject(fg.BuildlogsRedis, "BUILDLOGS_REDIS", "Redis"); !ok { + errors = append(errors, err) + return errors + } + + // Check for build log host + if ok, err := shared.ValidateRequiredString(fg.BuildlogsRedis.Host, "BUILDLOGS_REDIS.HOST", "Redis"); !ok { + errors = append(errors, err) + } + + // Check for user events config + if ok, err := shared.ValidateRequiredObject(fg.UserEventsRedis, "USER_EVENTS_REDIS", "Redis"); !ok { + errors = append(errors, err) + return errors + } + + // Check for user events host + if ok, err := shared.ValidateRequiredString(fg.BuildlogsRedis.Host, "USER_EVENTS_REDIS.HOST", "Redis"); !ok { + errors = append(errors, err) + } + + // Build options for build logs and connect + addr := fg.BuildlogsRedis.Host + if fg.BuildlogsRedis.Port != 0 { + addr = addr + ":" + fmt.Sprintf("%d", fg.BuildlogsRedis.Port) + } + + var tlsConfig *tls.Config = nil + if fg.BuildlogsRedis.Ssl { + tlsConfig = &tls.Config{ + InsecureSkipVerify: true, + } + } + + options := &redis.Options{ + Addr: addr, + Password: fg.BuildlogsRedis.Password, + DB: 0, + TLSConfig: tlsConfig, + } + if ok, err := shared.ValidateRedisConnection(options, "BUILDLOGS_REDIS", "Redis"); !ok { + errors = append(errors, err) + } + + // Build options for user events and connect + addr = fg.UserEventsRedis.Host + if fg.UserEventsRedis.Port != 0 { + addr = addr + ":" + fmt.Sprintf("%d", fg.BuildlogsRedis.Port) + } + + tlsConfig = nil + if fg.UserEventsRedis.Ssl { + tlsConfig = &tls.Config{ + InsecureSkipVerify: true, + } + } + + options = &redis.Options{ + Addr: addr, + Password: fg.UserEventsRedis.Password, + DB: 0, + TLSConfig: tlsConfig, + } + if ok, err := shared.ValidateRedisConnection(options, "USER_EVENTS_REDIS", "Redis"); !ok { + errors = append(errors, err) + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/repomirror/repomirror.go b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror.go new file mode 100644 index 000000000..fd9a05669 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror.go @@ -0,0 +1,48 @@ +package repomirror + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// RepoMirrorFieldGroup represents the RepoMirrorFieldGroup config fields +type RepoMirrorFieldGroup struct { + FeatureRepoMirror bool `default:"false" validate:"" json:"FEATURE_REPO_MIRROR" yaml:"FEATURE_REPO_MIRROR"` + RepoMirrorInterval int `default:"30" validate:"" json:"REPO_MIRROR_INTERVAL,omitempty" yaml:"REPO_MIRROR_INTERVAL,omitempty"` + RepoMirrorServerHostname string `default:"" validate:"" json:"REPO_MIRROR_SERVER_HOSTNAME,omitempty" yaml:"REPO_MIRROR_SERVER_HOSTNAME,omitempty"` + RepoMirrorTlsVerify bool `default:"true" validate:"" json:"REPO_MIRROR_TLS_VERIFY" yaml:"REPO_MIRROR_TLS_VERIFY"` +} + +// NewRepoMirrorFieldGroup creates a new RepoMirrorFieldGroup +func NewRepoMirrorFieldGroup(fullConfig map[string]interface{}) (*RepoMirrorFieldGroup, error) { + newRepoMirrorFieldGroup := &RepoMirrorFieldGroup{} + defaults.Set(newRepoMirrorFieldGroup) + + if value, ok := fullConfig["FEATURE_REPO_MIRROR"]; ok { + newRepoMirrorFieldGroup.FeatureRepoMirror, ok = value.(bool) + if !ok { + return newRepoMirrorFieldGroup, errors.New("FEATURE_REPO_MIRROR must be of type bool") + } + } + if value, ok := fullConfig["REPO_MIRROR_INTERVAL"]; ok { + newRepoMirrorFieldGroup.RepoMirrorInterval, ok = value.(int) + if !ok { + return newRepoMirrorFieldGroup, errors.New("REPO_MIRROR_INTERVAL must be of type int") + } + } + if value, ok := fullConfig["REPO_MIRROR_SERVER_HOSTNAME"]; ok { + newRepoMirrorFieldGroup.RepoMirrorServerHostname, ok = value.(string) + if !ok { + return newRepoMirrorFieldGroup, errors.New("REPO_MIRROR_SERVER_HOSTNAME must be of type string") + } + } + if value, ok := fullConfig["REPO_MIRROR_TLS_VERIFY"]; ok { + newRepoMirrorFieldGroup.RepoMirrorTlsVerify, ok = value.(bool) + if !ok { + return newRepoMirrorFieldGroup, errors.New("REPO_MIRROR_TLS_VERIFY must be of type bool") + } + } + + return newRepoMirrorFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_fields.go b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_fields.go new file mode 100644 index 000000000..5486776a4 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_fields.go @@ -0,0 +1,6 @@ +package repomirror + +// Fields returns a list of strings representing the fields in this field group +func (fg *RepoMirrorFieldGroup) Fields() []string { + return []string{"FEATURE_REPO_MIRROR", "REPO_MIRROR_INTERVAL", "REPO_MIRROR_SERVER_HOSTNAME", "REPO_MIRROR_TLS_VERIFY"} +} diff --git a/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_test.go b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_test.go new file mode 100644 index 000000000..236bf1eed --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_test.go @@ -0,0 +1,69 @@ +package repomirror + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateRepoMirror tests the Validate function +func TestValidateRepoMirror(t *testing.T) { + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + { + config: map[string]interface{}{"FEATURE_REPO_MIRROR": true, "REPO_MIRROR_SERVER_HOSTNAME": "google.com"}, + name: "goodConfig", + want: "valid", + }, + { + config: map[string]interface{}{"FEATURE_REPO_MIRROR": true, "REPO_MIRROR_SERVER_HOSTNAME": "not a hostname"}, + name: "badHostname", + want: "invalid", + }, + { + config: map[string]interface{}{"FEATURE_REPO_MIRROR": false, "REPO_MIRROR_SERVER_HOSTNAME": "not a hostname"}, + name: "badHostnameFeatureOff", + want: "valid", + }, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewRepoMirrorFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + for _, err := range validationErrors { + t.Errorf(err.Message) + } + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_validator.go b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_validator.go new file mode 100644 index 000000000..2e908045a --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/repomirror/repomirror_validator.go @@ -0,0 +1,25 @@ +package repomirror + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *RepoMirrorFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "RepoMirror" + var errors []shared.ValidationError + + // Make sure feature is enabled + if !fg.FeatureRepoMirror { + return errors + } + + // if repo mirror hostname is set, make sure its a valid hostname + if fg.RepoMirrorServerHostname != "" { + if ok, err := shared.ValidateIsHostname(fg.RepoMirrorServerHostname, "REPO_MIRROR_SERVER_HOSTNAME", fgName); !ok { + errors = append(errors, err) + } + } + + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner.go b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner.go new file mode 100644 index 000000000..ad055aa66 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner.go @@ -0,0 +1,79 @@ +package securityscanner + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// SecurityScannerFieldGroup represents the SecurityScannerFieldGroup config fields +type SecurityScannerFieldGroup struct { + FeatureSecurityScanner bool `default:"false" validate:"" json:"FEATURE_SECURITY_SCANNER" yaml:"FEATURE_SECURITY_SCANNER"` + SecurityScannerEndpoint string `default:"" validate:"" json:"SECURITY_SCANNER_ENDPOINT,omitempty" yaml:"SECURITY_SCANNER_ENDPOINT,omitempty"` + SecurityScannerIndexingInterval int `default:"30" validate:"" json:"SECURITY_SCANNER_INDEXING_INTERVAL,omitempty" yaml:"SECURITY_SCANNER_INDEXING_INTERVAL,omitempty"` + SecurityScannerNotifications bool `default:"false" validate:"" json:"FEATURE_SECURITY_NOTIFICATIONS" yaml:"FEATURE_SECURITY_NOTIFICATIONS"` + SecurityScannerV4Endpoint string `default:"" validate:"" json:"SECURITY_SCANNER_V4_ENDPOINT,omitempty" yaml:"SECURITY_SCANNER_V4_ENDPOINT,omitempty"` + SecurityScannerV4NamespaceWhitelist []string `default:"[]" validate:"" json:"SECURITY_SCANNER_V4_NAMESPACE_WHITELIST,omitempty" yaml:"SECURITY_SCANNER_V4_NAMESPACE_WHITELIST,omitempty"` + SecurityScannerV4PSK string `default:"" json:"SECURITY_SCANNER_V4_PSK,omitempty" yaml:"SECURITY_SCANNER_V4_PSK,omitempty"` +} + +// NewSecurityScannerFieldGroup creates a new SecurityScannerFieldGroup +func NewSecurityScannerFieldGroup(fullConfig map[string]interface{}) (*SecurityScannerFieldGroup, error) { + newSecurityScannerFieldGroup := &SecurityScannerFieldGroup{} + defaults.Set(newSecurityScannerFieldGroup) + + if value, ok := fullConfig["FEATURE_SECURITY_SCANNER"]; ok { + newSecurityScannerFieldGroup.FeatureSecurityScanner, ok = value.(bool) + if !ok { + return newSecurityScannerFieldGroup, errors.New("FEATURE_SECURITY_SCANNER must be of type bool") + } + } + if value, ok := fullConfig["SECURITY_SCANNER_ENDPOINT"]; ok { + newSecurityScannerFieldGroup.SecurityScannerEndpoint, ok = value.(string) + if !ok { + return newSecurityScannerFieldGroup, errors.New("SECURITY_SCANNER_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["SECURITY_SCANNER_INDEXING_INTERVAL"]; ok { + newSecurityScannerFieldGroup.SecurityScannerIndexingInterval, ok = value.(int) + if !ok { + return newSecurityScannerFieldGroup, errors.New("SECURITY_SCANNER_INDEXING_INTERVAL must be of type int") + } + } + if value, ok := fullConfig["SECURITY_SCANNER_NOTIFICATIONS"]; ok { + newSecurityScannerFieldGroup.SecurityScannerNotifications, ok = value.(bool) + if !ok { + return newSecurityScannerFieldGroup, errors.New("SECURITY_SCANNER_NOTIFICATIONS must be of type bool") + } + } + if value, ok := fullConfig["SECURITY_SCANNER_V4_ENDPOINT"]; ok { + newSecurityScannerFieldGroup.SecurityScannerV4Endpoint, ok = value.(string) + if !ok { + return newSecurityScannerFieldGroup, errors.New("SECURITY_SCANNER_V4_ENDPOINT must be of type string") + } + } + if value, ok := fullConfig["SECURITY_SCANNER_V4_PSK"]; ok { + newSecurityScannerFieldGroup.SecurityScannerV4PSK, ok = value.(string) + if !ok { + return newSecurityScannerFieldGroup, errors.New("SECURITY_SCANNER_V4_PSK must be of type string") + } + } + + if value, ok := fullConfig["SECURITY_SCANNER_V4_NAMESPACE_WHITELIST"]; ok { + + // newSecurityScannerFieldGroup.SecurityScannerV4NamespaceWhitelist = value.([]string) + for _, element := range value.([]interface{}) { + strElement, ok := element.(string) + if !ok { + return newSecurityScannerFieldGroup, errors.New("SECURITY_SCANNER_V4_NAMESPACE_WHITELIST must be of type []string") + } + + newSecurityScannerFieldGroup.SecurityScannerV4NamespaceWhitelist = append( + newSecurityScannerFieldGroup.SecurityScannerV4NamespaceWhitelist, + strElement, + ) + } + } + + return newSecurityScannerFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_fields.go b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_fields.go new file mode 100644 index 000000000..a97d703b6 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_fields.go @@ -0,0 +1,6 @@ +package securityscanner + +// Fields returns a list of strings representing the fields in this field group +func (fg *SecurityScannerFieldGroup) Fields() []string { + return []string{"FEATURE_SECURITY_SCANNER", "SECURITY_SCANNER_ENDPOINT", "SECURITY_SCANNER_INDEXING_INTERVAL", "SECURITY_SCANNER_NOTIFICATIONS", "SECURITY_SCANNER_V4_ENDPOINT", "SECURITY_SCANNER_V4_NAMESPACE_WHITELIST"} +} diff --git a/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_test.go b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_test.go new file mode 100644 index 000000000..2ef597cd2 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_test.go @@ -0,0 +1,58 @@ +package securityscanner + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateSchema tests the ValidateSchema function +func TestValidateSecurityScanner(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + {name: "checkFeatureOff", config: map[string]interface{}{"FEATURE_SECURITY_SCANNER": false}, want: "valid"}, + {name: "checkFeatureOnMissigEndpoint", config: map[string]interface{}{"FEATURE_SECURITY_SCANNER": true}, want: "invalid"}, + {name: "checkFeatureOnGoodEnpointWProtocol", config: map[string]interface{}{"FEATURE_SECURITY_SCANNER": true, "SECURITY_SCANNER_ENDPOINT": "https://www.google.com:443"}, want: "valid"}, + {name: "checkEndpointNotURL", config: map[string]interface{}{"FEATURE_SECURITY_SCANNER": true, "SECURITY_SCANNER_ENDPOINT": "not_a_url"}, want: "invalid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewSecurityScannerFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "online", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } + +} diff --git a/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_validator.go b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_validator.go new file mode 100644 index 000000000..f57d2d329 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/securityscanner/securityscanner_validator.go @@ -0,0 +1,53 @@ +package securityscanner + +import ( + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// Validate checks the configuration settings for this field group +func (fg *SecurityScannerFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // Make sure feature is enabled + if !fg.FeatureSecurityScanner { + return errors + } + + // Make sure at least one endpoint is present + if ok, err := shared.ValidateAtLeastOneOfString([]string{fg.SecurityScannerEndpoint, fg.SecurityScannerV4Endpoint}, []string{"SECURITY_SCANNER_ENDPOINT", "SECURITY_SCANNER_V4_ENDPOINT"}, "SecurityScanner"); !ok { + errors = append(errors, err) + return errors + } + + // If v2 endpoint is present + if len(fg.SecurityScannerEndpoint) > 0 { + // Check for endpoint + if ok, err := shared.ValidateRequiredString(fg.SecurityScannerEndpoint, "SECURITY_SCANNER_ENDPOINT", "SecurityScanner"); !ok { + errors = append(errors, err) + } + + // Check endpoint is valid url + if ok, err := shared.ValidateIsURL(fg.SecurityScannerEndpoint, "SECURITY_SCANNER_ENDPOINT", "SecurityScanner"); !ok { + errors = append(errors, err) + } + } + + // If v4 endpoint is present + if len(fg.SecurityScannerV4Endpoint) > 0 { + // Check for endpoint + if ok, err := shared.ValidateRequiredString(fg.SecurityScannerV4Endpoint, "SECURITY_SCANNER_V4_ENDPOINT", "SecurityScanner"); !ok { + errors = append(errors, err) + } + + // Check endpoint is valid url + if ok, err := shared.ValidateIsURL(fg.SecurityScannerV4Endpoint, "SECURITY_SCANNER_V4_ENDPOINT", "SecurityScanner"); !ok { + errors = append(errors, err) + } + } + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing.go b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing.go new file mode 100644 index 000000000..afc45b856 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing.go @@ -0,0 +1,41 @@ +package teamsyncing + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// TeamSyncingFieldGroup represents the TeamSyncingFieldGroup config fields +type TeamSyncingFieldGroup struct { + FeatureNonsuperuserTeamSyncingSetup bool `default:"false" validate:"" json:"FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP" yaml:"FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP"` + FeatureTeamSyncing bool `default:"false" validate:"" json:"FEATURE_TEAM_SYNCING" yaml:"FEATURE_TEAM_SYNCING"` + TeamResyncStaleTime string `default:"30m" validate:"customValidateTimePattern" json:"TEAM_RESYNC_STALE_TIME,omitempty" yaml:"TEAM_RESYNC_STALE_TIME,omitempty"` +} + +// NewTeamSyncingFieldGroup creates a new TeamSyncingFieldGroup +func NewTeamSyncingFieldGroup(fullConfig map[string]interface{}) (*TeamSyncingFieldGroup, error) { + newTeamSyncingFieldGroup := &TeamSyncingFieldGroup{} + defaults.Set(newTeamSyncingFieldGroup) + + if value, ok := fullConfig["FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP"]; ok { + newTeamSyncingFieldGroup.FeatureNonsuperuserTeamSyncingSetup, ok = value.(bool) + if !ok { + return newTeamSyncingFieldGroup, errors.New("FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP must be of type bool") + } + } + if value, ok := fullConfig["FEATURE_TEAM_SYNCING"]; ok { + newTeamSyncingFieldGroup.FeatureTeamSyncing, ok = value.(bool) + if !ok { + return newTeamSyncingFieldGroup, errors.New("FEATURE_TEAM_SYNCING must be of type bool") + } + } + if value, ok := fullConfig["TEAM_RESYNC_STALE_TIME"]; ok { + newTeamSyncingFieldGroup.TeamResyncStaleTime, ok = value.(string) + if !ok { + return newTeamSyncingFieldGroup, errors.New("TEAM_RESYNC_STALE_TIME must be of type string") + } + } + + return newTeamSyncingFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_fields.go b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_fields.go new file mode 100644 index 000000000..1c78482e4 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_fields.go @@ -0,0 +1,6 @@ +package teamsyncing + +// Fields returns a list of strings representing the fields in this field group +func (fg *TeamSyncingFieldGroup) Fields() []string { + return []string{"FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP", "FEATURE_TEAM_SYNCING", "TEAM_RESYNC_STALE_TIME"} +} diff --git a/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_test.go b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_test.go new file mode 100644 index 000000000..8f5063baf --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_test.go @@ -0,0 +1,56 @@ +package teamsyncing + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateTeamSyncing tests the Validate function +func TestValidateTeamSyncing(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + {name: "Empty", config: map[string]interface{}{}, want: "valid"}, + {name: "BadSyncTime", config: map[string]interface{}{"TEAM_RESYNC_STALE_TIME": "10fff"}, want: "invalid"}, + {name: "GoodSyncTime", config: map[string]interface{}{"TEAM_RESYNC_STALE_TIME": "10m"}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewTeamSyncingFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_validator.go b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_validator.go new file mode 100644 index 000000000..80b1d2647 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/teamsyncing/teamsyncing_validator.go @@ -0,0 +1,19 @@ +package teamsyncing + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *TeamSyncingFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // If resync stale time has bad pattern + if ok, err := shared.ValidateTimePattern(fg.TeamResyncStaleTime, "TEAM_RESYNC_STALE_TIME", "TeamSyncing"); !ok { + errors = append(errors, err) + } + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/fieldgroups/timemachine/timemachine.go b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine.go new file mode 100644 index 000000000..3237b1ce1 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine.go @@ -0,0 +1,43 @@ +package timemachine + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// TimeMachineFieldGroup represents the TimeMachineFieldGroup config fields +type TimeMachineFieldGroup struct { + DefaultTagExpiration string `default:"2w" validate:"" json:"DEFAULT_TAG_EXPIRATION,omitempty" yaml:"DEFAULT_TAG_EXPIRATION,omitempty"` + FeatureChangeTagExpiration bool `default:"true" validate:"" json:"FEATURE_CHANGE_TAG_EXPIRATION" yaml:"FEATURE_CHANGE_TAG_EXPIRATION"` + TagExpirationOptions []interface{} `default:"" validate:"" json:"TAG_EXPIRATION_OPTIONS,omitempty" yaml:"TAG_EXPIRATION_OPTIONS,omitempty"` +} + +// NewTimeMachineFieldGroup creates a new TimeMachineFieldGroup +func NewTimeMachineFieldGroup(fullConfig map[string]interface{}) (*TimeMachineFieldGroup, error) { + newTimeMachineFieldGroup := &TimeMachineFieldGroup{} + defaults.Set(newTimeMachineFieldGroup) + + if value, ok := fullConfig["DEFAULT_TAG_EXPIRATION"]; ok { + newTimeMachineFieldGroup.DefaultTagExpiration, ok = value.(string) + if !ok { + return newTimeMachineFieldGroup, errors.New("DEFAULT_TAG_EXPIRATION must be of type string") + } + } + if value, ok := fullConfig["FEATURE_CHANGE_TAG_EXPIRATION"]; ok { + newTimeMachineFieldGroup.FeatureChangeTagExpiration, ok = value.(bool) + if !ok { + return newTimeMachineFieldGroup, errors.New("FEATURE_CHANGE_TAG_EXPIRATION must be of type bool") + } + } + if value, ok := fullConfig["TAG_EXPIRATION_OPTIONS"]; ok { + newTimeMachineFieldGroup.TagExpirationOptions, ok = value.([]interface{}) + if !ok { + return newTimeMachineFieldGroup, errors.New("TAG_EXPIRATION_OPTIONS must be of type []interface{}") + } + } else { + newTimeMachineFieldGroup.TagExpirationOptions = []interface{}{"0s", "1d", "1w", "2w", "4w"} + } + + return newTimeMachineFieldGroup, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_fields.go b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_fields.go new file mode 100644 index 000000000..2799c18f8 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_fields.go @@ -0,0 +1,6 @@ +package timemachine + +// Fields returns a list of strings representing the fields in this field group +func (fg *TimeMachineFieldGroup) Fields() []string { + return []string{"DEFAULT_TAG_EXPIRATION", "FEATURE_CHANGE_TAG_EXPIRATION", "TAG_EXPIRATION_OPTIONS"} +} diff --git a/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_test.go b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_test.go new file mode 100644 index 000000000..6778d959d --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_test.go @@ -0,0 +1,17 @@ +package timemachine + +// TestValidateTimeMachine tests the Validate function +// func TestValidateTimeMachine(t testing.T) { +// var tests = []struct { +// name string +// config map[string]interface{} +// want string +// }{{ +// config: map[string]interface{}{}, +// name: "testOne", +// want: "valid", +// }} +// for _, tt := range tests { +// fmt.Println("hello") +// } +// } diff --git a/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_validator.go b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_validator.go new file mode 100644 index 000000000..067d345f5 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/timemachine/timemachine_validator.go @@ -0,0 +1,20 @@ +package timemachine + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *TimeMachineFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + fgName := "TimeMachine" + + // Make empty errors + errors := []shared.ValidationError{} + + if ok, err := shared.ValidateIsOneOfString(fg.DefaultTagExpiration, shared.InterfaceArrayToStringArray(fg.TagExpirationOptions), "DEFAULT_TAG_EXPIRATION", fgName); !ok { + err.Message = "The default tag expiration period must be included in the allowed expiration periods." + errors = append(errors, err) + return errors + } + + return errors +} diff --git a/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings.go b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings.go new file mode 100644 index 000000000..f5ed53818 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings.go @@ -0,0 +1,106 @@ +package uservisiblesettings + +import ( + "errors" + + "github.com/creasty/defaults" +) + +// UserVisibleSettingsFieldGroup represents the UserVisibleSettingsFieldGroup config fields +type UserVisibleSettingsFieldGroup struct { + AvatarKind string `default:"local" validate:"" json:"AVATAR_KIND,omitempty" yaml:"AVATAR_KIND,omitempty"` + Branding *BrandingStruct `default:"" validate:"" json:"BRANDING,omitempty" yaml:"BRANDING,omitempty"` + ContactInfo []interface{} `default:"[]" validate:"" json:"CONTACT_INFO,omitempty" yaml:"CONTACT_INFO,omitempty"` + RegistryTitle string `default:"Project Quay" validate:"" json:"REGISTRY_TITLE,omitempty" yaml:"REGISTRY_TITLE,omitempty"` + RegistryTitleShort string `default:"Project Quay" validate:"" json:"REGISTRY_TITLE_SHORT,omitempty" yaml:"REGISTRY_TITLE_SHORT,omitempty"` + SearchMaxResultPageCount int `default:"10" validate:"" json:"SEARCH_MAX_RESULT_PAGE_COUNT,omitempty" yaml:"SEARCH_MAX_RESULT_PAGE_COUNT,omitempty"` + SearchResultsPerPage int `default:"10" validate:"" json:"SEARCH_RESULTS_PER_PAGE,omitempty" yaml:"SEARCH_RESULTS_PER_PAGE,omitempty"` + EnterpriseLogoUrl string `default:"" validate:"" json:"ENTERPRISE_LOGO_URL,omitempty" yaml:"ENTERPRISE_LOGO_URL,omitempty"` +} + +// BrandingStruct represents the BrandingStruct config fields +type BrandingStruct struct { + Logo string `default:"/static/img/quay-horizontal-color.svg" validate:"url" json:"logo,omitempty" yaml:"logo,omitempty"` + FooterImg string `default:"" validate:"url" json:"footer_img,omitempty" yaml:"footer_img,omitempty"` + FooterUrl string `default:"" validate:"url" json:"footer_url,omitempty" yaml:"footer_url,omitempty"` +} + +// NewUserVisibleSettingsFieldGroup creates a new UserVisibleSettingsFieldGroup +func NewUserVisibleSettingsFieldGroup(fullConfig map[string]interface{}) (*UserVisibleSettingsFieldGroup, error) { + newUserVisibleSettingsFieldGroup := &UserVisibleSettingsFieldGroup{} + defaults.Set(newUserVisibleSettingsFieldGroup) + + if value, ok := fullConfig["AVATAR_KIND"]; ok { + newUserVisibleSettingsFieldGroup.AvatarKind, ok = value.(string) + if !ok { + return newUserVisibleSettingsFieldGroup, errors.New("AVATAR_KIND must be of type string") + } + } + if value, ok := fullConfig["BRANDING"]; ok { + var err error + value := value.(map[string]interface{}) + newUserVisibleSettingsFieldGroup.Branding, err = NewBrandingStruct(value) + if err != nil { + return newUserVisibleSettingsFieldGroup, err + } + } + if value, ok := fullConfig["CONTACT_INFO"]; ok { + newUserVisibleSettingsFieldGroup.ContactInfo, ok = value.([]interface{}) + if !ok { + return newUserVisibleSettingsFieldGroup, errors.New("CONTACT_INFO must be of type []interface{}") + } + } + if value, ok := fullConfig["REGISTRY_TITLE"]; ok { + newUserVisibleSettingsFieldGroup.RegistryTitle, ok = value.(string) + if !ok { + return newUserVisibleSettingsFieldGroup, errors.New("REGISTRY_TITLE must be of type string") + } + } + if value, ok := fullConfig["REGISTRY_TITLE_SHORT"]; ok { + newUserVisibleSettingsFieldGroup.RegistryTitleShort, ok = value.(string) + if !ok { + return newUserVisibleSettingsFieldGroup, errors.New("REGISTRY_TITLE_SHORT must be of type string") + } + } + if value, ok := fullConfig["SEARCH_MAX_RESULT_PAGE_COUNT"]; ok { + newUserVisibleSettingsFieldGroup.SearchMaxResultPageCount, ok = value.(int) + if !ok { + return newUserVisibleSettingsFieldGroup, errors.New("SEARCH_MAX_RESULT_PAGE_COUNT must be of type int") + } + } + if value, ok := fullConfig["SEARCH_RESULTS_PER_PAGE"]; ok { + newUserVisibleSettingsFieldGroup.SearchResultsPerPage, ok = value.(int) + if !ok { + return newUserVisibleSettingsFieldGroup, errors.New("SEARCH_RESULTS_PER_PAGE must be of type int") + } + } + + return newUserVisibleSettingsFieldGroup, nil +} + +// NewBrandingStruct creates a new BrandingStruct +func NewBrandingStruct(fullConfig map[string]interface{}) (*BrandingStruct, error) { + newBrandingStruct := &BrandingStruct{} + defaults.Set(newBrandingStruct) + + if value, ok := fullConfig["logo"]; ok { + newBrandingStruct.Logo, ok = value.(string) + if !ok { + return newBrandingStruct, errors.New("logo must be of type string") + } + } + if value, ok := fullConfig["footer_img"]; ok { + newBrandingStruct.FooterImg, ok = value.(string) + if !ok { + return newBrandingStruct, errors.New("footer_img must be of type string") + } + } + if value, ok := fullConfig["footer_url"]; ok { + newBrandingStruct.FooterUrl, ok = value.(string) + if !ok { + return newBrandingStruct, errors.New("footer_url must be of type string") + } + } + + return newBrandingStruct, nil +} diff --git a/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_fields.go b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_fields.go new file mode 100644 index 000000000..c8c15af47 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_fields.go @@ -0,0 +1,6 @@ +package uservisiblesettings + +// Fields returns a list of strings representing the fields in this field group +func (fg *UserVisibleSettingsFieldGroup) Fields() []string { + return []string{"AVATAR_KIND", "BRANDING", "CONTACT_INFO", "REGISTRY_TITLE", "REGISTRY_TITLE_SHORT", "SEARCH_MAX_RESULT_PAGE_COUNT", "SEARCH_RESULTS_PER_PAGE"} +} diff --git a/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_test.go b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_test.go new file mode 100644 index 000000000..1780d11a7 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_test.go @@ -0,0 +1,54 @@ +package uservisiblesettings + +import ( + "testing" + + "github.com/quay/quay/config-tool/pkg/lib/shared" +) + +// TestValidateUserVisibleSettings tests the Validate function +func TestValidateUserVisibleSettings(t *testing.T) { + + // Define test data + var tests = []struct { + name string + config map[string]interface{} + want string + }{ + {name: "checkFeatureOff", config: map[string]interface{}{"SIGNING_ENGINE": ""}, want: "valid"}, + } + + // Iterate through tests + for _, tt := range tests { + + // Run specific test + t.Run(tt.name, func(t *testing.T) { + + // Get validation result + fg, err := NewUserVisibleSettingsFieldGroup(tt.config) + if err != nil && tt.want != "typeError" { + t.Errorf("Expected %s. Received %s", tt.want, err.Error()) + } + + opts := shared.Options{ + Mode: "testing", + } + + validationErrors := fg.Validate(opts) + + // Get result type + received := "" + if len(validationErrors) == 0 { + received = "valid" + } else { + received = "invalid" + } + + // Compare with expected + if tt.want != received { + t.Errorf("Expected %s. Received %s", tt.want, received) + } + + }) + } +} diff --git a/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_validator.go b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_validator.go new file mode 100644 index 000000000..6b5a331c7 --- /dev/null +++ b/config-tool/pkg/lib/fieldgroups/uservisiblesettings/uservisiblesettings_validator.go @@ -0,0 +1,14 @@ +package uservisiblesettings + +import "github.com/quay/quay/config-tool/pkg/lib/shared" + +// Validate checks the configuration settings for this field group +func (fg *UserVisibleSettingsFieldGroup) Validate(opts shared.Options) []shared.ValidationError { + + // Make empty errors + errors := []shared.ValidationError{} + + // Return errors + return errors + +} diff --git a/config-tool/pkg/lib/generate/generate.go b/config-tool/pkg/lib/generate/generate.go new file mode 100644 index 000000000..4d626b578 --- /dev/null +++ b/config-tool/pkg/lib/generate/generate.go @@ -0,0 +1,75 @@ +package generate + +import ( + "errors" + + "github.com/quay/quay/config-tool/pkg/lib/config" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/database" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/hostsettings" + "github.com/quay/quay/config-tool/pkg/lib/fieldgroups/redis" +) + +// AioiInputOptions defines the minimum required fields necessary for building a working config +type AioiInputOptions struct { + DatabaseURI string + RedisHostname string + RedisPassword string + ServerHostname string +} + +// GenerateBaseConfig will generate a minimal config for the Quay all in one installer. +// Database, Redis, and Server Hostname settings must be included +func GenerateBaseConfig(options AioiInputOptions) (config.Config, error) { + + // Check that all fields are correctly populated (this is a naive validtion) + if options.DatabaseURI == "" { + return nil, errors.New("Database URI is required") + } + if options.RedisHostname == "" { + return nil, errors.New("Redis Hostname is required") + } + if options.RedisPassword == "" { + return nil, errors.New("Redis Password is required") + } + if options.ServerHostname == "" { + return nil, errors.New("Server Hostname is required") + } + + defaultConfig, err := config.NewConfig(map[string]interface{}{}) + if err != nil { + return defaultConfig, nil + } + + // Set redis settings + redisFieldGroup, err := redis.NewRedisFieldGroup(map[string]interface{}{}) + if err != nil { + return defaultConfig, err + } + redisFieldGroup.BuildlogsRedis = &redis.BuildlogsRedisStruct{ + Host: options.RedisHostname, + Password: options.RedisPassword, + } + redisFieldGroup.UserEventsRedis = &redis.UserEventsRedisStruct{ + Host: options.RedisHostname, + Password: options.RedisPassword, + } + defaultConfig["Redis"] = redisFieldGroup + + // Set database settings + databaseFieldGroup, err := database.NewDatabaseFieldGroup(map[string]interface{}{}) + if err != nil { + return defaultConfig, err + } + databaseFieldGroup.DbUri = options.DatabaseURI + defaultConfig["Database"] = databaseFieldGroup + + // Set host settings + hostSettingsFieldGroup, err := hostsettings.NewHostSettingsFieldGroup(map[string]interface{}{}) + if err != nil { + return defaultConfig, err + } + hostSettingsFieldGroup.ServerHostname = options.ServerHostname + defaultConfig["HostSettings"] = hostSettingsFieldGroup + + return defaultConfig, nil +} diff --git a/config-tool/pkg/lib/generate/generate_test.go b/config-tool/pkg/lib/generate/generate_test.go new file mode 100644 index 000000000..d826efdc6 --- /dev/null +++ b/config-tool/pkg/lib/generate/generate_test.go @@ -0,0 +1,25 @@ +package generate + +import ( + "testing" + + "github.com/jojomi/go-spew/spew" +) + +// TestValidateSchema tests the ValidateSchema function +func TestGenerateBaseConfig(t *testing.T) { + + options := AioiInputOptions{ + ServerHostname: "myHostname", + DatabaseURI: "postgres://user:pass@192.168.250.159/quay", + RedisHostname: "192.168.250.159", + RedisPassword: "strongpassword", + } + + baseConfig, err := GenerateBaseConfig(options) + if err != nil { + t.Errorf(err.Error()) + } + + spew.Dump(baseConfig) +} diff --git a/config-tool/pkg/lib/shared/functions.go b/config-tool/pkg/lib/shared/functions.go new file mode 100644 index 000000000..eaa95d1a2 --- /dev/null +++ b/config-tool/pkg/lib/shared/functions.go @@ -0,0 +1,225 @@ +package shared + +import ( + "archive/tar" + "compress/gzip" + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "strings" + + log "github.com/sirupsen/logrus" +) + +// FixInterface converts a map[interface{}]interface{} into a map[string]interface{} +func FixInterface(input map[interface{}]interface{}) map[string]interface{} { + output := make(map[string]interface{}) + for key, value := range input { + strKey := fmt.Sprintf("%v", key) + output[strKey] = value + } + return output +} + +// GetFields will return the list of YAML fields in a given field group +func GetFields(fg FieldGroup) []string { + + var fieldNames []string + + // get type + t := reflect.Indirect(reflect.ValueOf(fg)).Type() + + // Iterate over all available fields and read the tag value + for i := 0; i < t.NumField(); i++ { + // Get the field, returns https://golang.org/pkg/reflect/#StructField + field := t.Field(i) + + // Get the field tag value + yaml := field.Tag.Get("yaml") + + fieldNames = append(fieldNames, yaml) + + } + + return fieldNames +} + +// LoadCerts will load certificates in a config directory. +func LoadCerts(dir string) map[string][]byte { + + // Check if dir exists + if _, err := os.Stat(dir); os.IsNotExist(err) { + return map[string][]byte{} + } + + // Get filenames in directory + certs := make(map[string][]byte) + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if info.IsDir() || strings.Contains(path, "..") || (!strings.HasSuffix(path, ".crt") && !strings.HasSuffix(path, ".cert") && !strings.HasSuffix(path, ".key") && !strings.HasSuffix(path, ".pem")) { + return nil + } + + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + relativePath, err := filepath.Rel(dir, path) + if err != nil { + return err + } + + certs[relativePath] = data + return nil + }) + if err != nil { + return map[string][]byte{} + } + + return certs +} + +// CreateArchive will create a tar file from a directory. +func CreateArchive(directory string, buf io.Writer) error { + gw := gzip.NewWriter(buf) + defer gw.Close() + tw := tar.NewWriter(gw) + defer tw.Close() + + files := []string{} + err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + files = append(files, path) + return nil + }) + if err != nil { + return err + } + + for _, file := range files { + err := addToArchive(tw, file) + if err != nil { + return err + } + } + + return nil +} + +// Helper function for creation of arhive +func addToArchive(tw *tar.Writer, filename string) error { + + // Open the file which will be written into the archive + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + // Get FileInfo about our file providing file size, mode, etc. + info, err := file.Stat() + if err != nil { + return err + } + + // Create a tar Header from the FileInfo data + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return err + } + + header.Name = filename + + // Write file header to the tar archive + err = tw.WriteHeader(header) + if err != nil { + return err + } + + // Copy file content to tar archive + _, err = io.Copy(tw, file) + if err != nil { + return err + } + + return nil +} + +// FixNumbers will convert all json.Number values to integer values +func FixNumbers(m map[string]interface{}) map[string]interface{} { + for k, v := range m { + _ = k + if v, ok := v.(map[string]interface{}); ok { + FixNumbers(v) + } + if v, ok := v.(float64); ok { + m[k] = int(v) + } + } + return m +} + +// RemoveNullValues will remove empty values from a config.yaml. This will prevent a panic when it is unmarshalled. +func RemoveNullValues(m map[string]interface{}) map[string]interface{} { + for k, v := range m { + _ = k + if v, ok := v.(map[string]interface{}); ok { + RemoveNullValues(v) + } + if v == nil { + delete(m, k) + } + } + return m +} + +// InterfaceArrayToStringArray converts a []interface{} to []string +func InterfaceArrayToStringArray(input []interface{}) []string { + output := make([]string, len(input)) + for i, v := range input { + output[i] = v.(string) + } + return output +} + +// GetTlsConfig will build a tls config struct given a set of CA certs +func GetTlsConfig(opts Options) (*tls.Config, error) { + + rootCAs, err := x509.SystemCertPool() + if err != nil { + return nil, err + } + if rootCAs == nil { + rootCAs = x509.NewCertPool() + } + + for name, cert := range opts.Certificates { + if strings.HasPrefix(name, "extra_ca_certs/") || strings.HasPrefix(name, "extra_ca_cert_") { + if ok := rootCAs.AppendCertsFromPEM(cert); !ok { + log.Warningf("Could not load extra ca cert file: %s. Skipping.", name) + } + } + } + + return &tls.Config{RootCAs: rootCAs}, nil +} + +// HasOIDCProvider will find if an OIDC provider is included in a config.yaml +func HasOIDCProvider(fullConfig map[string]interface{}) bool { + + for key, value := range fullConfig { + if _, ok := value.(map[string]interface{}); ok { + if strings.HasSuffix(key, "_LOGIN_CONFIG") && key != "GOOGLE_LOGIN_CONFIG" && key != "GITHUB_LOGIN_CONFIG" { + return true + } + } + } + return false +} diff --git a/config-tool/pkg/lib/shared/interfaces.go b/config-tool/pkg/lib/shared/interfaces.go new file mode 100644 index 000000000..9642c5012 --- /dev/null +++ b/config-tool/pkg/lib/shared/interfaces.go @@ -0,0 +1,7 @@ +package shared + +// FieldGroup is an interface that implements the Validate() function +type FieldGroup interface { + Validate(opts Options) []ValidationError + Fields() []string +} diff --git a/config-tool/pkg/lib/shared/jsonTypes.go b/config-tool/pkg/lib/shared/jsonTypes.go new file mode 100644 index 000000000..06ed8f677 --- /dev/null +++ b/config-tool/pkg/lib/shared/jsonTypes.go @@ -0,0 +1,18 @@ +package shared + +import ( + "bytes" + "encoding/json" +) + +// IntOrString is an int that may be unmarshaled from either a JSON number +// literal, or a JSON string. +type IntOrString int + +// UnmarshalJSON will unmarshal an array of bytes into this type +func (i *IntOrString) UnmarshalJSON(d []byte) error { + var v int + err := json.Unmarshal(bytes.Trim(d, `"`), &v) + *i = IntOrString(v) + return err +} diff --git a/config-tool/pkg/lib/shared/storage_validators.go b/config-tool/pkg/lib/shared/storage_validators.go new file mode 100644 index 000000000..b2eaf126f --- /dev/null +++ b/config-tool/pkg/lib/shared/storage_validators.go @@ -0,0 +1,520 @@ +package shared + +import ( + "context" + "fmt" + "net/url" + "strconv" + "time" + + "github.com/Azure/azure-storage-blob-go/azblob" + "github.com/aws/aws-sdk-go/aws" + awscredentials "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/ncw/swift" + log "github.com/sirupsen/logrus" +) + +// ValidateStorage will validate a S3 storage connection. +func ValidateStorage(opts Options, storageName string, storageType string, args *DistributedStorageArgs, fgName string) (bool, []ValidationError) { + + errors := []ValidationError{} + + // Get credentials + var endpoint string + var accessKey string + var secretKey string + var isSecure bool + var bucketName string + var token string = "" + + switch storageType { + case "LocalStorage": + return true, []ValidationError{} + case "RHOCSStorage", "RadosGWStorage": + + // Check access key + if ok, err := ValidateRequiredString(args.AccessKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".access_key", fgName); !ok { + errors = append(errors, err) + } + // Check secret key + if ok, err := ValidateRequiredString(args.SecretKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".secret_key", fgName); !ok { + errors = append(errors, err) + } + // Check hostname + if ok, err := ValidateRequiredString(args.Hostname, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".hostname", fgName); !ok { + errors = append(errors, err) + } + // Check bucket name + if ok, err := ValidateRequiredString(args.BucketName, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".bucket_name", fgName); !ok { + errors = append(errors, err) + } + + // Grab necessary variables + accessKey = args.AccessKey + secretKey = args.SecretKey + endpoint = args.Hostname + isSecure = args.IsSecure + bucketName = args.BucketName + + // Append port if present + if args.Port != 0 { + endpoint = endpoint + ":" + strconv.Itoa(args.Port) + } + + if len(errors) > 0 { + return false, errors + } + + if ok, err := validateMinioGateway(opts, storageName, endpoint, accessKey, secretKey, bucketName, token, isSecure, fgName); !ok { + errors = append(errors, err) + } + + case "S3Storage": + + // // Check access key + // if ok, err := ValidateRequiredString(args.S3AccessKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_access_key", fgName); !ok { + // errors = append(errors, err) + // } + // // Check secret key + // if ok, err := ValidateRequiredString(args.S3SecretKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_secret_key", fgName); !ok { + // errors = append(errors, err) + // } + + // Check bucket name + if ok, err := ValidateRequiredString(args.S3Bucket, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_bucket", fgName); !ok { + errors = append(errors, err) + } + + accessKey = args.S3AccessKey + secretKey = args.S3SecretKey + bucketName = args.S3Bucket + isSecure = true + + if len(args.Host) == 0 { + endpoint = "s3.amazonaws.com" + } else { + endpoint = args.Host + } + if args.Port != 0 { + endpoint = endpoint + ":" + strconv.Itoa(args.Port) + } + + if len(errors) > 0 { + return false, errors + } + + // If no keys are provided, we attempt to use IAM + if args.S3AccessKey == "" || args.S3SecretKey == "" { + + sess, err := session.NewSession() + if err != nil { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: "Could not create S3 session.", + } + errors = append(errors, newError) + return false, errors + } + + // Get credentials and set + appCreds := ec2rolecreds.NewCredentials(sess) + value, err := appCreds.Get() + if err != nil { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: "No access key or secret key were provided. Attempted to fetch IAM role and failed.", + } + errors = append(errors, newError) + return false, errors + } + + accessKey = value.AccessKeyID + secretKey = value.SecretAccessKey + token = value.SessionToken + + } + if ok, err := validateMinioGateway(opts, storageName, endpoint, accessKey, secretKey, bucketName, token, isSecure, fgName); !ok { + errors = append(errors, err) + } + + case "GoogleCloudStorage": + + // Check access key + if ok, err := ValidateRequiredString(args.AccessKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".access_key", fgName); !ok { + errors = append(errors, err) + } + // Check secret key + if ok, err := ValidateRequiredString(args.SecretKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".secret_key", fgName); !ok { + errors = append(errors, err) + } + // Check bucket name + if ok, err := ValidateRequiredString(args.BucketName, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".bucket_name", fgName); !ok { + errors = append(errors, err) + } + + accessKey = args.AccessKey + secretKey = args.SecretKey + endpoint = "storage.googleapis.com" + isSecure = true + bucketName = args.BucketName + + if len(errors) > 0 { + return false, errors + } + + if ok, err := validateMinioGateway(opts, storageName, endpoint, accessKey, secretKey, bucketName, token, isSecure, fgName); !ok { + errors = append(errors, err) + } + + case "AzureStorage": + + // Check access key + if ok, err := ValidateRequiredString(args.AzureContainer, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".azure_container", fgName); !ok { + errors = append(errors, err) + } + // Check account name + if ok, err := ValidateRequiredString(args.AzureAccountName, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".azure_account_name", fgName); !ok { + errors = append(errors, err) + } + + accountName := args.AzureAccountName + accountKey := args.AzureAccountKey + containerName := args.AzureContainer + token = args.SASToken + if args.EndpointURL != "" { + endpoint = args.EndpointURL + } else { + endpoint = fmt.Sprintf("https://%s.blob.core.windows.net", accountName) + } + + if len(errors) > 0 { + return false, errors + } + + if ok, err := validateAzureGateway(opts, endpoint, storageName, accountName, accountKey, containerName, token, fgName); !ok { + errors = append(errors, err) + } + + case "CloudFrontedS3Storage": + + // Check access key + if ok, err := ValidateRequiredString(args.S3AccessKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_access_key", fgName); !ok { + errors = append(errors, err) + } + // Check secret key + if ok, err := ValidateRequiredString(args.S3SecretKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_secret_key", fgName); !ok { + errors = append(errors, err) + } + // Check bucket name + if ok, err := ValidateRequiredString(args.S3Bucket, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_bucket", fgName); !ok { + errors = append(errors, err) + } + // Check distribution domain + if ok, err := ValidateRequiredString(args.CloudfrontDistributionDomain, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".cloudfront_distribution_domain", fgName); !ok { + errors = append(errors, err) + } + // Check key id + if ok, err := ValidateRequiredString(args.CloudfrontKeyID, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".cloudfront_key_id", fgName); !ok { + errors = append(errors, err) + } + + accessKey = args.S3AccessKey + secretKey = args.S3SecretKey + bucketName = args.S3Bucket + isSecure = true + + if len(args.Host) == 0 { + endpoint = "s3.amazonaws.com" + } else { + endpoint = args.Host + } + if args.Port != 0 { + endpoint = endpoint + ":" + strconv.Itoa(args.Port) + } + + if len(errors) > 0 { + return false, errors + } + + // Validate bucket settings + if ok, err := validateMinioGateway(opts, storageName, endpoint, accessKey, secretKey, bucketName, token, isSecure, fgName); !ok { + errors = append(errors, err) + } + + _, err := session.NewSession(&aws.Config{ + Credentials: awscredentials.NewStaticCredentials(accessKey, secretKey, ""), + }) + if err != nil { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: "Could not create S3 session", + } + errors = append(errors, newError) + return false, errors + } + + case "SwiftStorage": + + // Validate auth version + if args.SwiftAuthVersion != 1 && args.SwiftAuthVersion != 2 && args.SwiftAuthVersion != 3 { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: strconv.Itoa(args.SwiftAuthVersion) + " must be either 1, 2, or 3.", + } + errors = append(errors, newError) + } + // Check auth url + if ok, err := ValidateRequiredString(args.SwiftAuthURL, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".auth_url", fgName); !ok { + errors = append(errors, err) + } + // Check swift container + if ok, err := ValidateRequiredString(args.SwiftContainer, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".swift_container", fgName); !ok { + errors = append(errors, err) + } + // Check swift user + if ok, err := ValidateRequiredString(args.SwiftUser, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".swift_user", fgName); !ok { + errors = append(errors, err) + } + // Check swift password + if ok, err := ValidateRequiredString(args.SwiftPassword, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".swift_password", fgName); !ok { + errors = append(errors, err) + } + + if len(errors) > 0 { + return false, errors + } + + if ok, err := validateSwift(opts, storageName, args.SwiftAuthVersion, args.SwiftUser, args.SwiftPassword, args.SwiftContainer, args.SwiftAuthURL, args.SwiftOsOptions, fgName); !ok { + errors = append(errors, err) + } + case "CloudFlareStorage": + // Check access key + if ok, err := ValidateRequiredString(args.S3AccessKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_access_key", fgName); !ok { + errors = append(errors, err) + } + // Check secret key + if ok, err := ValidateRequiredString(args.S3SecretKey, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_secret_key", fgName); !ok { + errors = append(errors, err) + } + // Check bucket name + if ok, err := ValidateRequiredString(args.S3Bucket, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".s3_bucket", fgName); !ok { + errors = append(errors, err) + } + // Check distribution domain + if ok, err := ValidateRequiredString(args.CloudflareDomain, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".cloudflare_domain", fgName); !ok { + errors = append(errors, err) + } + case "MultiCDNStorage": + // Check provider map + if ok, err := ValidateRequiredObject(args.Providers, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".providers", fgName); !ok { + errors = append(errors, err) + } + + // Check default provider + if ok, err := ValidateRequiredString(args.DefaultProvider, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".default_provider", fgName); !ok { + errors = append(errors, err) + } + + // Check storage config + if ok, err := ValidateRequiredObject(args.StorageConfig, "DISTRIBUTED_STORAGE_CONFIG."+storageName+".storage_config", fgName); !ok { + errors = append(errors, err) + } + + default: + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: storageType + " is not a valid storage type.", + } + return false, []ValidationError{newError} + } + + if len(errors) > 0 { + return false, errors + } else { + return true, nil + } + +} + +func validateMinioGateway(opts Options, storageName, endpoint, accessKey, secretKey, bucketName, token string, isSecure bool, fgName string) (bool, ValidationError) { + + // Set transport + tr, err := minio.DefaultTransport(true) + if err != nil { + log.Fatalf("error creating the minio connection: error creating the default transport layer: %v", err) + } + + config, err := GetTlsConfig(opts) + if err != nil { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: err.Error(), + } + return false, newError + } + tr.TLSClientConfig = config + + // Create client + st, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKey, secretKey, token), + Secure: isSecure, + Transport: tr, + }) + if err != nil { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: "An error occurred while attempting to connect to " + storageName + " storage. Error: " + err.Error(), + } + return false, newError + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + found, err := st.BucketExists(ctx, bucketName) + if err != nil { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: "Could not connect to storage " + storageName + ". Error: " + err.Error(), + } + return false, newError + } + + if !found { + newError := ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: fmt.Sprintf("Could not find bucket (%s) in storage (%s)", bucketName, storageName), + } + return false, newError + } + + return true, ValidationError{} + +} + +func validateAzureGateway(opts Options, endpointURL, storageName, accountName, accountKey, containerName, token, fgName string) (bool, ValidationError) { + + credentials, err := azblob.NewSharedKeyCredential(accountName, accountKey) + if err != nil { + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: "Could not create credentials for Azure storage. Error: " + err.Error(), + } + } + + p := azblob.NewPipeline(credentials, azblob.PipelineOptions{}) + u, err := url.Parse(endpointURL) + + serviceURL := azblob.NewServiceURL(*u, p) + containerURL := serviceURL.NewContainerURL(containerName) + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + _, err = containerURL.GetAccountInfo(ctx) + if err != nil { + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: "Could not connect to Azure storage. Error: " + err.Error(), + } + } + + return true, ValidationError{} +} + +func validateSwift(opts Options, storageName string, authVersion int, swiftUser, swiftPassword, containerName, authUrl string, osOptions map[string]interface{}, fgName string) (bool, ValidationError) { + + var c swift.Connection + switch authVersion { + case 1: + c = swift.Connection{ + UserName: swiftUser, + ApiKey: swiftPassword, + AuthUrl: authUrl, + AuthVersion: 1, + } + case 2: + c = swift.Connection{ + UserName: swiftUser, + ApiKey: swiftPassword, + AuthUrl: authUrl, + AuthVersion: 2, + } + case 3: + + // Need domain + domain, ok := osOptions["user_domain_name"].(string) + if !ok { + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: "Swift auth v3 requires a domain (string) in os_options", + } + } + // Need domain + tenantId, ok := osOptions["tenant_id"].(string) + if !ok { + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: "Swift auth v3 requires tenant_id (string) in os_options", + } + } + c = swift.Connection{ + UserName: swiftUser, + ApiKey: swiftPassword, + AuthUrl: authUrl, + AuthVersion: 3, + Domain: domain, + TenantId: tenantId, + } + } + + err := c.Authenticate() + if err != nil { + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: "Could not connect to Swift storage. Error: " + err.Error(), + } + } + + // List containers + containers, err := c.ContainerNames(nil) + if err != nil { + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: "Could not list containers in Swift storage. Error: " + err.Error(), + } + } + + // Validate container name is present + for _, name := range containers { + if containerName == name { + return true, ValidationError{} + } + } + + return false, ValidationError{ + FieldGroup: fgName, + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + Message: fmt.Sprintf("Could not find container (%s) in Swift storage.", containerName), + } + +} diff --git a/config-tool/pkg/lib/shared/structs.go b/config-tool/pkg/lib/shared/structs.go new file mode 100644 index 000000000..812f29457 --- /dev/null +++ b/config-tool/pkg/lib/shared/structs.go @@ -0,0 +1,59 @@ +package shared + +// Options is a struct that tells the validator how to validate +type Options struct { + Mode string // One of Online, Offline, Testing + Certificates map[string][]byte +} + +// ValidationError is a struct that holds information about a failed field group policy +type ValidationError struct { + FieldGroup string + Tags []string + Message string +} + +func (ve ValidationError) String() string { + return ve.Message +} + +// DistributedStorageArgs +type DistributedStorageArgs struct { + // Args for RHOCSStorage, RadosGWStorage + Hostname string `default:"" validate:"" json:"hostname,omitempty" yaml:"hostname,omitempty"` + Port int `default:"" validate:"" json:"port,omitempty" yaml:"port,omitempty"` + IsSecure bool `default:"" validate:"" json:"is_secure" yaml:"is_secure"` + StoragePath string `default:"" validate:"" json:"storage_path,omitempty" yaml:"storage_path,omitempty"` + AccessKey string `default:"" validate:"" json:"access_key,omitempty" yaml:"access_key,omitempty"` + SecretKey string `default:"" validate:"" json:"secret_key,omitempty" yaml:"secret_key,omitempty"` + BucketName string `default:"" validate:"" json:"bucket_name,omitempty" yaml:"bucket_name,omitempty"` + // Args for S3Storage + S3Bucket string `default:"" validate:"" json:"s3_bucket,omitempty" yaml:"s3_bucket,omitempty"` + S3AccessKey string `default:"" validate:"" json:"s3_access_key,omitempty" yaml:"s3_access_key,omitempty"` + S3SecretKey string `default:"" validate:"" json:"s3_secret_key,omitempty" yaml:"s3_secret_key,omitempty"` + Host string `default:"" validate:"" json:"host,omitempty" yaml:"host,omitempty"` + // Args for AzureStorage + AzureContainer string `default:"" validate:"" json:"azure_container,omitempty" yaml:"azure_container,omitempty"` + AzureAccountName string `default:"" validate:"" json:"azure_account_name,omitempty" yaml:"azure_account_name,omitempty"` + AzureAccountKey string `default:"" validate:"" json:"azure_account_key,omitempty" yaml:"azure_account_key,omitempty"` + SASToken string `default:"" validate:"" json:"sas_token,omitempty" yaml:"sas_token,omitempty"` + EndpointURL string `default:"" validate:"" json:"endpoint_url,omitempty" yaml:"endpoint_url,omitempty"` + // Args for Cloudfront + CloudfrontDistributionDomain string `default:"" validate:"" json:"cloudfront_distribution_domain,omitempty" yaml:"cloudfront_distribution_domain,omitempty"` + CloudfrontKeyID string `default:"" validate:"" json:"cloudfront_key_id,omitempty" yaml:"cloudfront_key_id,omitempty"` + // Args for SwiftStorage + SwiftAuthVersion int `default:"" validate:"" json:"auth_version,omitempty" yaml:"auth_version,omitempty"` + SwiftAuthURL string `default:"" validate:"" json:"auth_url,omitempty" yaml:"auth_url,omitempty"` + SwiftContainer string `default:"" validate:"" json:"swift_container,omitempty" yaml:"swift_container,omitempty"` + SwiftUser string `default:"" validate:"" json:"swift_user,omitempty" yaml:"swift_user,omitempty"` + SwiftPassword string `default:"" validate:"" json:"swift_password,omitempty" yaml:"swift_password,omitempty"` + SwiftCaCertPath string `default:"" validate:"" json:"ca_cert_path,omitempty" yaml:"ca_cert_path,omitempty"` + SwiftTempURLKey string `default:"" validate:"" json:"temp_url_key,omitempty" yaml:"temp_url_key,omitempty"` + SwiftOsOptions map[string]interface{} `default:"" validate:"" json:"os_options,omitempty" yaml:"os_options,omitempty"` + // Args for CloudFlare + CloudflareDomain string `default:"" validate:"" json:"cloudflare_domain,omitempty" yaml:"cloudflare_domain,omitempty"` + // Args for MultiCDNStorage + DefaultProvider string `default:"" validate:"" json:"default_provider,omitempty" yaml:"default_provider,omitempty"` + Providers map[string]interface{} `default:"" validate:"" json:"providers,omitempty" yaml:"providers,omitempty"` + StorageConfig map[string]interface{} `default:"" validate:"" json:"storage_config,omitempty" yaml:"storage_config,omitempty"` +} diff --git a/config-tool/pkg/lib/shared/validators.go b/config-tool/pkg/lib/shared/validators.go new file mode 100644 index 000000000..867e0398d --- /dev/null +++ b/config-tool/pkg/lib/shared/validators.go @@ -0,0 +1,951 @@ +package shared + +import ( + "context" + "crypto/tls" + "crypto/x509" + "database/sql" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/smtp" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + + goOIDC "github.com/coreos/go-oidc" + "github.com/go-ldap/ldap/v3" + "github.com/go-redis/redis/v8" + "github.com/go-sql-driver/mysql" + "github.com/jackc/pgx/v4" + log "github.com/sirupsen/logrus" + "golang.org/x/oauth2" +) + +// ValidateGitHubOAuth checks that the Bitbucker OAuth credentials are correct +func ValidateGitHubOAuth(opts Options, clientID, clientSecret, githubEndpoint string) bool { + + req, err := http.NewRequest("GET", githubEndpoint, nil) + if err != nil { + return false + } + req.SetBasicAuth(clientID, clientSecret) + + tlsConfig, err := GetTlsConfig(opts) + if err != nil { + log.Warning(err) + return false + } + transport := &http.Transport{TLSClientConfig: tlsConfig} + client := &http.Client{Transport: transport} + + resp, err := client.Do(req) + if err != nil { + log.Warning(err) + return false + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Warning(err) + return false + } + + log.Debugf("github response: %d, %s", resp.StatusCode, string(body)) + + return resp.StatusCode == 200 +} + +// ValidateRequiredObject validates that a object input is not nil +func ValidateRequiredObject(input interface{}, field, fgName string) (bool, ValidationError) { + + // Check string + if input == nil || reflect.ValueOf(input).IsNil() { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " is required", + } + return false, newError + } + + // Return okay + return true, ValidationError{} + +} + +// ValidateRequiredString validates that a string input is not empty +func ValidateRequiredString(input, field, fgName string) (bool, ValidationError) { + + // Check string + if input == "" { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " is required", + } + return false, newError + } + + // Return okay + return true, ValidationError{} + +} + +// ValidateAtLeastOneOfBool validates that at least one of the given options is true +func ValidateAtLeastOneOfBool(inputs []bool, fields []string, fgName string) (bool, ValidationError) { + + // At first, assume none are true + atLeastOne := false + + // Iterate through options + for _, val := range inputs { + if val { + atLeastOne = true + break + } + } + + // If at least one isnt true, return error + if !atLeastOne { + newError := ValidationError{ + Tags: fields, + FieldGroup: fgName, + Message: "At least one of " + strings.Join(fields, ",") + " must be enabled", + } + return false, newError + } + + return true, ValidationError{} + +} + +// ValidateAtLeastOneOfString validates that at least one of the given options is true +func ValidateAtLeastOneOfString(inputs []string, fields []string, fgName string) (bool, ValidationError) { + + // At first, assume none are true + atLeastOne := false + + // Iterate through options + for _, val := range inputs { + if val != "" { + atLeastOne = true + break + } + } + + // If at least one isnt true, return error + if !atLeastOne { + newError := ValidationError{ + Tags: fields, + FieldGroup: fgName, + Message: "At least one of " + strings.Join(fields, ",") + " must be present", + } + return false, newError + } + + return true, ValidationError{} + +} + +// ValidateRedisConnection validates that a Redis connection can successfully be established +func ValidateRedisConnection(options *redis.Options, field, fgName string) (bool, ValidationError) { + + // Start client + rdb := redis.NewClient(options) + log.Debugf("Address: %s", options.Addr) + log.Debugf("Username: %s", options.Username) + log.Debugf("Password Len: %d", len(options.Password)) + log.Debugf("Ssl: %+v", options.TLSConfig) + + // Ping client + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + _, err := rdb.Ping(ctx).Result() + if err != nil { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: "Could not connect to Redis with values provided in " + field + ". Error: " + err.Error(), + } + return false, newError + } + + return true, ValidationError{} + +} + +// ValidateIsOneOfString validates that a string is one of a given option +func ValidateIsOneOfString(input string, options []string, field string, fgName string) (bool, ValidationError) { + + // At first, assume none are true + isOneOf := false + + // Iterate through options + for _, val := range options { + if input == val { + isOneOf = true + break + } + } + + // If at least one isnt true, return error + if !isOneOf { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " must be one of " + strings.Join(options, ", ") + ".", + } + return false, newError + } + + return true, ValidationError{} +} + +// ValidateIsURL tests a string to determine if it is a well-structured url or not. +func ValidateIsURL(input string, field string, fgName string) (bool, ValidationError) { + + _, err := url.ParseRequestURI(input) + if err != nil { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " must be of type URL", + } + return false, newError + } + + u, err := url.Parse(input) + if err != nil || u.Scheme == "" || u.Host == "" { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " must be of type URL", + } + return false, newError + } + + return true, ValidationError{} +} + +// ValidateIsHostname tests a string to determine if it is a well-structured hostname or not. +func ValidateIsHostname(input string, field string, fgName string) (bool, ValidationError) { + + // trim whitespace + input = strings.Trim(input, " ") + + // check against regex + re, _ := regexp.Compile(`^[a-zA-Z-0-9\.]+(:[0-9]+)?$`) + if !re.MatchString(input) { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " must be of type Hostname", + } + return false, newError + } + + return true, ValidationError{} +} + +// ValidateHostIsReachable will check if a get request returns a 200 status code +func ValidateHostIsReachable(opts Options, input string, field string, fgName string) (bool, ValidationError) { + + log.Debugf("Attempting to reach %s", input) + // Get protocol + u, err := url.Parse(input) + if err != nil { + return false, ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: err.Error(), + } + } + + scheme := u.Scheme + + // Get raw hostname without protocol + host := strings.TrimPrefix(input, "https://") + host = strings.TrimPrefix(host, "http://") + + // Set timeout + timeout := 5 * time.Second + + log.Debugf("Dialing %s with scheme %s", host, scheme) + + // Switch on protocol + switch scheme { + case "http": + + _, err := net.DialTimeout("tcp", host, timeout) + if err != nil { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: err.Error(), + } + return false, newError + } + + case "https": + + config, err := GetTlsConfig(opts) + if err != nil { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: err.Error(), + } + return false, newError + } + dialer := &net.Dialer{ + Timeout: timeout, + } + + _, err = tls.DialWithDialer(dialer, "tcp", host, config) + if err != nil { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: "Cannot reach " + input + ". Error: " + err.Error(), + } + return false, newError + } + + default: + return false, ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " must have a valid scheme", + } + } + + return true, ValidationError{} + +} + +// ValidateFileExists will check if a path exists on the current machine +func ValidateFileExists(input string, field string, fgName string) (bool, ValidationError) { + + // Check path + if _, err := os.Stat(input); os.IsNotExist(err) { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: "Cannot access the file " + input, + } + return false, newError + } + + return true, ValidationError{} + +} + +// ValidateTimePattern validates that a string has the pattern ^[0-9]+(w|m|d|h|s)$ +func ValidateTimePattern(input string, field string, fgName string) (bool, ValidationError) { + + re := regexp.MustCompile(`^[0-9]+(w|m|d|h|s)$`) + matches := re.FindAllString(input, -1) + + // If the pattern is not matched + if len(matches) != 1 { + newError := ValidationError{ + Tags: []string{field}, + FieldGroup: fgName, + Message: field + " must have the regex pattern ^[0-9]+(w|m|d|h|s)$", + } + return false, newError + } + + return true, ValidationError{} +} + +// ValidateCertsPresent validates that all required certificates are present in the options struct +func ValidateCertsPresent(opts Options, requiredCertNames []string, fgName string) (bool, ValidationError) { + + // If no certificates are passed + if opts.Certificates == nil { + newError := ValidationError{ + Tags: []string{"Certificates"}, + FieldGroup: fgName, + Message: "Certificates are required for SSL but are not present", + } + return false, newError + } + + // Check that all required certificates are present + for _, certName := range requiredCertNames { + + // Check that cert has been included + if _, ok := opts.Certificates[certName]; !ok { + newError := ValidationError{ + Tags: []string{"Certificates"}, + FieldGroup: fgName, + Message: "Certificate " + certName + " is required for " + fgName + " .", + } + return false, newError + } + } + + return true, ValidationError{} + +} + +// ValidateCertPairWithHostname will validate that a public private key pair are valid and have the correct hostname +func ValidateCertPairWithHostname(cert, key []byte, hostname string, fgName string) (bool, ValidationError) { + + // Load key pair, this will check the public, private keys are paired + certChain, err := tls.X509KeyPair(cert, key) + if err != nil { + newError := ValidationError{ + Tags: []string{"Certificates"}, + FieldGroup: fgName, + Message: err.Error(), + } + return false, newError + } + + certificate, err := x509.ParseCertificate(certChain.Certificate[0]) + if err != nil { + newError := ValidationError{ + Tags: []string{"Certificates"}, + FieldGroup: fgName, + Message: err.Error(), + } + return false, newError + } + + // Make sure port is removed + cleanHost, _, err := net.SplitHostPort(hostname) + if err != nil { + cleanHost = hostname + } + + err = certificate.VerifyHostname(cleanHost) + if err != nil { + newError := ValidationError{ + Tags: []string{"Certificates"}, + FieldGroup: fgName, + Message: err.Error(), + } + return false, newError + } + + return true, ValidationError{} + +} + +// ValidateBitbucketOAuth checks that the Bitbucker OAuth credentials are correct +func ValidateBitbucketOAuth(clientID, clientSecret string) bool { + + // Generated by curl-to-Go: https://mholt.github.io/curl-to-go + body := strings.NewReader(`grant_type=authorization_code&code={code}`) + req, _ := http.NewRequest("POST", "https://bitbucket.org/site/oauth2/access_token", body) + + req.SetBasicAuth(clientID, clientSecret) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Warning(err) + return false + } + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Warning(err) + return false + } + + log.Debugf("bitbucket response: %d, %s", resp.StatusCode, string(respBody)) + // Load response into json + var responseJSON map[string]interface{} + _ = json.Unmarshal(respBody, &responseJSON) + + // If the error isnt unauthorized + return responseJSON["error_description"] == "The specified code is not valid." + +} + +// ValidateDatabaseConnection checks that the Bitbucker OAuth credentials are correct +func ValidateDatabaseConnection(opts Options, rawURI, caCert string, threadlocals, autorollback bool, sslmode, sslrootcert, fgName string) error { + + // Convert uri into correct format + uri, err := url.Parse(rawURI) + if err != nil { + return err + } + + credentials, err := url.PathUnescape(uri.User.String()) + if err != nil { + return err + } + + // Get database type + scheme := uri.Scheme + fullHostName := uri.Host + dbname := uri.Path[1:] + params := uri.Query() + + log.Debugf("Scheme: %s", scheme) + log.Debugf("Host: %s", fullHostName) + log.Debugf("Db: %s", dbname) + log.Debugf("Params: %s", params.Encode()) + + if sslmode != "" { + params.Add("sslmode", sslmode) + } + + // Database is MySQL + if scheme == "mysql+pymysql" { + + // Create db connection string + dsn := credentials + "@tcp(" + fullHostName + ")/" + dbname + + // Check if CA cert is used + if caCert != "" { + log.Debug("CA Cert provided") + relativePath, err := filepath.Rel("conf/stack", caCert) + if err != nil { + return err + } + certBytes, ok := opts.Certificates[relativePath] + if !ok { + return errors.New("could not find " + relativePath + " in config bundle") + } + caCertPool := x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM(certBytes); !ok { + return errors.New("could not add CA cert to pool") + } + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + RootCAs: caCertPool, + } + mysql.RegisterTLSConfig("custom-tls", tlsConfig) + log.Debug("Created tls config for database successfully") + + // Add param + params.Add("tls", "custom-tls") + } + + log.Debugf("Including params %s", params.Encode()) + dsn = fmt.Sprintf("%s?%s", dsn, params.Encode()) + + db, err := sql.Open("mysql", dsn) + if err != nil { + return err + } + + defer db.Close() + + // Try to ping database + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + log.Debugf("Pinging database at %s dsn:", dsn) + err = db.PingContext(ctx) + if err != nil { + return err + } + + // Database is Postgres + } else if scheme == "postgresql" { + + // Try to ping database + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // If CA cert was included + // Check if sslmode is either "verify-full" or "verify-ca" + // If that's the case, then postgres needs sslrootcert in order to verify + // the CA against the server (defaults to ~/.postgresql/root.crt) + // In our case, we will temporarily write the database.pem in /tmp + // For the actual bundle, the value of sslrootcert should be conf/stack/database.pem + // Ref: https://www.postgresql.org/docs/9.1/libpq-ssl.html + if sslmode == "verify-full" || sslmode == "verify-ca" { + params.Add("sslrootcert", sslrootcert) + } + + var dsn string + username := uri.User.Username() + password, present := uri.User.Password() + + log.Debugf("Including params %s", params.Encode()) + if present { + dsn = fmt.Sprint(&url.URL{ + Scheme: scheme, + User: url.UserPassword(username, password), + Host: fullHostName, + RawPath: dbname, + Path: "/" + dbname, + RawQuery: params.Encode(), + }) + } else { + dsn = fmt.Sprint(&url.URL{ + Scheme: scheme, + User: url.User(username), + Host: fullHostName, + Path: "/" + dbname, + RawQuery: params.Encode(), + }) + } + + // Connect and defer closing + log.Debugf("Pinging database at %s", dsn) + conn, err := pgx.Connect(ctx, dsn) + if err != nil { + return err + } + defer conn.Close(ctx) + + // Get version + var version string + row := conn.QueryRow(ctx, "SHOW server_version;") + err = row.Scan(&version) + if err != nil { + return err + } + + // Extract major version number using regex + var re = regexp.MustCompile(`^(\d+)`) + match := re.FindStringSubmatch(version) + + if len(match) < 1 { + // If no match, return an error + return fmt.Errorf("could not parse major version from: %s", version) + } + + // Parse the major version as an integer + majorVersion, err := strconv.Atoi(match[0]) + if err != nil { + return err + } + + // Check version + if majorVersion < 13 { + log.Warnf("Warning: Your version of PostgreSQL (%s) is EOL (End Of Life). Consider upgrading.", strconv.Itoa(majorVersion)) + } + + // If database is postgres, make sure that extension pg_trgm is installed + rows, err := conn.Query(ctx, `SELECT extname FROM pg_extension`) + if err != nil { + return err + } + + for rows.Next() { + var extension string + err = rows.Scan(&extension) + if err != nil { + return err + } + fmt.Println(extension) + if strings.Contains(extension, "pg_trgm") { + return nil + } + } + return errors.New("if you are using a Postgres database, you must install the pg_trgm extension") + + } else { + return errors.New("you must use a valid scheme") + } + + // Return no error + return nil + +} + +// ValidateElasticSearchCredentials will validate credentials +func ValidateElasticSearchCredentials(url, accessKey, accessSecret string) bool { + + // Generated by curl-to-Go: https://mholt.github.io/curl-to-go + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("o", err.Error()) + } + req.SetBasicAuth(accessKey, accessSecret) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Println("e", err.Error()) + } + + if resp.StatusCode != 200 { + return false + } + + return true +} + +// ValidateEmailServer validates that the provided smtp server is valid +func ValidateEmailServer(opts Options, mailServer string, mailPort int, useTLS bool, useAuth bool, username string, password string, fgName string) (bool, ValidationError) { + + // Dial smtp server + conn, err := net.DialTimeout("tcp", mailServer+":"+strconv.Itoa(int(mailPort)), 3*time.Second) + if err != nil { + return false, ValidationError{ + Tags: []string{"MAIL_SERVER"}, + FieldGroup: fgName, + Message: "Cannot reach " + mailServer + ". Error: " + err.Error(), + } + } + + client, err := smtp.NewClient(conn, mailServer) + if err != nil { + return false, ValidationError{ + Tags: []string{"MAIL_SERVER"}, + FieldGroup: fgName, + Message: "Cannot reach " + mailServer + ". Error: " + err.Error(), + } + } + + // If TLS is enabled. + if useTLS { + config, err := GetTlsConfig(opts) + if err != nil { + return false, ValidationError{ + Tags: []string{"MAIL_USE_TLS"}, + FieldGroup: fgName, + Message: err.Error(), + } + + } + config.ServerName = mailServer + + err = client.StartTLS(config) + if err != nil { + return false, ValidationError{ + Tags: []string{"MAIL_USE_TLS"}, + FieldGroup: fgName, + Message: err.Error(), + } + + } + return true, ValidationError{} + } + + // If auth is enabled, try to authenticate + if useAuth { + auth := smtp.PlainAuth("", username, password, mailServer) + if err = client.Auth(auth); err != nil { + return false, ValidationError{ + Tags: []string{"MAIL_SERVER"}, + FieldGroup: fgName, + Message: "Error: " + err.Error(), + } + } + } + + return true, ValidationError{} + +} + +// ValidateGitLabOAuth checks that the Bitbucker OAuth credentials are correct +func ValidateGitLabOAuth(clientID, clientSecret, gitlabEndpoint string) bool { + + // Generated by curl-to-Go: https://mholt.github.io/curl-to-go + + // Add trailing slash if it doesn't exist + if string(gitlabEndpoint[len(gitlabEndpoint)-1]) != "/" { + gitlabEndpoint = gitlabEndpoint + "/" + } + + req, err := http.NewRequest("POST", gitlabEndpoint+"oauth/token?client_id="+clientID+"&client_secret="+clientSecret+"&grant_type=authorization_code&code=FAKECODE&redirect_uri=REDIRECT_URI", nil) + if err != nil { + return false + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false + } + defer resp.Body.Close() + + respBody, _ := ioutil.ReadAll(resp.Body) + + // Load response into json + var responseJSON map[string]interface{} + _ = json.Unmarshal(respBody, &responseJSON) + + // If the error isnt unauthorized + return responseJSON["error"] == "invalid_grant" + +} + +// ValidateGoogleOAuth checks that the Bitbucker OAuth credentials are correct +func ValidateGoogleOAuth(clientID, clientSecret string) bool { + + // Generated by curl-to-Go: https://mholt.github.io/curl-to-go + + req, err := http.NewRequest("POST", "https://www.googleapis.com/oauth2/v3/token?client_id="+clientID+"&client_secret="+clientSecret+"&grant_type=authorization_code&code=FAKECODE&redirect_uri=https://fakeredirect.com", nil) + if err != nil { + return false + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false + } + defer resp.Body.Close() + + respBody, _ := ioutil.ReadAll(resp.Body) + + // Load response into json + var responseJSON map[string]interface{} + _ = json.Unmarshal(respBody, &responseJSON) + + // If the error isnt unauthorized + return responseJSON["error"] == "invalid_grant" + +} + +// ValidateLDAPServer validates that the provided ldap server is valid +func ValidateLDAPServer(opts Options, ldapUri, ldapAdminDn, ldapAdminPasswd, ldapUidAttr, ldapEmailAttr, ldapUserFilter string, ldapBaseDn []interface{}, fgName string) (bool, ValidationError) { + + // Get tls config + tlsConfig, err := GetTlsConfig(opts) + if err != nil { + return false, ValidationError{ + Tags: []string{"LDAP"}, + FieldGroup: fgName, + Message: err.Error(), + } + } + + // Check for LDAP ca cert and add if present + if crt, ok := opts.Certificates["ldap.crt"]; ok { + certAdded := tlsConfig.RootCAs.AppendCertsFromPEM(crt) + if !certAdded { + return false, ValidationError{ + Tags: []string{"LDAP"}, + FieldGroup: fgName, + Message: "Could not successfully load ldap.crt", + } + } + } + + // Dial ldap server + l, err := ldap.DialURL(ldapUri, ldap.DialWithTLSConfig(tlsConfig)) + if err != nil { + return false, ValidationError{ + Tags: []string{"LDAP_URI"}, + FieldGroup: fgName, + Message: "Could not connect to " + ldapUri + ". Error: " + err.Error(), + } + } + + // Authenticate + err = l.Bind(ldapAdminDn, ldapAdminPasswd) + if err != nil { + return false, ValidationError{ + Tags: []string{"LDAP_URI"}, + FieldGroup: fgName, + Message: "Could not authenticate LDAP server. Error: " + err.Error(), + } + } + + userFilter := fmt.Sprintf("(&(%s=*)%s)", ldapUidAttr, ldapUserFilter) + request := &ldap.SearchRequest{ + BaseDN: strings.Join(InterfaceArrayToStringArray(ldapBaseDn), ","), + Scope: ldap.ScopeWholeSubtree, + Filter: userFilter, + Attributes: []string{ + ldapEmailAttr, ldapUidAttr, + }, + Controls: []ldap.Control{ldap.NewControlPaging(32)}, + } + + _, err = l.Search(request) + if err != nil { + return false, ValidationError{ + Tags: []string{"LDAP_URI"}, + FieldGroup: fgName, + Message: "Could not query LDAP server. Error: " + err.Error(), + } + } + + return true, ValidationError{} + +} + +// ValidateOIDCServer validates that the provided oidc server is valid +func ValidateOIDCServer(opts Options, oidcServer, clientID, clientSecret, serviceName string, loginScopes []interface{}, fgName string) (bool, ValidationError) { + + // Create http client + config, err := GetTlsConfig(opts) + if err != nil { + return false, ValidationError{ + Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"}, + FieldGroup: fgName, + Message: err.Error(), + } + } + tr := &http.Transport{TLSClientConfig: config} + client := &http.Client{Transport: tr, Timeout: 5 * time.Second} + + // Try to ping database + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + ctx = goOIDC.ClientContext(ctx, client) + + if !strings.HasSuffix(oidcServer, "/") { + return false, ValidationError{ + Tags: []string{"OIDC_SERVER"}, + FieldGroup: fgName, + Message: "OIDC_SERVER must end with a trailing /", + } + } + + // Create provider + p, err := goOIDC.NewProvider(ctx, oidcServer) + if err != nil { + p, err = goOIDC.NewProvider(ctx, strings.TrimSuffix(oidcServer, "/")) + if err != nil { + return false, ValidationError{ + Tags: []string{"OIDC_SERVER"}, + FieldGroup: fgName, + Message: "Could not create provider for " + serviceName + ". Error: " + err.Error(), + } + } + } + + oauth2Config := oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + Endpoint: p.Endpoint(), + RedirectURL: "http://quay/oauth2/auth0/callback", + Scopes: InterfaceArrayToStringArray(loginScopes), + } + + _, err = oauth2Config.Exchange(ctx, "badcode") + if err != nil { + if strings.Contains(err.Error(), "access_denied") { + return false, ValidationError{ + Tags: []string{"OIDC_SERVER"}, + FieldGroup: fgName, + Message: fmt.Sprintf("Incorrect credentials for OIDC %s", serviceName), + } + } else if strings.Contains(err.Error(), "invalid_grant") { + return true, ValidationError{} // this means we connected to the server correctly + } else { + return false, ValidationError{ + Tags: []string{"OIDC_SERVER"}, + FieldGroup: fgName, + Message: "Could not reach OIDC server " + serviceName + ". Error: " + err.Error(), + } + } + } + + return true, ValidationError{} + +} diff --git a/config-tool/pkg/lib/shared/validators_test.go b/config-tool/pkg/lib/shared/validators_test.go new file mode 100644 index 000000000..fa7d45e08 --- /dev/null +++ b/config-tool/pkg/lib/shared/validators_test.go @@ -0,0 +1,66 @@ +package shared + +import ( + "testing" +) + +// TestValidateCertPairWithHostname tests the Validate function +func TestValidateCertPairWithHostname(t *testing.T) { + + certs := map[string][]byte{ + "ssl.key": []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAv1iWzjlGIxtjAJ/1oDZvWQQDyAMBFpy79/A5VbeIBDpcv/Ve +PuaG8oTYCYAktSRuwAYbiTxN0K7HZ2njAdY4BxyD3yCNLQF+KVn3Q/eYSZh3OJLO +EXgcQvEzopiridg975rzm3ksTtOIlLOneUSj6ONinqOpkaYe+D8p+ANHv7YN7s5V +qQnxJn6cSixOigT9zUGEyOHeyfyIcZLfmLxpCw0t5QfjCTFLShTz0V4hWajfOp3z +bvl4O8YzvfUKYO72JHioqa75dwh0eBtJM12jckbHh9JYx/EoV//zaNxP2qrYEvtB +uStgTST84rTzx+xrkiaWfGxs/Moeme8cPy5zfQIDAQABAoIBAGSCa0zOJvpf82Qr +ogFTNrACfN3+Pf8bu1zkkall64uVAI1QnP3bZ71SbIypBB8mkQpK6wHubE2W0WWP +6E9ZsDqEDv0QgzfF1fhwqoLINvVJoi5UZuwkNGwxeNcK7OhOb1JCCX58avrJALBj +ojAADz1Q28fK3lKEeTYbL7d4OaMIXMXMu1036+Exr8N1R5R6mXlb4vLIWUfnersJ +GDqC5qkFhkODcDwULrftrxij2dvOPamJlCLpduXfLDhZ3UQqBhaS4dr0saLfgYBF +16a4NE+pPMCC9wFOUo1JNYiY7ME/ntqLhmLL0sWc7gwQUOFDGbVkTNYJ6qMBjx/6 +ZlTaSd0CgYEA4EU7a76WFQbTkGauoy/ihKKmsX+jAxNy5EeoI6XPEWx9vGz4rJzi +TXLEQAiTcgX3IhLa7dAs5iade+Hk/WyKZbkmJRhmxfn4RLj/u+WYs3WC6hU9hyvK +m9Cxh623+TJC+Y+jJlKlaGCxls0eBCgRL7m/PG56vyL/ERaOc7Fr2o8CgYEA2mrh +fHGygPtMtx6Q1xpBETZnXN7Vx9kvlqfCCFEb3vLWGEpskl0TWkpPQmnE416rdYOf +X78bJUjoqO5GIaC3NgZQKxMrp9XJWfrTxp6r68Wa1cSA4LgFHZ3gtGbJbNq3z3IR +A9jKU9t310KCscVpHx9AMne6v5g/LfRXQ4zIBzMCgYAvt4tFCW/1WVZ6St6taerQ +Pasp6PZOGT1AxN5Jd2XvVx4JkUX3tAmSYPDQjwKQKCTE4y4hm0FyVpT7XrzSDt4D +drle+yoixWTFencvC1LKHB6Wn55PvEmHjYe4ToXuR3tojd8wsDTxWGFwrIPObpf5 +h5Pgz8DeGhwbDqmQhBdmkQKBgQC4VxyX+x283luQ8ass4GuqK1BxgWDMmvEfJdcN +TedH84veVHHt1cBPpAfg9YPGok/zjnMkTBaNEUvLx85I82utnQZsVHGz5StbVecG +60QOaWiUopRjFOy8YlMT7uxxguc/nfXeWUnqHIC4nNnRT9u4+JcmAQcMTWKFVoOP +73GjIQKBgQC96LM1Vr4/FRb3OiaULsefr9VrVw/XOTsLLVgfspUI6jmrxpjxGNxz +FBoETbL3x6nPf1jv6c8rL6rdflF7Bj1Qduw+K/MiOGtGHVcycgQLr75JZXTZd6ZG +xZrZ1RNMio7dF5BCORdZQP9bO45+GwcKf551AJbm7Go0CxL+ULL/AQ== +-----END RSA PRIVATE KEY-----`), + "ssl.cert": []byte( + `-----BEGIN CERTIFICATE----- +MIIDUzCCAjsCFFsU6n8cHfw0saeTp+atDOWXDzicMA0GCSqGSIb3DQEBCwUAMGYx +CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOSjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRUwEwYDVQQDDAxmYWtlaG9z +dC5jb20wHhcNMjAwODEwMTY0MTM4WhcNMjExMjIzMTY0MTM4WjBmMQswCQYDVQQG +EwJVUzELMAkGA1UECAwCTkoxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDEVMBMGA1UEAwwMZmFrZWhvc3QuY29tMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1iWzjlGIxtjAJ/1oDZvWQQD +yAMBFpy79/A5VbeIBDpcv/VePuaG8oTYCYAktSRuwAYbiTxN0K7HZ2njAdY4BxyD +3yCNLQF+KVn3Q/eYSZh3OJLOEXgcQvEzopiridg975rzm3ksTtOIlLOneUSj6ONi +nqOpkaYe+D8p+ANHv7YN7s5VqQnxJn6cSixOigT9zUGEyOHeyfyIcZLfmLxpCw0t +5QfjCTFLShTz0V4hWajfOp3zbvl4O8YzvfUKYO72JHioqa75dwh0eBtJM12jckbH +h9JYx/EoV//zaNxP2qrYEvtBuStgTST84rTzx+xrkiaWfGxs/Moeme8cPy5zfQID +AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfinJu6bpCiCkSsbMv2dmZltwFroHbUzRE +bFuUGmqsOSm/3B3UqH5Vp7bm4RhHuinAwtb6FzcVQQY2jYQGsvvpjuh8fWC1Rr6L +/AcGC0ihUZv9towdLQbDuQZDnxgbMBZbh1OIAeN2JvKz6yGvmzSPdSYr8rr6nvDO +6ZTU0IGmctm4YXXtq5wnPZw40GRTNxLP/dRjz30dH4cikt+r/W/kefT5Ue6eQ5da +bWD+dzQSYs0v6rDp+e/V39maCcNjeFuoK2Wq5ldhKdzAZWmVH3Lx2ION/6Fr4iL5 +WHgsCLsLspQZqpi5o9pKqo1WH8b+uaqm5LDQvvrk3kkQLB/M5VMQ +-----END CERTIFICATE-----`), + } + + ok, err := ValidateCertPairWithHostname(certs["ssl.cert"], certs["ssl.key"], "fakehost.com", "") + if !ok { + t.Error(err) + } + +} diff --git a/config-tool/testdata/config-bundle/config.yaml b/config-tool/testdata/config-bundle/config.yaml new file mode 100644 index 000000000..be6fad470 --- /dev/null +++ b/config-tool/testdata/config-bundle/config.yaml @@ -0,0 +1,67 @@ +AUTHENTICATION_TYPE: Database +BUILDLOGS_REDIS: + host: 192.168.250.159 + password: strongpassword + port: 6379 +SECRET_KEY: "40157269433064266822674401740626984898972632465622168464725100311621640999470" +DATABASE_SECRET_KEY: "81541057085600720484162638317561463611194901378275494293746615390984668417511" +DB_URI: postgresql://user:pass@192.168.250.159/quay +DEFAULT_TAG_EXPIRATION: 2w +DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS: [] +DISTRIBUTED_STORAGE_PREFERENCE: + - amazon +ENTERPRISE_LOGO_URL: /static/img/quay-horizontal-color.svg +FEATURE_ACI_CONVERSION: false +FEATURE_ANONYMOUS_ACCESS: true +FEATURE_APP_REGISTRY: false +FEATURE_APP_SPECIFIC_TOKENS: true +FEATURE_BUILD_SUPPORT: false +FEATURE_CHANGE_TAG_EXPIRATION: true +FEATURE_DIRECT_LOGIN: true +FEATURE_PARTIAL_USER_AUTOCOMPLETE: true +FEATURE_REPO_MIRROR: false +FEATURE_MAILING: false +MAIL_USERNAME: jonathan +MAIL_PASSWORD: king +MAIL_USE_AUTH: true +FEATURE_REQUIRE_TEAM_INVITE: true +FEATURE_RESTRICTED_V1_PUSH: true +FEATURE_SECURITY_NOTIFICATIONS: true +FEATURE_SECURITY_SCANNER: false +FEATURE_USERNAME_CONFIRMATION: true +FEATURE_USER_CREATION: true +FEATURE_USER_LOG_ACCESS: true +GITHUB_LOGIN_CONFIG: {} +GITHUB_TRIGGER_CONFIG: {} +GITLAB_TRIGGER_KIND: {} +LOGS_MODEL: database +LOGS_MODEL_CONFIG: {} +LOG_ARCHIVE_LOCATION: default +MAIL_DEFAULT_SENDER: support@quay.io +MAIL_PORT: 587 +MAIL_USE_TLS: true +PREFERRED_URL_SCHEME: http +REGISTRY_TITLE: Red Hat Quay +REGISTRY_TITLE_SHORT: Red Hat Quay +REPO_MIRROR_SERVER_HOSTNAME: null +REPO_MIRROR_TLS_VERIFY: true +SECURITY_SCANNER_ISSUER_NAME: security_scanner +SERVER_HOSTNAME: quay +SETUP_COMPLETE: true +SUPER_USERS: + - user +TAG_EXPIRATION_OPTIONS: + - 0s + - 1d + - 1w + - 2w + - 4w +TEAM_RESYNC_STALE_TIME: 60m +TESTING: false +USERFILES_LOCATION: default +USERFILES_PATH: userfiles/ +USER_EVENTS_REDIS: + host: 192.168.250.159 + password: strongpassword + port: 6379 +USE_CDN: false diff --git a/config-tool/testdata/tls/localhost.crt b/config-tool/testdata/tls/localhost.crt new file mode 100644 index 000000000..6260a52ff --- /dev/null +++ b/config-tool/testdata/tls/localhost.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOTCCAiECFGJXq3hBISTS1wvdm/lYYVecqTvcMA0GCSqGSIb3DQEBCwUAMFkx +CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOSjEhMB8GA1UECgwYSW50ZXJuZXQgV2lk +Z2l0cyBQdHkgTHRkMRowGAYDVQQDDBFqb25hdGhhbi1yZWdpc3RyeTAeFw0yMDA5 +MjgxOTI0MDNaFw0yMjAyMTAxOTI0MDNaMFkxCzAJBgNVBAYTAlVTMQswCQYDVQQI +DAJOSjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRowGAYDVQQD +DBFqb25hdGhhbi1yZWdpc3RyeTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKhV6ekp4+YRWMyYPK5BlNDzLVeX/CLrNRcr8b7+9guXSB87Dmdk73aTgruK +0RMoLa62tNjE2vAGn7y+Py3Dt1XKD+HssXSAV+pZuVTbomS5Y1MUhMCbOcyFIl3P +VY3aN6UdaHer01gGC/aBSDeaQtm9CAfhIPr+46ctmR7dhOGRS5fggbda5ezjrShr +CYooBCyKSeqi3II3AT2BtKV/6lSuXRhanAsueFR4udTo32fFc7EACV/Q/Bst66Q0 +KSrkRh0/dSebDRSlVCeNNB5S2pWpX+3FQMOmNzfoY9i9KaMnpt1VcStfULCpDI3P +FVJ6yT5efTj7bjQrJETiAmOUMRUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAgWk +2BoQ05FA/7/Dx0fszahJPxi8m9MbibzO/iHDxM3CC4EfOQ2wFQEqGmuifQitj4kv +H8cQj9iywLejKreu8dC5AkzUPTY0541ZWxrFGwO3CuWPv792sRLUtMxgvMxBUlkw +hYUbA0pSgVXBwd2zdj4Oah95DNWRn4nzRGUohj6EK4qaX1zGjsyx5aooSrmM/7Pd +zbs2W+1Cj9/ZXZvUK7/urD8sSlEn3z97L5GMe40+HTbFd5JgLNbSgSthKgH0zM7d +MXO3mSv8brvZ2sDHBWg3V+z+7FMJRCIeOPgeV3ZAsk+I4+cHVT7YN9ssnxHHFKXW +EHmsiLcAWh710ixn4g== +-----END CERTIFICATE----- diff --git a/config-tool/testdata/tls/localhost.key b/config-tool/testdata/tls/localhost.key new file mode 100644 index 000000000..f88794098 --- /dev/null +++ b/config-tool/testdata/tls/localhost.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqFXp6Snj5hFYzJg8rkGU0PMtV5f8Ius1Fyvxvv72C5dIHzsO +Z2TvdpOCu4rREygtrra02MTa8AafvL4/LcO3VcoP4eyxdIBX6lm5VNuiZLljUxSE +wJs5zIUiXc9Vjdo3pR1od6vTWAYL9oFIN5pC2b0IB+Eg+v7jpy2ZHt2E4ZFLl+CB +t1rl7OOtKGsJiigELIpJ6qLcgjcBPYG0pX/qVK5dGFqcCy54VHi51OjfZ8VzsQAJ +X9D8Gy3rpDQpKuRGHT91J5sNFKVUJ400HlLalalf7cVAw6Y3N+hj2L0poyem3VVx +K19QsKkMjc8VUnrJPl59OPtuNCskROICY5QxFQIDAQABAoIBACiKID4F15ulm9QR +6bMxmgxENCw/Lvqd2HhPHfYYYtrhwP28dDiUR1MwLTDLOpS5fV7xI1Z8+hkKD1ge +HQsBclqXwgFvxkC/U+zdVNPJ3S8Ssofkjh/wghfuDBu5feoenn4GOg1v9QLi2eiw +s9qoLOdRLejv38hysBKn2ZNkV8cWmZnlu6c0LF2nKRYUWE8PMbkBX62SxTycsTcJ +nKsOh+zvvqmq+Mqflue+iEH3DAGuLpBgTaSTBsB7izvFivvr+rMfmtGOc3dpdkA6 +T6AOs2RiONo72z/1LuE6XZ59ZqsKmGPYoL8G+rq6smUlAH7SSHlNhdC0kvSx0eaM +1MWeSSECgYEA2edvDRPb5cKF+ONwznfz3gipsF4PO2zuH2az1ZdOr4jGfmP6Gval +89Fjs44tRQgb0Rof2pfHKdTega2PK11XnvzbbV/QB4OWuDpQVLXI4ay+wEYz+QDZ +EpBGSRF9OELU10NUNGv0E2iU4vtWYZ41PcnSNZw7srTmrcQ+glMd8Q0CgYEAxcP7 +m8dL4dpowRRPfi7OvsRXDznY9N6PQ3SQA1hMVWPDOCKIZd6gylVqcFz3KcuSNaF0 +CzaEnhpZaPgLBOIujPZHWroRCwTN1Po4i1m1ZvHgTc0Wy52/dMZduT/5i6t3ZW9D +z4tO/PU0NggbzLwHRX0tU3clDESvOrnRhv/JbikCgYAAybp4FKNN8GcloETvKjXi +jqt2bNCnSVyPFoL9+b8aGWeGW3wzDsNI32/53UDCqCXZmruUwcnBl/h6BZOn931y +oL12ZstMy6gG4Icb11BzqHlEHrzfYKZoT8dBSmPmiS9V2/N5AQ7v5wfqzjfgwH62 +y30MHpWEruzKohQLypsoHQKBgQCqBxJWXyVNW3L+tVCW4vVbLADRlWGDPamcTCXY +ylxueaQIqh/svVguPPuS+UqCw4PD2jijv06Lg4nlZoRKwY9WIBM+3IP0nj+84Rgm +Lo14oXHAUhhMHSbS5g0ETQ1mWJgBPITndhUGBGbMIXKNNgdmZfXHMcNHZfHlooaS +820h6QKBgBHxjtigf3wJNS1oHAPC9hv+i1ZgjBQZJ6KYVNscUBKw9eoBbn03dsqw +im3JPdGI05h/Tt2OoV9V7JH3u4SlW7y5KnOYn/tWrrPAUObOeEbmYOAZYHSALpAd +UhYLBqErq6FB3r62oPM+dkvdUD0DOVNVP9E0vCBFSQlDXpYvzg1E +-----END RSA PRIVATE KEY----- diff --git a/config-tool/utils/generate/README.md b/config-tool/utils/generate/README.md new file mode 100644 index 000000000..7de2d42f1 --- /dev/null +++ b/config-tool/utils/generate/README.md @@ -0,0 +1,189 @@ +# Creating Custom Config Definition + +By default, this tool generates a validator executable for Quay. However, one also has the option to define a custom Config definition. + +## Design: + +A Config definiton is a list of `FieldGroup`'s which contain metadata regarding a config.yaml field. This includes the field name, type, default, and validation flags. The definition will follow the following schema: + +```json +{ + "fieldGroupOne": [ + { + "name": "attributeOne", + "type": "string", + "yaml": "ATTRIBUTE_ONE", + "default": "", + "validate": "required" + }, + { + "name": "attributeTwo", + "type": "interface{}", + "yaml": "ATTRIBUTE_TWO", + "default": "", + "validate": "required", + "properties": [ + { + "name": "attributeThree", + "type": "string", + "yaml": "ATTRIBUTE_THREE", + "default": "", + "validate": "required" + } + ] + } + ], + "fieldGroupTwo": [ + { + "name": "attributeFour", + "type": "string", + "yaml": "ATTRIBUTE_FOUR", + "default": "", + "validate": "required" + } + ] +} +``` + +For each field group, a file will be made that includes struct definitions and constructors for the group and nested subfields. A `config.go` file will also be created. This will be an object that holds all of your field group structs in a map. + +## Usage + +To create your own config validator, you only need to follow two simple steps. + +1. Edit the `fieldgroups.json` file in this folder to match your desired Config definition. + +2. Run `make all` from the home directory + +3. Run `config-tool validate -c ` to see your new validator + +4. Import field group structs from the `fieldgroups` package into any external project if desired + +## Example + +To show an example of how one can create their a validator from their own Config definition, we will go through how the `SecurityScannerFieldGroup` was implemented in the Quay validator: + +1. Edit `fieldgroups.json` to the following + +```json +{ + "SecurityScannerFieldGroup": [ + { + "name": "FeatureSecurityScanner", + "yaml": "FEATURE_SECURITY_SCANNER", + "type": "bool", + "default": "false", + "validate": "omitempty" + }, + { + "name": "FeatureSecurityNotifications", + "yaml": "FEATURE_SECURITY_NOTIFICATIONS", + "type": "bool", + "default": "false", + "validate": "omitempty" + }, + { + "name": "SecurityScannerEndpoint", + "yaml": "SECURITY_SCANNER_ENDPOINT", + "type": "string", + "default": "", + "validate": "required_with=FeatureSecurityScanner" + } + ] +} +``` + +2. Run `make clean generate` + + This will remove all previously generated field groups and repopulate the fieldgroups package with your new code. If you look in the fieldgroups package, you will notice that the file `securityscannerfieldgroup.go` has been created. + +```go +package fieldgroups + +import ( + "github.com/creasty/defaults" + "github.com/go-playground/validator/v10" +) + +// SecurityScannerFieldGroup represents the SecurityScannerFieldGroup config fields +type SecurityScannerFieldGroup struct { + FeatureSecurityScanner bool `default:"false" validate:"omitempty"` + FeatureSecurityNotifications bool `default:"false" validate:"omitempty"` + SecurityScannerEndpoint string `default:"" validate:"required_with=FeatureSecurityScanner"` +} + +// NewSecurityScannerFieldGroup creates a new SecurityScannerFieldGroup +func NewSecurityScannerFieldGroup(fullConfig map[string]interface{}) FieldGroup { + newSecurityScannerFieldGroup := &SecurityScannerFieldGroup{} + defaults.Set(newSecurityScannerFieldGroup) + + if value, ok := fullConfig["FEATURE_SECURITY_SCANNER"]; ok { + newSecurityScannerFieldGroup.FeatureSecurityScanner = value.(bool) + } + if value, ok := fullConfig["FEATURE_SECURITY_NOTIFICATIONS"]; ok { + newSecurityScannerFieldGroup.FeatureSecurityNotifications = value.(bool) + } + if value, ok := fullConfig["SECURITY_SCANNER_ENDPOINT"]; ok { + newSecurityScannerFieldGroup.SecurityScannerEndpoint = value.(string) + } + + return newSecurityScannerFieldGroup +} + +// Validate checks the configuration settings for this field group +func (fg *SecurityScannerFieldGroup) Validate() validator.ValidationErrors { + validate := validator.New() + err := validate.Struct(fg) + if err == nil { + return nil + } + validationErrors := err.(validator.ValidationErrors) + return validationErrors +} +``` + +This generated file contains our constructors, struct definition, and validator function for this specific field group. This code can be exported into any external project to be used in addition to the CLI tool. + +3. Run `make install`. + +This will bundle up our newly created field group files into an executable that can be ran from the command line. + +4. Run `config-tool validate -c ` + +If we run our newly created validation tool against the following `config.yaml`, we should get an error since `SECURITY_SCANNER_ENDPOINT` must be present if `FEATURE_SECURITY_SCANNER` is on. + +`config.yaml` + +``` +FEATURE_SECURITY_SCANNER: true +FEATURE_SECURITY_NOTIFICATIONS: false +``` + +This config returns the following: + +``` ++-----------------+-------------------------+--------------------------------------+--------+ +| FIELD GROUP | FIELD | ERROR | STATUS | ++-----------------+-------------------------+--------------------------------------+--------+ +| SecurityScanner | SecurityScannerEndpoint | Field enforces tag: | 🔴 | +| | | required_with FeatureSecurityScanner | | ++-----------------+-------------------------+--------------------------------------+--------+ +``` + +If we fix our `config.yaml` to the following: + +``` +FEATURE_SECURITY_SCANNER: true +FEATURE_SECURITY_NOTIFICATIONS: false +SECURITY_SCANNER_ENDPOINT: localhost:8080 +``` + +This config returns the following: + +``` ++-----------------+-------+-------+--------+ +| FIELD GROUP | FIELD | ERROR | STATUS | ++-----------------+-------+-------+--------+ +| SecurityScanner | - | - | 🟢 | ++-----------------+-------+-------+--------+ +``` diff --git a/config-tool/utils/generate/gen.go b/config-tool/utils/generate/gen.go new file mode 100644 index 000000000..24f6f46db --- /dev/null +++ b/config-tool/utils/generate/gen.go @@ -0,0 +1,583 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "runtime" + "sort" + "strings" + + "github.com/dave/jennifer/jen" + "github.com/iancoleman/strcase" + "github.com/jojomi/go-spew/spew" +) + +// ConfigDefinition holds the information about different field groups +type ConfigDefinition map[string][]FieldDefinition + +// FieldDefinition is a struct that represents a single field from a fieldgroups.json file +type FieldDefinition struct { + Name string `json:"name"` + YAML string `json:"yaml"` + Type string `json:"type"` + Default string `json:"default"` + Validate string `json:"validate"` + Properties []FieldDefinition `json:"properties"` +} + +// JSONSchema is a type used to represent a Json Schema file +type JSONSchema struct { + Type string `json:"object"` + Description string `json:"description"` + Properties map[string]SchemaProperty `json:"properties"` +} + +// SchemaProperty is a type that holds field data from a json schema file. This is how a field is represented before it is converted to a FieldDefinition. +type SchemaProperty struct { + Type string `json:"type"` + Description string `json:"description"` + Default string `json:"ct-default"` + Validate string `json:"ct-validate"` + Items []struct { + Type string `json:"type"` + } `json:"items"` + FieldGroups []string `json:"ct-fieldgroups"` + Properties map[string]SchemaProperty `json:"properties"` +} + +//go:generate go run gen.go +func main() { + + // Read config definition file + jsonSchemaPath := getFullInputPath("schema.json") + jsonSchemaFile, err := ioutil.ReadFile(jsonSchemaPath) + if err != nil { + fmt.Println(err.Error()) + } + + // Load config definition file into struct + var jsonSchema JSONSchema + if err = json.Unmarshal(jsonSchemaFile, &jsonSchema); err != nil { + fmt.Println("error: " + err.Error()) + } + + // Convert JSON Schema to Config Definiton + configDef := jsonSchemaPropertiesToConfigDefinition(jsonSchema) + + // Create config.go + err = createConfigBase(configDef) + if err != nil { + fmt.Println(err.Error()) + } + + // Create field group files + err = createFieldGroups(configDef) + if err != nil { + fmt.Println(err.Error()) + } + +} + +/************************************************** + Generate Files +**************************************************/ + +// createFieldGroups will generate a .go file for a field group defined struct +func createFieldGroups(configDef ConfigDefinition) error { + + // For each field group, create structs, constructors, and validate function + for fgName, fields := range configDef { + + // Create file for field group + f := jen.NewFile(strings.ToLower(fgName)) + + // Import statements based on presence + f.ImportName("github.com/creasty/defaults", "defaults") + f.ImportName("github.com/go-playground/validator/v10", "validator") + f.ImportName("github.com/quay/quay/config-tool/pkg/lib/shared", "shared") + + // Struct Definitions + structsList := reverseList(generateStructs(fgName, fields, true)) + op := jen.Options{ + Open: "\n", + Multi: true, + Close: "\n", + } + f.CustomFunc(op, func(g *jen.Group) { + for _, structDef := range structsList { + g.Add(structDef) + } + }) + + // Constructor Definitions + constructorsList := reverseList(generateConstructors(fgName, fields, true)) + op = jen.Options{ + Open: "\n", + Multi: true, + Close: "\n", + } + f.CustomFunc(op, func(g *jen.Group) { + for _, constructorDef := range constructorsList { + g.Add(constructorDef) + } + }) + + // Find custom validators in field group and register + customValidators := findCustomValidator(fields) + registerValidators := jen.Empty() + op = jen.Options{ + Open: "\n", + Multi: true, + Close: "\n", + } + registerValidators.CustomFunc(op, func(g *jen.Group) { + for _, customValidator := range customValidators { + g.Add(jen.Id("validate").Dot("RegisterValidation").Call(jen.List(jen.Lit(customValidator), jen.Id(customValidator)))) + } + }) + + // Create Validator file + v := jen.NewFile(strings.ToLower(fgName)) + v.ImportName("github.com/quay/quay/config-tool/pkg/lib/shared", "shared") + v.Comment("Validate checks the configuration settings for this field group") + v.Func().Params(jen.Id("fg *" + fgName + "FieldGroup")).Id("Validate").Params().Params(jen.Index().Qual("github.com/quay/quay/config-tool/pkg/lib/shared", "ValidationError")).Block( + jen.Return(jen.Nil()), + ) + + // Create test file + t := jen.NewFile(strings.ToLower(fgName)) + t.ImportName("testing", "testing") + t.Comment("TestValidate" + fgName + " tests the Validate function") + t.Func().Id("TestValidate"+fgName).Params(jen.Id("t").Qual("testing", "T")).Block( + jen.Var().Id("tests").Op("=").Index().Struct( + jen.Id("name").String(), + jen.Id("config").Map(jen.String()).Interface(), + jen.Id("want").String(), + ).Values(jen.Values(jen.Dict{ + jen.Id("name"): jen.Lit("testOne"), + jen.Id("config"): jen.Map(jen.String()).Interface().Values(jen.Dict{}), + jen.Id("want"): jen.Lit("valid"), + })), + jen.For().List(jen.Id("_"), jen.Id("tt")).Op(":=").Range().Id("tests").Block( + jen.Qual("fmt", "Println").Call(jen.Lit("hello")), + ), + ) + + // Output files if package does not already exist + packagePath := getFullPackageOutputPath(strings.ToLower(fgName)) + if _, err := os.Stat(packagePath); os.IsNotExist(err) { + + // create directory + os.Mkdir(packagePath, 0777) + + // Generate file path + dOutfile := getFullFileOutputPath(strings.ToLower(fgName), strings.ToLower(fgName)+".go") + fmt.Println(dOutfile) + f.Save(dOutfile) + + vOutfile := getFullFileOutputPath(strings.ToLower(fgName), strings.ToLower(fgName)+"_validator.go") + if err := v.Save(vOutfile); err != nil { + fmt.Println(err.Error()) + } + + tOutfile := getFullFileOutputPath(strings.ToLower(fgName), strings.ToLower(fgName)+"_test.go") + if err := t.Save(tOutfile); err != nil { + fmt.Println(err.Error()) + } + + } + + // Implement fields function + fFile := jen.NewFile(strings.ToLower(fgName)) + fFile.Comment("Fields returns a list of strings representing the fields in this field group") + fFile.Func().Params(jen.Id("fg *" + fgName + "FieldGroup")).Id("Fields").Params().Params(jen.Index().String()).Block( + jen.Return(jen.Index().String().ValuesFunc(func(g *jen.Group) { + for _, field := range fields { + g.Lit(field.YAML) + } + }), + ), + ) + fOutfile := getFullFileOutputPath(strings.ToLower(fgName), strings.ToLower(fgName)+"_fields.go") + if err := fFile.Save(fOutfile); err != nil { + fmt.Println(err.Error()) + } + + } + return nil + +} + +// createConfigBase will create the base configuration file in the fieldgroups package +func createConfigBase(configDef ConfigDefinition) error { + + // Create file for QuayConfig + f := jen.NewFile("config") + f.ImportName("github.com/quay/quay/config-tool/pkg/lib/shared", "shared") + + // Write Config struct definition + f.Comment("Config is a struct that represents a configuration as a mapping of field groups") + f.Type().Id("Config").Map(jen.String()).Qual("github.com/quay/quay/config-tool/pkg/lib/shared", "FieldGroup") + + // Generate Config constructor block + op := jen.Options{ + Open: "\n", + Multi: true, + Close: "\n", + } + constructorBlock := jen.CustomFunc(op, func(g *jen.Group) { + g.Var().Id("err").Error() + g.Id("newConfig").Op(":=").Id("Config").Values() + for fgName := range configDef { + g.List(jen.Id("new"+fgName+"FieldGroup"), jen.Id("err")).Op(":=").Id(strings.ToLower(fgName) + ".New" + fgName + "FieldGroup").Call(jen.Id("fullConfig")) + g.If(jen.Id("err").Op("!=").Nil()).Block( + jen.Return(jen.List(jen.Id("newConfig"), jen.Id("err"))), + ) + g.Id("newConfig").Index(jen.Lit(fgName)).Op("=").Id("new" + fgName + "FieldGroup") + } + + }) + + // Write Config constructor + f.Comment("NewConfig creates a Config struct from a map[string]interface{}") + f.Func().Id("NewConfig").Params(jen.Id("fullConfig").Map(jen.String()).Interface()).Parens(jen.List(jen.Id("Config"), jen.Error())).Block(constructorBlock, jen.Return(jen.List(jen.Id("newConfig"), jen.Nil()))) + + // Define outputfile name + outfile := "config.go" + outfilePath := getFullConfigOutputPath("config", outfile) + if err := f.Save(outfilePath); err != nil { + return err + } + + return nil + +} + +/************************************************* + Generate Blocks +*************************************************/ + +// generateStructDefaults generates a struct definition block +func generateStructs(fgName string, fields []FieldDefinition, topLevel bool) (structs []*jen.Statement) { + + var innerStructs []*jen.Statement = []*jen.Statement{} + + // If top level is true, this struct is a field group + if topLevel { + fgName = fgName + "FieldGroup" + + } else { // Otherwise it is a inner struct + fgName = fgName + "Struct" + } + + op := jen.Options{ + Open: "\n", + Multi: true, + Close: "\n", + } + structBlock := jen.CustomFunc(op, func(g *jen.Group) { + + for _, field := range fields { + + // hacky fix to escape string + fieldName := field.Name + fieldDefault := strings.Replace(field.Default, `"`, `\"`, -1) + fieldValidate := field.Validate + fieldYAML := field.YAML + + switch field.Type { + case "array": + g.Id(fieldName).Index().Interface().Tag(map[string]string{"default": fieldDefault, "validate": fieldValidate, "yaml": fieldYAML}) + case "boolean": + g.Id(fieldName).Bool().Tag(map[string]string{"default": fieldDefault, "validate": fieldValidate, "yaml": fieldYAML}) + case "string": + g.Id(fieldName).String().Tag(map[string]string{"default": fieldDefault, "validate": fieldValidate, "yaml": fieldYAML}) + case "number": + g.Id(fieldName).Int().Tag(map[string]string{"default": fieldDefault, "validate": fieldValidate, "yaml": fieldYAML}) + case "object": + g.Id(fieldName).Id("*" + fieldName + "Struct").Tag(map[string]string{"default": fieldDefault, "validate": fieldValidate, "yaml": fieldYAML}) + if len(field.Properties) == 0 { + innerStructs = append(innerStructs, jen.Comment("// "+fieldName+"Struct represents the "+fieldName+" struct\n").Type().Id(fieldName+"Struct").Map(jen.String()).Interface()) + } else { + innerStructs = append(innerStructs, generateStructs(fieldName, field.Properties, false)...) + } + default: + + } + + } + }) + + structDef := jen.Comment("// " + fgName + " represents the " + fgName + " config fields\n") + structDef.Add(jen.Type().Id(fgName).Struct(structBlock)) + + return append(innerStructs, structDef) + +} + +// generateConstructorBlock generates a constructor block +func generateConstructors(fgName string, fields []FieldDefinition, topLevel bool) (constructors []*jen.Statement) { + + var innerConstructors []*jen.Statement = []*jen.Statement{} + var returnType string + + // If top level is true, this struct is a field group + if topLevel { + fgName = fgName + "FieldGroup" + returnType = "*" + fgName + + } else { // Otherwise it is a inner struct + fgName = fgName + "Struct" + returnType = "*" + fgName + } + + // Load values from map[string]interface{} + op := jen.Options{ + Open: "\n", + Multi: true, + Close: "\n", + } + setValues := jen.CustomFunc(op, func(g *jen.Group) { + + for _, field := range fields { + + // If the field is a nested struct + if field.Type == "object" { + g.If(jen.List(jen.Id("value"), jen.Id("ok")).Op(":=").Id("fullConfig").Index(jen.Lit(field.YAML)), jen.Id("ok")).Block( + jen.Var().Id("err").Error(), + jen.Id("value").Op(":=").Id("shared.FixInterface").Call(jen.Id("value").Assert(jen.Map(jen.Interface()).Interface())), + jen.List(jen.Id("new"+fgName).Dot(field.Name), jen.Id("err")).Op("=").Id("New"+field.Name+"Struct").Call(jen.Id("value")), + jen.If(jen.Id("err").Op("!=").Nil()).Block(jen.Return(jen.List(jen.Id("new"+fgName), jen.Id("err")))), + ) + + innerConstructors = append(innerConstructors, generateConstructors(field.Name, field.Properties, false)...) + + } else { // If the field is a primitive + + // Translate type to go type + var ftype string + switch field.Type { + case "array": + ftype = "[]interface{}" + case "boolean": + ftype = "bool" + case "string": + ftype = "string" + case "number": + ftype = "int" + default: + } + + g.If(jen.List(jen.Id("value"), jen.Id("ok")).Op(":=").Id("fullConfig").Index(jen.Lit(field.YAML)), jen.Id("ok")).Block( + jen.List(jen.Id("new"+fgName).Dot(field.Name), jen.Id("ok")).Op("=").Id("value").Assert(jen.Id(ftype)), + jen.If(jen.Id("!ok")).Block(jen.Return(jen.List(jen.Id("new"+fgName), jen.Qual("errors", "New").Call(jen.Lit(field.YAML+" must be of type "+ftype))))), + ) + } + + } + + }) + + constructor := jen.Comment("// New" + fgName + " creates a new " + fgName + "\n") + + // If the field is just a map[string]interface{} + if len(fields) == 0 { + constructor.Add(jen.Func().Id("New"+fgName).Params(jen.Id("fullConfig").Map(jen.String()).Interface()).Parens(jen.List(jen.Id(returnType), jen.Error())).Block( + jen.Id("new"+fgName).Op(":=").Id(fgName).Values(), + jen.For(jen.List(jen.Id("key"), jen.Id("value")).Op(":=").Range().Id("fullConfig").Block( + jen.Id("new"+fgName).Index(jen.Id("key")).Op("=").Id("value"), + )), + jen.Return(jen.List(jen.Id("&new"+fgName), jen.Nil())), + )) + } else { + constructor.Add(jen.Func().Id("New"+fgName).Params(jen.Id("fullConfig").Map(jen.String()).Interface()).Parens(jen.List(jen.Id(returnType), jen.Error())).Block( + jen.Id("new"+fgName).Op(":=").Op("&").Id(fgName).Values(), + jen.Qual("github.com/creasty/defaults", "Set").Call(jen.Id("new"+fgName)), + setValues, + jen.Return(jen.List(jen.Id("new"+fgName), jen.Nil())), + )) + } + + return append(innerConstructors, constructor) + +} + +/************************************************** + Convert Format +**************************************************/ + +// jsonSchemaToConfigDefinition converts a JSON schema file to the config definition format +func jsonSchemaPropertiesToConfigDefinition(jsonSchema JSONSchema) ConfigDefinition { + + // Create output struct + configDef := ConfigDefinition{} + + // Get properties + properties := jsonSchema.Properties + + // Sort keys to enforce consistent order + var fieldNames []string + for fieldName := range properties { + fieldNames = append(fieldNames, fieldName) + } + sort.Strings(fieldNames) + + // Iterate through keys and get data from properties + for _, fieldName := range fieldNames { + + // Get field data for this field + fieldData := properties[fieldName] + + // Convert to correct format + fieldDef := propertySchemaToFieldDefinition(fieldName, fieldData) + + // Iterate through different field groups for this specific field + for _, fgName := range fieldData.FieldGroups { + + // If field group exists, append field definition + if fg, ok := configDef[fgName]; ok { + fg := append(fg, fieldDef) + configDef[fgName] = fg + } else { // Otherwise create list + configDef[fgName] = []FieldDefinition{fieldDef} + } + + } + + } + + // Return config definition + return configDef +} + +// propertySchemaToFieldDefinition will turn a single property schema into a field definition +func propertySchemaToFieldDefinition(fieldName string, fieldData SchemaProperty) FieldDefinition { + + // Get generic information + name := strcase.ToCamel(strings.ToLower(fieldName)) + yaml := fieldName + ftype := fieldData.Type + fdefault := fieldData.Default + fvalidate := fieldData.Validate + nestedProperties := []FieldDefinition{} + + // Get nested properties + for nestedPropName, nestedPropData := range fieldData.Properties { + nestedProperties = append(nestedProperties, propertySchemaToFieldDefinition(nestedPropName, nestedPropData)) + } + + // Create field definition for with generic information + fieldDef := FieldDefinition{ + Name: name, + YAML: yaml, + Type: ftype, + Default: fdefault, + Validate: fvalidate, + Properties: nestedProperties, + } + + return fieldDef +} + +// addFieldGroupSpecificData will append field group specific data to the Field Definition +func addFieldGroupSpecificData(fieldDef FieldDefinition, fgSpecificData map[string]interface{}) FieldDefinition { + + // Check to see if append value exists + if value, ok := fgSpecificData["ct-validate"]; ok { + + // Check to determine necessity of comma + if len(fieldDef.Validate) == 0 { + fieldDef.Validate = value.(string) + } else { + fieldDef.Validate = strings.Join([]string{fieldDef.Validate, value.(string)}, ",") + } + + } + + return fieldDef +} + +/************************************************ + Helper Functions +************************************************/ + +// getFullOutputPath returns the full path to the input file +func getFullInputPath(fileName string) string { + // Get root of project + _, b, _, _ := runtime.Caller(0) + projRoot := path.Join(path.Dir(path.Dir(path.Dir(b))), path.Join("utils", "generate")) + fullPath := path.Join(projRoot, fileName) + return fullPath +} + +// getFullOutputPath returns the full path to an output file +func getFullPackageOutputPath(packageName string) string { + // Get root of project + _, b, _, _ := runtime.Caller(0) + projRoot := path.Join(path.Dir(path.Dir(path.Dir(b))), path.Join("pkg", "lib", "fieldgroups")) + fullPath := path.Join(projRoot, packageName) + return fullPath +} + +func getFullFileOutputPath(packageName, fileName string) string { + // Get root of project + _, b, _, _ := runtime.Caller(0) + projRoot := path.Join(path.Dir(path.Dir(path.Dir(b))), path.Join("pkg", "lib", "fieldgroups", packageName)) + fullPath := path.Join(projRoot, fileName) + return fullPath +} + +func getFullConfigOutputPath(packageName, fileName string) string { + // Get root of project + _, b, _, _ := runtime.Caller(0) + projRoot := path.Join(path.Dir(path.Dir(path.Dir(b))), path.Join("pkg", "lib", packageName)) + fullPath := path.Join(projRoot, fileName) + return fullPath +} + +// reverseStructOrder reverses the list of structs. TAKEN FROM https://stackoverflow.com/questions/28058278/how-do-i-reverse-a-slice-in-go +func reverseList(structs []*jen.Statement) []*jen.Statement { + for i, j := 0, len(structs)-1; i < j; i, j = i+1, j-1 { + structs[i], structs[j] = structs[j], structs[i] + } + + return structs +} + +// findCustomValidator find and register a custom validator +func findCustomValidator(fields []FieldDefinition) []string { + + // Create list of custom validators + var customValidators []string + + // Iterate through fields + for _, field := range fields { + validatorTags := strings.Split(field.Validate, ",") + + // Iterate through individual validator function + for _, tag := range validatorTags { + + // If tag has custom prefix it is a custom validator + if strings.HasPrefix(tag, "custom") { + customValidators = append(customValidators, tag) + } + } + } + + return customValidators +} + +// dumpStruct will pretty print a struct +func dumpStruct(i interface{}) string { + spew.Config.Indent = "\t" + spew.Config.DisableCapacities = true + spew.Config.DisablePointerAddresses = true + spew.Config.SortKeys = true + spew.Config.DisableMethods = true + spew.Config.DisableTypes = true + spew.Config.DisableLengths = true + return spew.Sdump(i) +} diff --git a/config-tool/utils/generate/not-done.json b/config-tool/utils/generate/not-done.json new file mode 100644 index 000000000..f7fe97e10 --- /dev/null +++ b/config-tool/utils/generate/not-done.json @@ -0,0 +1,292 @@ +{ + "required": [ + "PREFERRED_URL_SCHEME", + "SERVER_HOSTNAME", + "DB_URI", + "AUTHENTICATION_TYPE", + "DISTRIBUTED_STORAGE_CONFIG", + "BUILDLOGS_REDIS", + "USER_EVENTS_REDIS", + "DISTRIBUTED_STORAGE_PREFERENCE", + "DEFAULT_TAG_EXPIRATION", + "TAG_EXPIRATION_OPTIONS" + ], + "type": "object", + "description": "Schema for Quay configuration", + "properties": { + "DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS": { + "items": { "uniqueItems": true, "type": "string" }, + "type": "array", + "description": "The list of storage engine(s) (by ID in DISTRIBUTED_STORAGE_CONFIG) whose images should be fully replicated, by default, to all other storage engines.", + "x-example": ["s3_us_east", "s3_us_west"] + }, + "REGISTRY_STATE": { + "x-example": "readonly", + "enum": ["normal", "readonly"], + "type": "string", + "description": "The state of the registry." + }, + "FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH": { + "x-example": false, + "type": "boolean", + "description": "Whether non-encrypted passwords (as opposed to encrypted tokens) can be used for basic auth. Defaults to False" + }, + + "ENABLE_HEALTH_DEBUG_SECRET": { + "x-example": "somesecrethere", + "type": ["string", "null"], + "description": "If specified, a secret that can be given to health endpoints to see full debug info whennot authenticated as a superuser" + }, + "FEATURE_STORAGE_REPLICATION": { + "x-example": false, + "type": "boolean", + "description": "Whether to automatically replicate between storage engines. Defaults to False" + }, + "FEATURE_REQUIRE_TEAM_INVITE": { + "x-example": true, + "type": "boolean", + "description": "Whether to require invitations when adding a user to a team. Defaults to True" + }, + "USERFILES_LOCATION": { + "x-example": "s3_us_east", + "type": "string", + "description": "ID of the storage engine in which to place user-uploaded files" + }, + "DISTRIBUTED_STORAGE_PREFERENCE": { + "items": { "uniqueItems": true, "type": "string" }, + "type": "array", + "description": "The preferred storage engine(s) (by ID in DISTRIBUTED_STORAGE_CONFIG) to use. A preferred engine means it is first checked for pullig and images are pushed to it.", + "x-example": ["s3_us_east", "s3_us_west"] + }, + "FEATURE_LIBRARY_SUPPORT": { + "x-example": true, + "type": "boolean", + "description": "Whether to allow for \"namespace-less\" repositories when pulling and pushing from Docker. Defaults to True" + }, + "FEATURE_SUPER_USERS": { + "x-example": true, + "type": "boolean", + "description": "Whether super users are supported. Defaults to True" + }, + "FEATURE_GARBAGE_COLLECTION": { + "x-example": false, + "type": "boolean", + "description": "Whether garbage collection of repositories is enabled. Defaults to True" + }, + "FEATURE_PUBLIC_CATALOG": { + "x-example": false, + "type": "boolean", + "description": "If set to true, the _catalog endpoint returns public repositories. Otherwise, only private repositories can be returned. Defaults to False" + }, + "RECAPTCHA_SECRET_KEY": { + "type": ["string", "null"], + "description": "If recaptcha is enabled, the secret key for the Recaptcha service" + }, + "FEATURE_ACI_CONVERSION": { + "x-example": false, + "type": "boolean", + "description": "Whether to enable conversion to ACIs. Defaults to False" + }, + + "FEATURE_SECURITY_NOTIFICATIONS": { + "x-example": false, + "type": "boolean", + "description": "If the security scanner is enabled, whether to turn of/off security notificaitons. Defaults to False" + }, + "EXPIRED_APP_SPECIFIC_TOKEN_GC": { + "pattern": "^[0-9]+(w|m|d|h|s)$", + "type": ["string", "null"], + "description": "Duration of time expired external app tokens will remain before being garbage collected. Defaults to 1d." + }, + "FEATURE_APP_REGISTRY": { + "x-example": false, + "type": "boolean", + "description": "Whether to enable support for App repositories. Defaults to False" + }, + "V2_PAGINATION_SIZE": { + "x-example": 100, + "type": "number", + "description": "The number of results returned per page in V2 registry APIs" + }, + "PROMETHEUS_NAMESPACE": { + "x-example": "myregistry", + "type": "string", + "description": "The prefix applied to all exposed Prometheus metrics. Defaults to `quay`" + }, + + "SUCCESSIVE_TRIGGER_FAILURE_DISABLE_THRESHOLD": { + "x-example": 50, + "type": ["number", "null"], + "description": "If not None, the number of successive failures that can occur before a build trigger is automatically disabled. Defaults to 100." + }, + "FEATURE_READER_BUILD_LOGS": { + "x-example": false, + "type": "boolean", + "description": "If set to true, build logs may be read by those with read access to the repo, rather than only write access or admin access. Defaults to False" + }, + "SSL_PROTOCOLS": { + "x-example": ["TLSv1.1", "TLSv1.2"], + "type": "array", + "description": "If specified, the nginx-defined list of SSL protocols to enabled and disabled", + "x-reference": "http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols" + }, + "WEBHOOK_HOSTNAME_BLACKLIST": { + "x-example": ["somexternaldomain.com"], + "type": "array", + "description": "The set of hostnames to disallow from webhooks when validating, beyond localhost" + }, + "APP_SPECIFIC_TOKEN_EXPIRATION": { + "pattern": "^[0-9]+(w|m|d|h|s)$", + "type": ["string", "null"], + "description": "The expiration for external app tokens. Defaults to None." + }, + + "BROWSER_API_CALLS_XHR_ONLY": { + "x-example": false, + "type": "boolean", + "description": "If enabled, only API calls marked as being made by an XHR will be allowed from browsers. Defaults to True." + }, + "SESSION_COOKIE_SECURE": { + "x-example": true, + "type": "boolean", + "description": "Whether the `secure` property should be set on session cookies. Defaults to False. Recommended to be True for all installations using SSL.", + "x-reference": "https://en.wikipedia.org/wiki/Secure_cookies" + }, + "FEATURE_RECAPTCHA": { + "x-example": false, + "type": "boolean", + "description": "Whether Recaptcha is necessary for user login and recovery. Defaults to False", + "x-reference": "https://www.google.com/recaptcha/intro/" + }, + "FEATURE_AGGREGATED_LOG_COUNT_RETRIEVAL": { + "x-example": true, + "type": "boolean", + "description": "Whether to allow retrieval of aggregated log counts. Defaults to True" + }, + "SUCCESSIVE_TRIGGER_INTERNAL_ERROR_DISABLE_THRESHOLD": { + "x-example": 10, + "type": ["number", "null"], + "description": "If not None, the number of successive internal errors that can occur before a build trigger is automatically disabled. Defaults to 5." + }, + + "FEATURE_LOG_EXPORT": { + "x-example": true, + "type": "boolean", + "description": "Whether to allow exporting of action logs. Defaults to True" + }, + + "ACTION_LOG_ROTATION_THRESHOLD": { + "x-example": "30d", + "type": "string", + "description": "If action log archiving is enabled, the time interval after which to archive data." + }, + "FEATURE_PERMANENT_SESSIONS": { + "x-example": true, + "type": "boolean", + "description": "Whether sessions are permanent. Defaults to True" + }, + + "MAXIMUM_LAYER_SIZE": { + "x-example": "100G", + "type": "string", + "description": "Maximum allowed size of an image layer. Defaults to 20G", + "pattern": "^[0-9]+(G|M)$" + }, + "SUPER_USERS": { + "uniqueItems": true, + "items": { "type": "string" }, + "type": "array", + "description": "Quay usernames of those users to be granted superuser privileges" + }, + "FEATURE_ADVERTISE_V2": { + "x-example": true, + "type": "boolean", + "description": "Whether the v2/ endpoint is visible. Defaults to True" + }, + + "USERFILES_PATH": { + "x-example": "userfiles", + "type": "string", + "description": "Path under storage in which to place user-uploaded files" + }, + "BLACKLIST_V2_SPEC": { + "x-example": "<1.8.0", + "type": "string", + "description": "The Docker CLI versions to which Quay will respond that V2 is *unsupported*. Defaults to `<1.6.0`", + "x-reference": "http://pythonhosted.org/semantic_version/reference.html#semantic_version.Spec" + }, + "RECAPTCHA_SITE_KEY": { + "type": ["string", "null"], + "description": "If recaptcha is enabled, the site key for the Recaptcha service" + }, + "FEATURE_RESTRICTED_V1_PUSH": { + "x-example": false, + "type": "boolean", + "description": "If set to true, only namespaces listed in V1_PUSH_WHITELIST support V1 push. Defaults to True" + }, + "ALLOW_PULLS_WITHOUT_STRICT_LOGGING": { + "x-example": true, + "type": "boolean", + "description": "If true, pulls in which the pull audit log entry cannot be written will still succeed. Useful if the database can fallback into a read-only state and it is desired for pulls to continue during that time. Defaults to False." + }, + "LOG_ARCHIVE_PATH": { + "x-example": "archives/buildlogs", + "type": "string", + "description": "If builds are enabled, the path in storage in which to place the archived build logs." + }, + "V1_PUSH_WHITELIST": { + "x-example": ["some", "namespaces"], + "type": "array", + "description": "The array of namespace names that support V1 push if FEATURE_RESTRICTED_V1_PUSH is set to true." + }, + "DEFAULT_NAMESPACE_MAXIMUM_BUILD_COUNT": { + "x-example": 20, + "type": ["number", "null"], + "description": "If not None, the default maximum number of builds that can be queued in a namespace." + }, + + "LOG_ARCHIVE_LOCATION": { + "x-example": "s3_us_east", + "type": "string", + "description": "If builds are enabled, the storage engine in which to place the archived build logs." + }, + "HEALTH_CHECKER": { + "x-example": [ + "RDSAwareHealthCheck", + { "access_key": "foo", "secret_key": "bar" } + ], + "description": "The configured health check." + }, + + "DIRECT_OAUTH_CLIENTID_WHITELIST": { + "uniqueItems": true, + "items": { "type": "string" }, + "type": "array", + "description": "A list of client IDs of *Quay-managed* applications that are allowed to perform direct OAuth approval without user approval.", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/direct-oauth.html" + }, + "SSL_CIPHERS": { + "x-example": ["CAMELLIA", "!3DES"], + "type": "array", + "description": "If specified, the nginx-defined list of SSL ciphers to enabled and disabled", + "x-reference": "http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers" + }, + + "FEATURE_RATE_LIMITS": { + "x-example": true, + "type": "boolean", + "description": "Whether to enable rate limits on API and registry endpoints. Defaults to False" + }, + "PUBLIC_NAMESPACES": { + "uniqueItems": true, + "items": { "type": "string" }, + "type": "array", + "description": "If a namespace is defined in the public namespace list, then it will appear on *all* user's repository list pages, regardless of whether that user is a member of the namespace. Typically, this is used by an enterprise customer in configuring a set of \"well-known\" namespaces." + }, + "FEATURE_READONLY_APP_REGISTRY": { + "x-example": true, + "type": "boolean", + "description": "Whether to App repositories are read-only. Defaults to False" + } + } +} diff --git a/config-tool/utils/generate/schema.json b/config-tool/utils/generate/schema.json new file mode 100644 index 000000000..289d97192 --- /dev/null +++ b/config-tool/utils/generate/schema.json @@ -0,0 +1,1151 @@ +{ + "type": "object", + "description": "Schema for Quay configuration", + "properties": { + "FEATURE_DIRECT_LOGIN": { + "x-example": true, + "type": "boolean", + "description": "Whether users can directly login to the UI. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings", "AppTokenAuthentication"] + }, + "FEATURE_GITHUB_LOGIN": { + "x-example": false, + "type": "boolean", + "description": "Whether GitHub login is supported. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings", "GitHubLogin"] + }, + "FEATURE_GOOGLE_LOGIN": { + "x-example": false, + "type": "boolean", + "description": "Whether Google login is supported. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings", "GoogleLogin"] + }, + "FEATURE_USER_CREATION": { + "x-example": true, + "type": "boolean", + "description": "Whether users can be created (by non-super users). Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_INVITE_ONLY_USER_CREATION": { + "x-example": false, + "type": "boolean", + "description": "Whether users being created must be invited by another user. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_PARTIAL_USER_AUTOCOMPLETE": { + "x-example": true, + "type": "boolean", + "description": "If set to true, autocompletion will apply to partial usernames. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_USER_LAST_ACCESSED": { + "x-example": true, + "type": "boolean", + "description": "Whether to record the last time a user was accessed. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_USER_LOG_ACCESS": { + "x-example": true, + "type": "boolean", + "description": "If set to true, users will have access to audit logs for their namespace. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_USER_METADATA": { + "x-example": false, + "type": "boolean", + "description": "Whether to collect and support user metadata. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_USERNAME_CONFIRMATION": { + "x-example": false, + "type": "boolean", + "description": "If set to true, users can confirm their generated usernames. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_USER_RENAME": { + "x-example": true, + "type": "boolean", + "description": "If set to true, users can rename their own namespace. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_ANONYMOUS_ACCESS": { + "x-example": true, + "type": "boolean", + "description": " Whether to allow anonymous users to browse and pull public repositories. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "USER_RECOVERY_TOKEN_LIFETIME": { + "x-example": "10m", + "type": "string", + "description": "The length of time a token for recovering a user accounts is valid. Defaults to 30m.", + "pattern": "^[0-9]+(w|m|d|h|s)$", + "ct-default": "30m", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FRESH_LOGIN_TIMEOUT": { + "x-example": "5m", + "type": "string", + "description": "The time after which a fresh login requires users to reenter their password", + "ct-default": "10m", + "ct-validate": "", + "ct-fieldgroups": ["AccessSettings"] + }, + "FEATURE_ACTION_LOG_ROTATION": { + "x-example": false, + "type": "boolean", + "description": "Whether or not to rotate old action logs to storage. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["ActionLogArchiving"] + }, + "ACTION_LOG_ARCHIVE_PATH": { + "x-example": "archives/actionlogs", + "type": "string", + "description": "If action log archiving is enabled, the path in storage in which to place the archived data.", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["ActionLogArchiving"] + }, + "ACTION_LOG_ARCHIVE_LOCATION": { + "x-example": "s3_us_east", + "type": "string", + "description": "If action log archiving is enabled, the storage engine in which to place the archived data.", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["ActionLogArchiving"] + }, + "FEATURE_PROXY_STORAGE": { + "x-example": false, + "type": "boolean", + "description": "Whether to proxy all direct download URLs in storage via the registry nginx. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["DistributedStorage"] + }, + "DISTRIBUTED_STORAGE_CONFIG": { + "x-example": { + "local_storage": ["LocalStorage", { "storage_path": "some/path/" }] + }, + "type": "object", + "description": "Configuration for storage engine(s) to use in Quay. Each key is a unique ID for a storage engine, with the value being a tuple of the type and configuration for that engine.", + "patternProperties": { + "^.*$": { + "type": "array" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["ActionLogArchiving"] + }, + "REGISTRY_TITLE": { + "x-example": "Corp Container Service", + "type": "string", + "description": "If specified, the long-form title for the registry. Defaults to `Red Hat Quay`.", + "ct-default": "Project Quay", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "REGISTRY_TITLE_SHORT": { + "x-example": "CCS", + "type": "string", + "description": "If specified, the short-form title for the registry. Defaults to `Red Hat Quay`.", + "ct-default": "Project Quay", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "SEARCH_RESULTS_PER_PAGE": { + "x-example": 10, + "type": "number", + "description": "Number of results returned per page by search page. Defaults to 10", + "ct-default": "10", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "SEARCH_MAX_RESULT_PAGE_COUNT": { + "x-example": 10, + "type": "number", + "description": "Maximum number of pages the user can paginate in search before they are limited. Defaults to 10", + "ct-default": "10", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "CONTACT_INFO": { + "type": "array", + "description": "If specified, contact information to display on the contact page. If only a single piece of contact information is specified, the contact footer will link directly.", + "uniqueItems": true, + "items": [ + { + "pattern": "^mailto:(.)+$", + "type": "string", + "description": "Adds a link to send an e-mail", + "x-example": "mailto:support@quay.io" + }, + { + "pattern": "^irc://(.)+$", + "type": "string", + "description": "Adds a link to visit an IRC chat room", + "x-example": "irc://chat.freenode.net:6665/quay" + }, + { + "pattern": "^tel:(.)+$", + "type": "string", + "description": "Adds a link to call a phone number", + "x-example": "tel:+1-888-930-3475" + }, + { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "Adds a link to a defined URL", + "x-example": "https://twitter.com/quayio" + } + ], + "ct-default": "[]", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "AVATAR_KIND": { + "enum": ["local", "gravatar"], + "type": "string", + "description": "The types of avatars to display, either generated inline (local) or Gravatar (gravatar)", + "ct-default": "local", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "BRANDING": { + "required": ["logo"], + "type": "object", + "description": "Custom branding for logos and URLs in the Quay UI", + "properties": { + "logo": { + "x-example": "/static/img/quay-horizontal-color.svg", + "type": "string", + "description": "Main logo image URL", + "ct-default": "/static/img/quay-horizontal-color.svg", + "ct-validate": "url" + }, + "footer_img": { + "x-example": "/static/img/RedHat.svg", + "type": "string", + "description": "Logo for UI footer", + "ct-default": "", + "ct-validate": "url" + }, + "footer_url": { + "x-example": "https://redhat.com", + "type": "string", + "description": "Link for footer image", + "ct-default": "", + "ct-validate": "url" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["UserVisibleSettings"] + }, + "DOCUMENTATION_ROOT": { + "type": "string", + "description": "Root URL for documentation links", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["QuayDocumentation"] + }, + "FEATURE_TEAM_SYNCING": { + "x-example": true, + "type": "boolean", + "description": "Whether to allow for team membership to be synced from a backing group in the authentication engine (LDAP or Keystone)", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["TeamSyncing"] + }, + "FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP": { + "x-example": true, + "type": "boolean", + "description": "If enabled, non-superusers can setup syncing on teams to backing LDAP or Keystone. Defaults To False.", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["TeamSyncing"] + }, + "TEAM_RESYNC_STALE_TIME": { + "x-example": "2h", + "type": "string", + "description": "If team syncing is enabled for a team, how often to check its membership and resync if necessary (Default: 30m)", + "pattern": "^[0-9]+(w|m|d|h|s)$", + "ct-default": "30m", + "ct-validate": "customValidateTimePattern", + "ct-fieldgroups": ["TeamSyncing"] + }, + "AUTHENTICATION_TYPE": { + "x-example": "Database", + "enum": ["Database", "LDAP", "JWT", "Keystone", "OIDC", "AppToken"], + "type": "string", + "description": "The authentication engine to use for credential authentication.", + "ct-default": "Database", + "ct-validate": "", + "ct-fieldgroups": [ + "AccessSettings", + "AppTokenAuthentication", + "JWTAuthentication" + ] + }, + "FEATURE_APP_SPECIFIC_TOKENS": { + "x-example": false, + "type": "boolean", + "description": "If enabled, users can create tokens for use by the Docker CLI. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["AppTokenAuthentication"] + }, + "FEATURE_SECURITY_SCANNER": { + "x-example": false, + "type": "boolean", + "description": "Whether to turn of/off the security scanner. Defaults to False", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/security-scanning.html", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "SECURITY_SCANNER_ENDPOINT": { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "The endpoint for the V2 security scanner", + "x-example": "http://192.168.99.101:6060", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "SECURITY_SCANNER_NOTIFICATIONS": { + "type": "boolean", + "description": "Whether or not to the security scanner notification feature", + "x-example": "false", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "SECURITY_SCANNER_INDEXING_INTERVAL": { + "x-example": 30, + "type": "number", + "description": "The number of seconds between indexing intervals in the security scanner. Defaults to 30.", + "ct-default": "30", + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "SECURITY_SCANNER_V4_ENDPOINT": { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "The endpoint for the V4 security scanner", + "x-example": "http://192.168.99.101:6060", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "SECURITY_SCANNER_V4_NAMESPACE_WHITELIST": { + "type": "array", + "description": "The namespaces to which the security scanner should be enabled for", + "x-example": ["quay-team", "red hat"], + "ct-default": "[]", + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "SECURITY_SCANNER_V4_PSK": { + "type": "string", + "description": "If 'SECURITY_SCANNER_V4_SIGN_JWT', Quay will sign JWTs with either the key provided by `SECURITY_SCANNER_V4_PSK' (if specified here) or the Quay instance's private key otherwise.", + "x-example": "secret", + "ct-default": false, + "ct-validate": "", + "ct-fieldgroups": ["SecurityScanner"] + }, + "FEATURE_BUILD_SUPPORT": { + "x-example": true, + "type": "boolean", + "description": "Whether to support Dockerfile build. Defaults to True", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": [ + "BitbucketBuildTrigger", + "GitHubBuildTrigger", + "GitLabBuildTrigger" + ] + }, + "BITBUCKET_TRIGGER_CONFIG": { + "properties": { + "CONSUMER_SECRET": { + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "type": "string", + "description": "The registered consumer secret (client secret) for this Quay instance", + "ct-default": "", + "ct-validate": "" + }, + "CONSUMER_KEY": { + "x-example": "0e8dbe15c4c7630b6780", + "type": "string", + "description": "The registered consumer key (client ID) for this Quay instance", + "ct-default": "", + "ct-validate": "" + } + }, + "required": ["CONSUMER_KEY", "CONSUMER_SECRET"], + "type": "object", + "description": "Configuration for using BitBucket for build triggers", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/bitbucket-build.html", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["BitbucketBuildTrigger"] + }, + "FEATURE_BITBUCKET_BUILD": { + "x-example": false, + "type": "boolean", + "description": "Whether to support Bitbucket build triggers. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["BitbucketBuildTrigger"] + }, + "DB_URI": { + "x-example": "mysql+pymysql://username:password@dns.of.database/quay", + "type": "string", + "description": "The URI at which to access the database, including any credentials.", + "x-reference": "https://www.postgresql.org/docs/9.3/static/libpq-connect.html#AEN39495", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Database"] + }, + "DB_CONNECTION_ARGS": { + "required": ["threadlocals", "autorollback"], + "type": "object", + "description": "If specified, connection arguments for the database such as timeouts and SSL.", + "properties": { + "ssl": { + "required": ["ca"], + "type": "object", + "description": "SSL connection configuration", + "properties": { + "ca": { + "x-example": "conf/stack/ssl-ca-cert.pem", + "type": "string", + "description": "*Absolute container path* to the CA certificate to use for SSL connections", + "ct-default": "", + "ct-validate": "" + } + } + }, + "threadlocals": { + "type": "boolean", + "description": "Whether to use thread-local connections. Should *ALWAYS* be `true`", + "ct-default": "true", + "ct-validate": "" + }, + "autorollback": { + "type": "boolean", + "description": "Whether to use auto-rollback connections. Should *ALWAYS* be `true`", + "ct-default": "true", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Database"] + }, + "LOGS_MODEL": { + "x-example": "database", + "enum": ["database", "transition_reads_both_writes_es", "elasticsearch"], + "type": "string", + "description": "Logs model for action logs", + "ct-default": "database", + "ct-validate": "", + "ct-fieldgroups": ["ElasticSearch"] + }, + "LOGS_MODEL_CONFIG": { + "properties": { + "elasticsearch_config": { + "type": "object", + "description": "Elasticsearch cluster configuration", + "properties": { + "access_key": { + "x-example": "some_string", + "type": "string", + "description": "Elasticsearch user (or IAM key for AWS ES)", + "ct-default": "", + "ct-validate": "" + }, + "host": { + "x-example": "host.elasticsearch.example", + "type": "string", + "description": "Elasticsearch cluster endpoint", + "ct-default": "", + "ct-validate": "" + }, + "index_prefix": { + "x-example": "logentry_", + "type": "string", + "description": "Elasticsearch's index prefix", + "ct-default": "logentry_", + "ct-validate": "" + }, + "index_settings": { + "type": "object", + "description": "Elasticsearch's index settings", + "ct-default": "", + "ct-validate": "" + }, + "use_ssl": { + "x-example": true, + "type": "boolean", + "description": "Use ssl for Elasticsearch. Defaults to True", + "ct-default": "true", + "ct-validate": "" + }, + "secret_key": { + "x-example": "some_secret_string", + "type": "string", + "description": "Elasticsearch password (or IAM secret for AWS ES)", + "ct-default": "", + "ct-validate": "" + }, + "aws_region": { + "x-example": "us-east-1", + "type": "string", + "description": "Amazon web service region", + "ct-default": "", + "ct-validate": "" + }, + "port": { + "x-example": 1234, + "type": "number", + "description": "Elasticsearch cluster endpoint port", + "ct-default": "", + "ct-validate": "" + } + } + }, + "kinesis_stream_config": { + "type": "object", + "description": "AWS Kinesis Stream configuration", + "properties": { + "aws_secret_key": { + "x-example": "some_secret_key", + "type": "string", + "description": "AWS secret key", + "ct-default": "", + "ct-validate": "" + }, + "stream_name": { + "x-example": "logentry-kinesis-stream", + "type": "string", + "description": "Kinesis stream to send action logs to", + "ct-default": "", + "ct-validate": "" + }, + "aws_access_key": { + "x-example": "some_access_key", + "type": "string", + "description": "AWS access key", + "ct-default": "", + "ct-validate": "" + }, + "retries": { + "x-example": 5, + "type": "number", + "description": "Max number of attempts made on a single request", + "ct-default": "", + "ct-validate": "" + }, + "read_timeout": { + "x-example": 5, + "type": "number", + "description": "Number of seconds before timeout when reading from a connection", + "ct-default": "", + "ct-validate": "" + }, + "max_pool_connections": { + "x-example": 10, + "type": "number", + "description": "The maximum number of connections to keep in a connection pool", + "ct-default": "", + "ct-validate": "" + }, + "aws_region": { + "x-example": "us-east-1", + "type": "string", + "description": "AWS region", + "ct-default": "", + "ct-validate": "" + }, + "connect_timeout": { + "x-example": 5, + "type": "number", + "description": "Number of seconds before timeout when attempting to make a connection", + "ct-default": "", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "" + }, + "producer": { + "x-example": "kafka", + "enum": ["kafka", "elasticsearch", "kinesis_stream"], + "type": "string", + "description": "Logs producer if logging to Elasticsearch", + "ct-default": "", + "ct-validate": "" + }, + "kafka_config": { + "type": "object", + "description": "Kafka cluster configuration", + "properties": { + "topic": { + "x-example": "logentry", + "type": "string", + "description": "Kafka topic to publish log entries to", + "ct-default": "", + "ct-validate": "" + }, + "bootstrap_servers": { + "uniqueItems": true, + "items": [{ "type": "string" }], + "type": "array", + "description": "List of Kafka brokers to bootstrap the client from", + "ct-default": "", + "ct-validate": "" + }, + "max_block_seconds": { + "x-example": 10, + "type": "number", + "description": "Max number of seconds to block during a `send()`, either because the buffer is full or metadata unavailable", + "ct-default": "", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "" + } + }, + "type": "object", + "description": "Logs model config for action logs", + "x-reference": "https://www.elastic.co/guide/en/elasticsearch/guide/master/_index_settings.html", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["ElasticSearch"] + }, + "FEATURE_GITHUB_BUILD": { + "x-example": false, + "type": "boolean", + "description": "Whether to support GitHub build triggers. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["GitHubBuildTrigger"] + }, + "GITHUB_TRIGGER_CONFIG": { + "properties": { + "ALLOWED_ORGANIZATIONS": { + "uniqueItems": true, + "items": [{ "type": "string" }], + "type": "array", + "description": "The names of the GitHub (Enterprise) organizations whitelisted to work with the ORG_RESTRICT option", + "ct-default": "[]", + "ct-validate": "" + }, + "ORG_RESTRICT": { + "x-example": true, + "type": "boolean", + "description": "If true, only users within the organization whitelist can login using this provider", + "ct-default": "false", + "ct-validate": "" + }, + "API_ENDPOINT": { + "x-example": "https://api.github.com/", + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) API to use. Must be overridden for github.com", + "ct-default": "", + "ct-validate": "" + }, + "CLIENT_SECRET": { + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "type": "string", + "description": "The registered client secret for this Quay instance", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + "ct-default": "", + "ct-validate": "" + }, + "GITHUB_ENDPOINT": { + "x-example": "https://github.com/", + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) being hit", + "ct-default": "", + "ct-validate": "" + }, + "CLIENT_ID": { + "x-example": "0e8dbe15c4c7630b6780", + "type": "string", + "description": "The registered client ID for this Quay instance; cannot be shared with GITHUB_LOGIN_CONFIG", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + "ct-default": "", + "ct-validate": "" + } + }, + "required": ["GITHUB_ENDPOINT", "CLIENT_ID", "CLIENT_SECRET"], + "type": "object", + "description": "Configuration for using GitHub (Enterprise) for build triggers", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-build.html", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["GitHubBuildTrigger"] + }, + "GITHUB_LOGIN_CONFIG": { + "properties": { + "ALLOWED_ORGANIZATIONS": { + "uniqueItems": true, + "items": [{ "type": "string" }], + "type": "array", + "description": "The names of the GitHub (Enterprise) organizations whitelisted to work with the ORG_RESTRICT option", + "ct-default": "[]", + "ct-validate": "" + }, + "ORG_RESTRICT": { + "x-example": true, + "type": "boolean", + "description": "If true, only users within the organization whitelist can login using this provider", + "ct-default": "false", + "ct-validate": "" + }, + "API_ENDPOINT": { + "x-example": "https://api.github.com/", + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) API to use. Must be overridden for github.com", + "ct-default": "", + "ct-validate": "" + }, + "GITHUB_ENDPOINT": { + "x-example": "https://github.com/", + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) being hit", + "ct-default": "", + "ct-validate": "" + }, + "CLIENT_ID": { + "x-example": "0e8dbe15c4c7630b6780", + "type": "string", + "description": "The registered client ID for this Quay instance; cannot be shared with GITHUB_TRIGGER_CONFIG", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + "ct-default": "", + "ct-validate": "" + }, + "CLIENT_SECRET": { + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "type": "string", + "description": "The registered client secret for this Quay instance", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + "ct-default": "", + "ct-validate": "" + } + }, + "required": ["CLIENT_ID", "CLIENT_SECRET"], + "type": "object", + "description": "Configuration for using GitHub (Enterprise) as an external login provider", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-auth.html", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["GitHubLogin"] + }, + "FEATURE_GITLAB_BUILD": { + "x-example": false, + "type": "boolean", + "description": "Whether to support GitLab build triggers. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["GitLabBuildTrigger"] + }, + "GITLAB_TRIGGER_CONFIG": { + "required": ["GITLAB_ENDPOINT", "CLIENT_ID", "CLIENT_SECRET"], + "type": "object", + "description": "Configuration for using Gitlab (Enterprise) for external authentication", + "properties": { + "CLIENT_SECRET": { + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "type": "string", + "description": "The registered client secret for this Quay instance", + "ct-default": "", + "ct-validate": "" + }, + "GITLAB_ENDPOINT": { + "x-example": "https://gitlab.com", + "type": "string", + "description": "The endpoint at which Gitlab(Enterprise) is running", + "ct-default": "", + "ct-validate": "" + }, + "CLIENT_ID": { + "x-example": "0e8dbe15c4c7630b6780", + "type": "string", + "description": "The registered client ID for this Quay instance", + "ct-default": "", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["GitLabBuildTrigger"] + }, + "GOOGLE_LOGIN_CONFIG": { + "required": ["CLIENT_ID", "CLIENT_SECRET"], + "type": "object", + "description": "Configuration for using Google for external authentication", + "properties": { + "CLIENT_SECRET": { + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "type": "string", + "description": "The registered client secret for this Quay instance", + "ct-default": "", + "ct-validate": "" + }, + "CLIENT_ID": { + "x-example": "0e8dbe15c4c7630b6780", + "type": "string", + "description": "The registered client ID for this Quay instance", + "ct-default": "", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["GoogleLogin"] + }, + "JWT_VERIFY_ENDPOINT": { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "The endpoint for JWT verification", + "x-example": "http://192.168.99.101:6060", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["JWTAuthentication"] + }, + "JWT_QUERY_ENDPOINT": { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "The endpoint for JWT queries", + "x-example": "http://192.168.99.101:6060", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["JWTAuthentication"] + }, + "JWT_GETUSER_ENDPOINT": { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "The endpoint for JWT users", + "x-example": "http://192.168.99.101:6060", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["JWTAuthentication"] + }, + "JWT_AUTH_ISSUER": { + "pattern": "^http(s)?://(.)+$", + "type": "string", + "description": "The endpoint for JWT users", + "x-example": "http://192.168.99.101:6060", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["JWTAuthentication"] + }, + "FEATURE_MAILING": { + "x-example": true, + "type": "boolean", + "description": "Whether emails are enabled. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["JWTAuthentication", "Email"] + }, + "BUILDLOGS_REDIS": { + "required": ["host"], + "type": "object", + "description": "Connection information for Redis for build logs caching", + "properties": { + "host": { + "x-example": "my.redis.cluster", + "type": "string", + "description": "The hostname at which Redis is accessible", + "ct-default": "", + "ct-validate": "" + }, + "password": { + "x-example": "mypassword", + "type": "string", + "description": "The password to connect to the Redis instance", + "ct-default": "", + "ct-validate": "" + }, + "port": { + "x-example": 1234, + "type": "number", + "description": "The port at which Redis is accessible", + "ct-default": "", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Redis"] + }, + "USER_EVENTS_REDIS": { + "required": ["host"], + "type": "object", + "description": "Connection information for Redis for build logs caching", + "properties": { + "host": { + "x-example": "my.redis.cluster", + "type": "string", + "description": "The hostname at which Redis is accessible", + "ct-default": "", + "ct-validate": "" + }, + "password": { + "x-example": "mypassword", + "type": "string", + "description": "The password to connect to the Redis instance", + "ct-default": "", + "ct-validate": "" + }, + "port": { + "x-example": 1234, + "type": "number", + "description": "The port at which Redis is accessible", + "ct-default": "", + "ct-validate": "" + } + }, + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Redis"] + }, + "SERVER_HOSTNAME": { + "x-example": "quay.io", + "type": "string", + "description": "The URL at which Quay is accessible, without the scheme.", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["HostSettings"] + }, + "PREFERRED_URL_SCHEME": { + "x-example": "https", + "enum": ["http", "https"], + "type": "string", + "description": "The URL scheme to use when hitting Quay. If Quay is behind SSL *at all*, this *must* be `https`", + "ct-default": "http", + "ct-validate": "", + "ct-fieldgroups": ["HostSettings"] + }, + "EXTERNAL_TLS_TERMINATION": { + "x-example": true, + "type": "boolean", + "description": "If TLS is supported, but terminated at a layer before Quay, must be true.", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["HostSettings"] + }, + "FEATURE_REPO_MIRROR": { + "x-example": false, + "type": "boolean", + "description": "Whether to enable support for repository mirroring. Defaults to False", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["RepoMirror"] + }, + "REPO_MIRROR_TLS_VERIFY": { + "x-example": true, + "type": "boolean", + "description": "Require HTTPS and verify certificates of Quay registry during mirror. Defaults to True", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["RepoMirror"] + }, + "REPO_MIRROR_INTERVAL": { + "x-example": 30, + "type": "number", + "description": "The number of seconds between checking for repository mirror candidates. Defaults to 30.", + "ct-default": "30", + "ct-validate": "", + "ct-fieldgroups": ["RepoMirror"] + }, + "REPO_MIRROR_SERVER_HOSTNAME": { + "x-example": "openshift-quay-service", + "type": "string", + "description": "Replaces the SERVER_HOSTNAME as the destination for mirroring. Defaults to unset", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["RepoMirror"] + }, + "MAIL_SERVER": { + "x-example": "smtp.somedomain.com", + "type": "string", + "description": "The SMTP server to use for sending e-mails. Only required if FEATURE_MAILING is set to true.", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "MAIL_PORT": { + "x-example": 588, + "type": "number", + "description": "The SMTP port to use. If not specified, defaults to 587.", + "ct-default": "587", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "FEATURE_BLACKLISTED_EMAILS": { + "x-example": false, + "type": "boolean", + "description": "If set to true, no new User accounts may be created if their email domain is blacklisted.", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "BLACKLISTED_EMAIL_DOMAINS": { + "x-example": ["example.com", "example.org"], + "type": "array", + "description": "The array of email-address domains that is used if FEATURE_BLACKLISTED_EMAILS is set to true.", + "ct-default": "[]", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "MAIL_USE_AUTH": { + "x-example": "myuser", + "type": "boolean", + "description": "Whether or not to use authentication for mail server.", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "MAIL_USERNAME": { + "x-example": "myuser", + "type": "string", + "description": "The SMTP username to use when sending e-mails.", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "MAIL_PASSWORD": { + "x-example": "mypassword", + "type": "string", + "description": "The SMTP password to use when sending e-mails.", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "MAIL_DEFAULT_SENDER": { + "x-example": "support@myco.com", + "type": "string", + "description": "If specified, the e-mail address used as the `from` when Quay sends e-mails. If none, defaults to `support@quay.io`.", + "ct-default": "support@quay.io", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "MAIL_USE_TLS": { + "x-example": true, + "type": "boolean", + "description": "If specified, whether to use TLS for sending e-mails.", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["Email"] + }, + "FEATURE_CHANGE_TAG_EXPIRATION": { + "x-example": false, + "type": "boolean", + "description": "Whether users and organizations are allowed to change the tag expiration for tags in their namespace. Defaults to True.", + "ct-default": "true", + "ct-validate": "", + "ct-fieldgroups": ["TimeMachine"] + }, + "TAG_EXPIRATION_OPTIONS": { + "items": { "pattern": "^[0-9]+(w|m|d|h|s)$", "type": "string" }, + "type": "array", + "description": "The options that users can select for expiration of tags in their namespace (if enabled)", + "ct-default": "[2w]", + "ct-validate": "", + "ct-fieldgroups": ["TimeMachine"] + }, + "DEFAULT_TAG_EXPIRATION": { + "pattern": "^[0-9]+(w|m|d|h|s)$", + "type": "string", + "description": "The default, configurable tag expiration time for time machine. Defaults to `2w`.", + "ct-default": "2w", + "ct-validate": "", + "ct-fieldgroups": ["TimeMachine"] + }, + "LDAP_ADMIN_DN": { + "type": "string", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_ADMIN_PASSWD": { + "type": "string", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_URI": { + "type": "string", + "ct-default": "ldap://localhost", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_ALLOW_INSECURE_FALLBACK": { + "type": "boolean", + "ct-default": "false", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_BASE_DN": { + "type": "string", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_USER_RDN": { + "type": "array", + "ct-default": "[]", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_UID_ATTR": { + "type": "string", + "ct-default": "uid", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_EMAIL_ATTR": { + "type": "string", + "ct-default": "mail", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + }, + "LDAP_USER_FILTER": { + "type": "string", + "ct-default": "", + "ct-validate": "", + "ct-fieldgroups": ["LDAP"] + } + } +} diff --git a/config-tool/utils/scripts/dumpschema.py b/config-tool/utils/scripts/dumpschema.py new file mode 100644 index 000000000..a07dd0ec5 --- /dev/null +++ b/config-tool/utils/scripts/dumpschema.py @@ -0,0 +1,1059 @@ +import json + +CONFIG_SCHEMA = { + "type": "object", + "description": "Schema for Quay configuration", + "required": [ + "PREFERRED_URL_SCHEME", + "SERVER_HOSTNAME", + "DB_URI", + "AUTHENTICATION_TYPE", + "DISTRIBUTED_STORAGE_CONFIG", + "BUILDLOGS_REDIS", + "USER_EVENTS_REDIS", + "DISTRIBUTED_STORAGE_PREFERENCE", + "DEFAULT_TAG_EXPIRATION", + "TAG_EXPIRATION_OPTIONS", + ], + "properties": { + "REGISTRY_STATE": { + "type": "string", + "description": "The state of the registry.", + "enum": ["normal", "readonly"], + "x-example": "readonly", + }, + # Hosting. + "PREFERRED_URL_SCHEME": { + "type": "string", + "description": "The URL scheme to use when hitting Quay. If Quay is behind SSL *at all*, this *must* be `https`", + "enum": ["http", "https"], + "x-example": "https", + }, + "SERVER_HOSTNAME": { + "type": "string", + "description": "The URL at which Quay is accessible, without the scheme.", + "x-example": "quay.io", + }, + "EXTERNAL_TLS_TERMINATION": { + "type": "boolean", + "description": "If TLS is supported, but terminated at a layer before Quay, must be true.", + "x-example": True, + }, + # SSL/TLS. + "SSL_CIPHERS": { + "type": "array", + "description": "If specified, the nginx-defined list of SSL ciphers to enabled and disabled", + "x-example": ["CAMELLIA", "!3DES"], + "x-reference": "http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers", + }, + "SSL_PROTOCOLS": { + "type": "array", + "description": "If specified, the nginx-defined list of SSL protocols to enabled and disabled", + "x-example": ["TLSv1.1", "TLSv1.2"], + "x-reference": "http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols", + }, + # User-visible configuration. + "REGISTRY_TITLE": { + "type": "string", + "description": "If specified, the long-form title for the registry. Defaults to `Red Hat Quay`.", + "x-example": "Corp Container Service", + }, + "REGISTRY_TITLE_SHORT": { + "type": "string", + "description": "If specified, the short-form title for the registry. Defaults to `Red Hat Quay`.", + "x-example": "CCS", + }, + "CONTACT_INFO": { + "type": "array", + "uniqueItems": True, + "description": "If specified, contact information to display on the contact page. " + + "If only a single piece of contact information is specified, the contact footer will link directly.", + "items": [ + { + "type": "string", + "pattern": "^mailto:(.)+$", + "x-example": "mailto:support@quay.io", + "description": "Adds a link to send an e-mail", + }, + { + "type": "string", + "pattern": "^irc://(.)+$", + "x-example": "irc://chat.freenode.net:6665/quay", + "description": "Adds a link to visit an IRC chat room", + }, + { + "type": "string", + "pattern": "^tel:(.)+$", + "x-example": "tel:+1-888-930-3475", + "description": "Adds a link to call a phone number", + }, + { + "type": "string", + "pattern": "^http(s)?://(.)+$", + "x-example": "https://twitter.com/quayio", + "description": "Adds a link to a defined URL", + }, + ], + }, + "SEARCH_RESULTS_PER_PAGE": { + "type": "number", + "description": "Number of results returned per page by search page. Defaults to 10", + "x-example": 10, + }, + "SEARCH_MAX_RESULT_PAGE_COUNT": { + "type": "number", + "description": "Maximum number of pages the user can paginate in search before they are limited. Defaults to 10", + "x-example": 10, + }, + # E-mail. + "FEATURE_MAILING": { + "type": "boolean", + "description": "Whether emails are enabled. Defaults to True", + "x-example": True, + }, + "MAIL_SERVER": { + "type": "string", + "description": "The SMTP server to use for sending e-mails. Only required if FEATURE_MAILING is set to true.", + "x-example": "smtp.somedomain.com", + }, + "MAIL_USE_TLS": { + "type": "boolean", + "description": "If specified, whether to use TLS for sending e-mails.", + "x-example": True, + }, + "MAIL_PORT": { + "type": "number", + "description": "The SMTP port to use. If not specified, defaults to 587.", + "x-example": 588, + }, + "MAIL_USERNAME": { + "type": ["string", "null"], + "description": "The SMTP username to use when sending e-mails.", + "x-example": "myuser", + }, + "MAIL_PASSWORD": { + "type": ["string", "null"], + "description": "The SMTP password to use when sending e-mails.", + "x-example": "mypassword", + }, + "MAIL_DEFAULT_SENDER": { + "type": ["string", "null"], + "description": "If specified, the e-mail address used as the `from` when Quay sends e-mails. If none, defaults to `support@quay.io`.", + "x-example": "support@myco.com", + }, + # Database. + "DB_URI": { + "type": "string", + "description": "The URI at which to access the database, including any credentials.", + "x-example": "mysql+pymysql://username:password@dns.of.database/quay", + "x-reference": "https://www.postgresql.org/docs/9.3/static/libpq-connect.html#AEN39495", + }, + "DB_CONNECTION_ARGS": { + "type": "object", + "description": "If specified, connection arguments for the database such as timeouts and SSL.", + "properties": { + "threadlocals": { + "type": "boolean", + "description": "Whether to use thread-local connections. Should *ALWAYS* be `true`", + }, + "autorollback": { + "type": "boolean", + "description": "Whether to use auto-rollback connections. Should *ALWAYS* be `true`", + }, + "ssl": { + "type": "object", + "description": "SSL connection configuration", + "properties": { + "ca": { + "type": "string", + "description": "*Absolute container path* to the CA certificate to use for SSL connections", + "x-example": "conf/stack/ssl-ca-cert.pem", + }, + }, + "required": ["ca"], + }, + }, + "required": ["threadlocals", "autorollback"], + }, + "ALLOW_PULLS_WITHOUT_STRICT_LOGGING": { + "type": "boolean", + "description": "If true, pulls in which the pull audit log entry cannot be written will " + + "still succeed. Useful if the database can fallback into a read-only state " + + "and it is desired for pulls to continue during that time. Defaults to False.", + "x-example": True, + }, + # Storage. + "FEATURE_STORAGE_REPLICATION": { + "type": "boolean", + "description": "Whether to automatically replicate between storage engines. Defaults to False", + "x-example": False, + }, + "FEATURE_PROXY_STORAGE": { + "type": "boolean", + "description": "Whether to proxy all direct download URLs in storage via the registry nginx. Defaults to False", + "x-example": False, + }, + "MAXIMUM_LAYER_SIZE": { + "type": "string", + "description": "Maximum allowed size of an image layer. Defaults to 20G", + "x-example": "100G", + "pattern": "^[0-9]+(G|M)$", + }, + "DISTRIBUTED_STORAGE_CONFIG": { + "type": "object", + "description": "Configuration for storage engine(s) to use in Quay. Each key is a unique ID" + + " for a storage engine, with the value being a tuple of the type and " + + " configuration for that engine.", + "x-example": { + "local_storage": ["LocalStorage", {"storage_path": "some/path/"}], + }, + "items": { + "type": "array", + }, + }, + "DISTRIBUTED_STORAGE_PREFERENCE": { + "type": "array", + "description": "The preferred storage engine(s) (by ID in DISTRIBUTED_STORAGE_CONFIG) to " + + "use. A preferred engine means it is first checked for pullig and images are " + + "pushed to it.", + "items": { + "type": "string", + "uniqueItems": True, + }, + "x-example": ["s3_us_east", "s3_us_west"], + }, + "DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS": { + "type": "array", + "description": "The list of storage engine(s) (by ID in DISTRIBUTED_STORAGE_CONFIG) whose " + + "images should be fully replicated, by default, to all other storage engines.", + "items": { + "type": "string", + "uniqueItems": True, + }, + "x-example": ["s3_us_east", "s3_us_west"], + }, + "USERFILES_LOCATION": { + "type": "string", + "description": "ID of the storage engine in which to place user-uploaded files", + "x-example": "s3_us_east", + }, + "USERFILES_PATH": { + "type": "string", + "description": "Path under storage in which to place user-uploaded files", + "x-example": "userfiles", + }, + "ACTION_LOG_ARCHIVE_LOCATION": { + "type": "string", + "description": "If action log archiving is enabled, the storage engine in which to place the " + + "archived data.", + "x-example": "s3_us_east", + }, + "ACTION_LOG_ARCHIVE_PATH": { + "type": "string", + "description": "If action log archiving is enabled, the path in storage in which to place the " + + "archived data.", + "x-example": "archives/actionlogs", + }, + "ACTION_LOG_ROTATION_THRESHOLD": { + "type": "string", + "description": "If action log archiving is enabled, the time interval after which to " + + "archive data.", + "x-example": "30d", + }, + "LOG_ARCHIVE_LOCATION": { + "type": "string", + "description": "If builds are enabled, the storage engine in which to place the " + + "archived build logs.", + "x-example": "s3_us_east", + }, + "LOG_ARCHIVE_PATH": { + "type": "string", + "description": "If builds are enabled, the path in storage in which to place the " + + "archived build logs.", + "x-example": "archives/buildlogs", + }, + # Authentication. + "AUTHENTICATION_TYPE": { + "type": "string", + "description": "The authentication engine to use for credential authentication.", + "x-example": "Database", + "enum": ["Database", "LDAP", "JWT", "Keystone", "OIDC", "AppToken"], + }, + "SUPER_USERS": { + "type": "array", + "description": "Quay usernames of those users to be granted superuser privileges", + "uniqueItems": True, + "items": { + "type": "string", + }, + }, + "DIRECT_OAUTH_CLIENTID_WHITELIST": { + "type": "array", + "description": "A list of client IDs of *Quay-managed* applications that are allowed " + + "to perform direct OAuth approval without user approval.", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/direct-oauth.html", + "uniqueItems": True, + "items": { + "type": "string", + }, + }, + # Redis. + "BUILDLOGS_REDIS": { + "type": "object", + "description": "Connection information for Redis for build logs caching", + "required": ["host"], + "properties": { + "host": { + "type": "string", + "description": "The hostname at which Redis is accessible", + "x-example": "my.redis.cluster", + }, + "port": { + "type": "number", + "description": "The port at which Redis is accessible", + "x-example": 1234, + }, + "password": { + "type": "string", + "description": "The password to connect to the Redis instance", + "x-example": "mypassword", + }, + }, + }, + "USER_EVENTS_REDIS": { + "type": "object", + "description": "Connection information for Redis for user event handling", + "required": ["host"], + "properties": { + "host": { + "type": "string", + "description": "The hostname at which Redis is accessible", + "x-example": "my.redis.cluster", + }, + "port": { + "type": "number", + "description": "The port at which Redis is accessible", + "x-example": 1234, + }, + "password": { + "type": "string", + "description": "The password to connect to the Redis instance", + "x-example": "mypassword", + }, + }, + }, + # OAuth configuration. + "GITHUB_LOGIN_CONFIG": { + "type": ["object", "null"], + "description": "Configuration for using GitHub (Enterprise) as an external login provider", + "required": ["CLIENT_ID", "CLIENT_SECRET"], + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-auth.html", + "properties": { + "GITHUB_ENDPOINT": { + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) being hit", + "x-example": "https://github.com/", + }, + "API_ENDPOINT": { + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) API to use. Must be overridden for github.com", + "x-example": "https://api.github.com/", + }, + "CLIENT_ID": { + "type": "string", + "description": "The registered client ID for this Quay instance; cannot be shared with GITHUB_TRIGGER_CONFIG", + "x-example": "0e8dbe15c4c7630b6780", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + }, + "CLIENT_SECRET": { + "type": "string", + "description": "The registered client secret for this Quay instance", + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + }, + "ORG_RESTRICT": { + "type": "boolean", + "description": "If true, only users within the organization whitelist can login using this provider", + "x-example": True, + }, + "ALLOWED_ORGANIZATIONS": { + "type": "array", + "description": "The names of the GitHub (Enterprise) organizations whitelisted to work with the ORG_RESTRICT option", + "uniqueItems": True, + "items": { + "type": "string", + }, + }, + }, + }, + "BITBUCKET_TRIGGER_CONFIG": { + "type": ["object", "null"], + "description": "Configuration for using BitBucket for build triggers", + "required": ["CONSUMER_KEY", "CONSUMER_SECRET"], + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/bitbucket-build.html", + "properties": { + "CONSUMER_KEY": { + "type": "string", + "description": "The registered consumer key (client ID) for this Quay instance", + "x-example": "0e8dbe15c4c7630b6780", + }, + "CONSUMER_SECRET": { + "type": "string", + "description": "The registered consumer secret (client secret) for this Quay instance", + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + }, + }, + }, + "GITHUB_TRIGGER_CONFIG": { + "type": ["object", "null"], + "description": "Configuration for using GitHub (Enterprise) for build triggers", + "required": ["GITHUB_ENDPOINT", "CLIENT_ID", "CLIENT_SECRET"], + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-build.html", + "properties": { + "GITHUB_ENDPOINT": { + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) being hit", + "x-example": "https://github.com/", + }, + "API_ENDPOINT": { + "type": "string", + "description": "The endpoint of the GitHub (Enterprise) API to use. Must be overridden for github.com", + "x-example": "https://api.github.com/", + }, + "CLIENT_ID": { + "type": "string", + "description": "The registered client ID for this Quay instance; cannot be shared with GITHUB_LOGIN_CONFIG", + "x-example": "0e8dbe15c4c7630b6780", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + }, + "CLIENT_SECRET": { + "type": "string", + "description": "The registered client secret for this Quay instance", + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/github-app.html", + }, + }, + }, + "GOOGLE_LOGIN_CONFIG": { + "type": ["object", "null"], + "description": "Configuration for using Google for external authentication", + "required": ["CLIENT_ID", "CLIENT_SECRET"], + "properties": { + "CLIENT_ID": { + "type": "string", + "description": "The registered client ID for this Quay instance", + "x-example": "0e8dbe15c4c7630b6780", + }, + "CLIENT_SECRET": { + "type": "string", + "description": "The registered client secret for this Quay instance", + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + }, + }, + }, + "GITLAB_TRIGGER_CONFIG": { + "type": ["object", "null"], + "description": "Configuration for using Gitlab (Enterprise) for external authentication", + "required": ["GITLAB_ENDPOINT", "CLIENT_ID", "CLIENT_SECRET"], + "properties": { + "GITLAB_ENDPOINT": { + "type": "string", + "description": "The endpoint at which Gitlab(Enterprise) is running", + "x-example": "https://gitlab.com", + }, + "CLIENT_ID": { + "type": "string", + "description": "The registered client ID for this Quay instance", + "x-example": "0e8dbe15c4c7630b6780", + }, + "CLIENT_SECRET": { + "type": "string", + "description": "The registered client secret for this Quay instance", + "x-example": "e4a58ddd3d7408b7aec109e85564a0d153d3e846", + }, + }, + }, + "BRANDING": { + "type": ["object", "null"], + "description": "Custom branding for logos and URLs in the Quay UI", + "required": ["logo"], + "properties": { + "logo": { + "type": "string", + "description": "Main logo image URL", + "x-example": "/static/img/quay-horizontal-color.svg", + }, + "footer_img": { + "type": "string", + "description": "Logo for UI footer", + "x-example": "/static/img/RedHat.svg", + }, + "footer_url": { + "type": "string", + "description": "Link for footer image", + "x-example": "https://redhat.com", + }, + }, + }, + "DOCUMENTATION_ROOT": {"type": "string", "description": "Root URL for documentation links"}, + # Health. + "HEALTH_CHECKER": { + "description": "The configured health check.", + "x-example": ("RDSAwareHealthCheck", {"access_key": "foo", "secret_key": "bar"}), + }, + # Metrics. + "PROMETHEUS_NAMESPACE": { + "type": "string", + "description": "The prefix applied to all exposed Prometheus metrics. Defaults to `quay`", + "x-example": "myregistry", + }, + # Misc configuration. + "BLACKLIST_V2_SPEC": { + "type": "string", + "description": "The Docker CLI versions to which Quay will respond that V2 is *unsupported*. Defaults to `<1.6.0`", + "x-reference": "http://pythonhosted.org/semantic_version/reference.html#semantic_version.Spec", + "x-example": "<1.8.0", + }, + "USER_RECOVERY_TOKEN_LIFETIME": { + "type": "string", + "description": "The length of time a token for recovering a user accounts is valid. Defaults to 30m.", + "x-example": "10m", + "pattern": "^[0-9]+(w|m|d|h|s)$", + }, + "SESSION_COOKIE_SECURE": { + "type": "boolean", + "description": "Whether the `secure` property should be set on session cookies. " + + "Defaults to False. Recommended to be True for all installations using SSL.", + "x-example": True, + "x-reference": "https://en.wikipedia.org/wiki/Secure_cookies", + }, + "PUBLIC_NAMESPACES": { + "type": "array", + "description": "If a namespace is defined in the public namespace list, then it will appear on *all*" + + " user's repository list pages, regardless of whether that user is a member of the namespace." + + ' Typically, this is used by an enterprise customer in configuring a set of "well-known"' + + " namespaces.", + "uniqueItems": True, + "items": { + "type": "string", + }, + }, + "AVATAR_KIND": { + "type": "string", + "description": "The types of avatars to display, either generated inline (local) or Gravatar (gravatar)", + "enum": ["local", "gravatar"], + }, + "V2_PAGINATION_SIZE": { + "type": "number", + "description": "The number of results returned per page in V2 registry APIs", + "x-example": 100, + }, + "ENABLE_HEALTH_DEBUG_SECRET": { + "type": ["string", "null"], + "description": "If specified, a secret that can be given to health endpoints to see full debug info when" + + "not authenticated as a superuser", + "x-example": "somesecrethere", + }, + "BROWSER_API_CALLS_XHR_ONLY": { + "type": "boolean", + "description": "If enabled, only API calls marked as being made by an XHR will be allowed from browsers. Defaults to True.", + "x-example": False, + }, + # Time machine and tag expiration settings. + "FEATURE_CHANGE_TAG_EXPIRATION": { + "type": "boolean", + "description": "Whether users and organizations are allowed to change the tag expiration for tags in their namespace. Defaults to True.", + "x-example": False, + }, + "DEFAULT_TAG_EXPIRATION": { + "type": "string", + "description": "The default, configurable tag expiration time for time machine. Defaults to `2w`.", + "pattern": "^[0-9]+(w|m|d|h|s)$", + }, + "TAG_EXPIRATION_OPTIONS": { + "type": "array", + "description": "The options that users can select for expiration of tags in their namespace (if enabled)", + "items": { + "type": "string", + "pattern": "^[0-9]+(w|m|d|h|s)$", + }, + }, + # Team syncing. + "FEATURE_TEAM_SYNCING": { + "type": "boolean", + "description": "Whether to allow for team membership to be synced from a backing group in the authentication engine (LDAP or Keystone)", + "x-example": True, + }, + "TEAM_RESYNC_STALE_TIME": { + "type": "string", + "description": "If team syncing is enabled for a team, how often to check its membership and resync if necessary (Default: 30m)", + "x-example": "2h", + "pattern": "^[0-9]+(w|m|d|h|s)$", + }, + "FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP": { + "type": "boolean", + "description": "If enabled, non-superusers can setup syncing on teams to backing LDAP or Keystone. Defaults To False.", + "x-example": True, + }, + # Security scanning. + "FEATURE_SECURITY_SCANNER": { + "type": "boolean", + "description": "Whether to turn of/off the security scanner. Defaults to False", + "x-example": False, + "x-reference": "https://coreos.com/quay-enterprise/docs/latest/security-scanning.html", + }, + "FEATURE_SECURITY_NOTIFICATIONS": { + "type": "boolean", + "description": "If the security scanner is enabled, whether to turn of/off security notificaitons. Defaults to False", + "x-example": False, + }, + "SECURITY_SCANNER_ENDPOINT": { + "type": "string", + "pattern": "^http(s)?://(.)+$", + "description": "The endpoint for the V2 security scanner", + "x-example": "http://192.168.99.101:6060", + }, + "SECURITY_SCANNER_V4_ENDPOINT": { + "type": ["string", "null"], + "pattern": "^http(s)?://(.)+$", + "description": "The endpoint for the V4 security scanner", + "x-example": "http://192.168.99.101:6060", + }, + "SECURITY_SCANNER_INDEXING_INTERVAL": { + "type": "number", + "description": "The number of seconds between indexing intervals in the security scanner. Defaults to 30.", + "x-example": 30, + }, + # Repository mirroring + "REPO_MIRROR_INTERVAL": { + "type": "number", + "description": "The number of seconds between checking for repository mirror candidates. Defaults to 30.", + "x-example": 30, + }, + # Build + "FEATURE_GITHUB_BUILD": { + "type": "boolean", + "description": "Whether to support GitHub build triggers. Defaults to False", + "x-example": False, + }, + "FEATURE_BITBUCKET_BUILD": { + "type": "boolean", + "description": "Whether to support Bitbucket build triggers. Defaults to False", + "x-example": False, + }, + "FEATURE_GITLAB_BUILD": { + "type": "boolean", + "description": "Whether to support GitLab build triggers. Defaults to False", + "x-example": False, + }, + "FEATURE_BUILD_SUPPORT": { + "type": "boolean", + "description": "Whether to support Dockerfile build. Defaults to True", + "x-example": True, + }, + "DEFAULT_NAMESPACE_MAXIMUM_BUILD_COUNT": { + "type": ["number", "null"], + "description": "If not None, the default maximum number of builds that can be queued in a namespace.", + "x-example": 20, + }, + "SUCCESSIVE_TRIGGER_INTERNAL_ERROR_DISABLE_THRESHOLD": { + "type": ["number", "null"], + "description": "If not None, the number of successive internal errors that can occur before a build trigger is automatically disabled. Defaults to 5.", + "x-example": 10, + }, + "SUCCESSIVE_TRIGGER_FAILURE_DISABLE_THRESHOLD": { + "type": ["number", "null"], + "description": "If not None, the number of successive failures that can occur before a build trigger is automatically disabled. Defaults to 100.", + "x-example": 50, + }, + # Login + "FEATURE_GITHUB_LOGIN": { + "type": "boolean", + "description": "Whether GitHub login is supported. Defaults to False", + "x-example": False, + }, + "FEATURE_GOOGLE_LOGIN": { + "type": "boolean", + "description": "Whether Google login is supported. Defaults to False", + "x-example": False, + }, + # Recaptcha + "FEATURE_RECAPTCHA": { + "type": "boolean", + "description": "Whether Recaptcha is necessary for user login and recovery. Defaults to False", + "x-example": False, + "x-reference": "https://www.google.com/recaptcha/intro/", + }, + "RECAPTCHA_SITE_KEY": { + "type": ["string", "null"], + "description": "If recaptcha is enabled, the site key for the Recaptcha service", + }, + "RECAPTCHA_SECRET_KEY": { + "type": ["string", "null"], + "description": "If recaptcha is enabled, the secret key for the Recaptcha service", + }, + # External application tokens. + "FEATURE_APP_SPECIFIC_TOKENS": { + "type": "boolean", + "description": "If enabled, users can create tokens for use by the Docker CLI. Defaults to True", + "x-example": False, + }, + "APP_SPECIFIC_TOKEN_EXPIRATION": { + "type": ["string", "null"], + "description": "The expiration for external app tokens. Defaults to None.", + "pattern": "^[0-9]+(w|m|d|h|s)$", + }, + "EXPIRED_APP_SPECIFIC_TOKEN_GC": { + "type": ["string", "null"], + "description": "Duration of time expired external app tokens will remain before being garbage collected. Defaults to 1d.", + "pattern": "^[0-9]+(w|m|d|h|s)$", + }, + # Feature Flag: Garbage collection. + "FEATURE_GARBAGE_COLLECTION": { + "type": "boolean", + "description": "Whether garbage collection of repositories is enabled. Defaults to True", + "x-example": False, + }, + # Feature Flag: Rate limits. + "FEATURE_RATE_LIMITS": { + "type": "boolean", + "description": "Whether to enable rate limits on API and registry endpoints. Defaults to False", + "x-example": True, + }, + # Feature Flag: Aggregated log retrieval. + "FEATURE_AGGREGATED_LOG_COUNT_RETRIEVAL": { + "type": "boolean", + "description": "Whether to allow retrieval of aggregated log counts. Defaults to True", + "x-example": True, + }, + # Feature Flag: Log export. + "FEATURE_LOG_EXPORT": { + "type": "boolean", + "description": "Whether to allow exporting of action logs. Defaults to True", + "x-example": True, + }, + # Feature Flag: User last accessed. + "FEATURE_USER_LAST_ACCESSED": { + "type": "boolean", + "description": "Whether to record the last time a user was accessed. Defaults to True", + "x-example": True, + }, + # Feature Flag: Permanent Sessions. + "FEATURE_PERMANENT_SESSIONS": { + "type": "boolean", + "description": "Whether sessions are permanent. Defaults to True", + "x-example": True, + }, + # Feature Flag: Super User Support. + "FEATURE_SUPER_USERS": { + "type": "boolean", + "description": "Whether super users are supported. Defaults to True", + "x-example": True, + }, + # Feature Flag: Anonymous Users. + "FEATURE_ANONYMOUS_ACCESS": { + "type": "boolean", + "description": " Whether to allow anonymous users to browse and pull public repositories. Defaults to True", + "x-example": True, + }, + # Feature Flag: User Creation. + "FEATURE_USER_CREATION": { + "type": "boolean", + "description": "Whether users can be created (by non-super users). Defaults to True", + "x-example": True, + }, + # Feature Flag: Invite Only User Creation. + "FEATURE_INVITE_ONLY_USER_CREATION": { + "type": "boolean", + "description": "Whether users being created must be invited by another user. Defaults to False", + "x-example": False, + }, + # Feature Flag: Encrypted Basic Auth. + "FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH": { + "type": "boolean", + "description": "Whether non-encrypted passwords (as opposed to encrypted tokens) can be used for basic auth. Defaults to False", + "x-example": False, + }, + # Feature Flag: Direct Login. + "FEATURE_DIRECT_LOGIN": { + "type": "boolean", + "description": "Whether users can directly login to the UI. Defaults to True", + "x-example": True, + }, + # Feature Flag: Advertising V2. + "FEATURE_ADVERTISE_V2": { + "type": "boolean", + "description": "Whether the v2/ endpoint is visible. Defaults to True", + "x-example": True, + }, + # Feature Flag: Log Rotation. + "FEATURE_ACTION_LOG_ROTATION": { + "type": "boolean", + "description": "Whether or not to rotate old action logs to storage. Defaults to False", + "x-example": False, + }, + # Feature Flag: ACI Conversion. + "FEATURE_ACI_CONVERSION": { + "type": "boolean", + "description": "Whether to enable conversion to ACIs. Defaults to False", + "x-example": False, + }, + # Feature Flag: Library Support. + "FEATURE_LIBRARY_SUPPORT": { + "type": "boolean", + "description": 'Whether to allow for "namespace-less" repositories when pulling and pushing from Docker. Defaults to True', + "x-example": True, + }, + # Feature Flag: Require Team Invite. + "FEATURE_REQUIRE_TEAM_INVITE": { + "type": "boolean", + "description": "Whether to require invitations when adding a user to a team. Defaults to True", + "x-example": True, + }, + # Feature Flag: Collecting and Supporting Metadata. + "FEATURE_USER_METADATA": { + "type": "boolean", + "description": "Whether to collect and support user metadata. Defaults to False", + "x-example": False, + }, + # Feature Flag: Support App Registry. + "FEATURE_APP_REGISTRY": { + "type": "boolean", + "description": "Whether to enable support for App repositories. Defaults to False", + "x-example": False, + }, + # Feature Flag: Read only app registry. + "FEATURE_READONLY_APP_REGISTRY": { + "type": "boolean", + "description": "Whether to App repositories are read-only. Defaults to False", + "x-example": True, + }, + # Feature Flag: Public Reposiotires in _catalog Endpoint. + "FEATURE_PUBLIC_CATALOG": { + "type": "boolean", + "description": "If set to true, the _catalog endpoint returns public repositories. Otherwise, only private repositories can be returned. Defaults to False", + "x-example": False, + }, + # Feature Flag: Reader Build Logs. + "FEATURE_READER_BUILD_LOGS": { + "type": "boolean", + "description": "If set to true, build logs may be read by those with read access to the repo, rather than only write access or admin access. Defaults to False", + "x-example": False, + }, + # Feature Flag: Usernames Autocomplete. + "FEATURE_PARTIAL_USER_AUTOCOMPLETE": { + "type": "boolean", + "description": "If set to true, autocompletion will apply to partial usernames. Defaults to True", + "x-example": True, + }, + # Feature Flag: User log access. + "FEATURE_USER_LOG_ACCESS": { + "type": "boolean", + "description": "If set to true, users will have access to audit logs for their namespace. Defaults to False", + "x-example": True, + }, + # Feature Flag: User renaming. + "FEATURE_USER_RENAME": { + "type": "boolean", + "description": "If set to true, users can rename their own namespace. Defaults to False", + "x-example": True, + }, + # Feature Flag: Username confirmation. + "FEATURE_USERNAME_CONFIRMATION": { + "type": "boolean", + "description": "If set to true, users can confirm their generated usernames. Defaults to True", + "x-example": False, + }, + # Feature Flag: V1 push restriction. + "FEATURE_RESTRICTED_V1_PUSH": { + "type": "boolean", + "description": "If set to true, only namespaces listed in V1_PUSH_WHITELIST support V1 push. Defaults to True", + "x-example": False, + }, + # Feature Flag: Support Repository Mirroring. + "FEATURE_REPO_MIRROR": { + "type": "boolean", + "description": "Whether to enable support for repository mirroring. Defaults to False", + "x-example": False, + }, + "REPO_MIRROR_TLS_VERIFY": { + "type": "boolean", + "description": "Require HTTPS and verify certificates of Quay registry during mirror. Defaults to True", + "x-example": True, + }, + "REPO_MIRROR_SERVER_HOSTNAME": { + "type": "string", + "description": "Replaces the SERVER_HOSTNAME as the destination for mirroring. Defaults to unset", + "x-example": "openshift-quay-service", + }, + # Feature Flag: V1 push restriction. + "V1_PUSH_WHITELIST": { + "type": "array", + "description": "The array of namespace names that support V1 push if FEATURE_RESTRICTED_V1_PUSH is set to true.", + "x-example": ["some", "namespaces"], + }, + # Logs model + "LOGS_MODEL": { + "type": "string", + "description": "Logs model for action logs", + "enum": ["database", "transition_reads_both_writes_es", "elasticsearch"], + "x-example": "database", + }, + "LOGS_MODEL_CONFIG": { + "type": "object", + "description": "Logs model config for action logs", + "x-reference": "https://www.elastic.co/guide/en/elasticsearch/guide/master/_index_settings.html", + "properties": { + "producer": { + "type": "string", + "description": "Logs producer if logging to Elasticsearch", + "enum": ["kafka", "elasticsearch", "kinesis_stream"], + "x-example": "kafka", + }, + "elasticsearch_config": { + "type": "object", + "description": "Elasticsearch cluster configuration", + "properties": { + "host": { + "type": "string", + "description": "Elasticsearch cluster endpoint", + "x-example": "host.elasticsearch.example", + }, + "port": { + "type": "number", + "description": "Elasticsearch cluster endpoint port", + "x-example": 1234, + }, + "access_key": { + "type": "string", + "description": "Elasticsearch user (or IAM key for AWS ES)", + "x-example": "some_string", + }, + "secret_key": { + "type": "string", + "description": "Elasticsearch password (or IAM secret for AWS ES)", + "x-example": "some_secret_string", + }, + "aws_region": { + "type": "string", + "description": "Amazon web service region", + "x-example": "us-east-1", + }, + "use_ssl": { + "type": "boolean", + "description": "Use ssl for Elasticsearch. Defaults to True", + "x-example": True, + }, + "index_prefix": { + "type": "string", + "description": "Elasticsearch's index prefix", + "x-example": "logentry_", + }, + "index_settings": { + "type": "object", + "description": "Elasticsearch's index settings", + }, + }, + }, + "kafka_config": { + "type": "object", + "description": "Kafka cluster configuration", + "properties": { + "bootstrap_servers": { + "type": "array", + "description": "List of Kafka brokers to bootstrap the client from", + "uniqueItems": True, + "items": { + "type": "string", + }, + }, + "topic": { + "type": "string", + "description": "Kafka topic to publish log entries to", + "x-example": "logentry", + }, + "max_block_seconds": { + "type": "number", + "description": "Max number of seconds to block during a `send()`, either because the buffer is full or metadata unavailable", + "x-example": 10, + }, + }, + }, + "kinesis_stream_config": { + "type": "object", + "description": "AWS Kinesis Stream configuration", + "properties": { + "stream_name": { + "type": "string", + "description": "Kinesis stream to send action logs to", + "x-example": "logentry-kinesis-stream", + }, + "aws_region": { + "type": "string", + "description": "AWS region", + "x-example": "us-east-1", + }, + "aws_access_key": { + "type": "string", + "description": "AWS access key", + "x-example": "some_access_key", + }, + "aws_secret_key": { + "type": "string", + "description": "AWS secret key", + "x-example": "some_secret_key", + }, + "connect_timeout": { + "type": "number", + "description": "Number of seconds before timeout when attempting to make a connection", + "x-example": 5, + }, + "read_timeout": { + "type": "number", + "description": "Number of seconds before timeout when reading from a connection", + "x-example": 5, + }, + "retries": { + "type": "number", + "description": "Max number of attempts made on a single request", + "x-example": 5, + }, + "max_pool_connections": { + "type": "number", + "description": "The maximum number of connections to keep in a connection pool", + "x-example": 10, + }, + }, + }, + }, + }, + # Feature Flag: Blacklist Email Domains + "FEATURE_BLACKLISTED_EMAILS": { + "type": "boolean", + "description": "If set to true, no new User accounts may be created if their email domain is blacklisted.", + "x-example": False, + }, + # Blacklisted Email Domains + "BLACKLISTED_EMAIL_DOMAINS": { + "type": "array", + "description": "The array of email-address domains that is used if FEATURE_BLACKLISTED_EMAILS is set to true.", + "x-example": ["example.com", "example.org"], + }, + "FRESH_LOGIN_TIMEOUT": { + "type": "string", + "description": "The time after which a fresh login requires users to reenter their password", + "x-example": "5m", + }, + # Webhook blacklist. + "WEBHOOK_HOSTNAME_BLACKLIST": { + "type": "array", + "description": "The set of hostnames to disallow from webhooks when validating, beyond localhost", + "x-example": ["somexternaldomain.com"], + }, + }, +} + +if __name__ == "__main__": + + with open("quay-config-schema.json", "w") as outfile: + json.dump(CONFIG_SCHEMA, outfile) diff --git a/config-tool/utils/scripts/get_attributes.py b/config-tool/utils/scripts/get_attributes.py new file mode 100644 index 000000000..c3bc0a9dd --- /dev/null +++ b/config-tool/utils/scripts/get_attributes.py @@ -0,0 +1,15 @@ +import json + +with open("/home/jonathan/Desktop/config-tool/pkg/lib/testdata/quay-config-schema.json") as infile: + config = json.load(infile) + + attributes = [] + + for prop_name in config["properties"].keys(): + attributes.append(prop_name) + + with open("/home/jonathan/Desktop/config-tool/attributes.txt", "w") as outfile: + + for prop_name in attributes: + + outfile.write(prop_name + "\n")