From d9b4602280748e2fb322bffef95d02dda92664cb Mon Sep 17 00:00:00 2001 From: Kimonas Sotirchos Date: Wed, 3 Mar 2021 04:38:48 +0200 Subject: [PATCH] First iteration of the new Katib UI (#1427) * Create a folder for the new-ui We will create a `new-ui` folder under the `pkg` dir to add the new UI. This will ensure that we won't break any existing functionality. Signed-off-by: Kimonas Sotirchos * Initial code for the frontend This PR introduces the new UI. We hope that this will be the last big PR in this repo and all of the subsequent ones will be smaller bit-sized PRs. Signed-off-by: Kimonas Sotirchos * backend: Expose the entire status of an experiment We want the table in the main UI page to show more information for each experiment. This information lives in the status of each Experiment CR, so we expand the API to also return the entire status for each Experiment. In the future we will probably need to just send the entire CR to the frontend and not parse it at all in the backend. Signed-off-by: Kimonas Sotirchos * backend: Return the KFP run uid We want to return the Pipeline UID for a Trial, if such exists. When combined with Kale, a Trial initiates a KFP run. In this case, there is an annotation with the KFP run ID, which we can use to navigate the user to the KFP UI for the specific run. Signed-off-by: Kimonas Sotirchos * backend: sereve an Angular SPA To serve an SPA the backend must return the index.html for any non-API route. The index.html must be sent for any request to the app's page. Then, once the javascript loads, the app will show to the user the correct view. In this commit we also completely remove any caching of the index.html, for the browser to always request the latest version. This eliminates the need to hard reload the page to view changes to the frontend code. Signed-off-by: Kimonas Sotirchos * Dockerfile for the new Katib web app Signed-off-by: Kimonas Sotirchos * Extend the dockerignore for the new UI Signed-off-by: Kimonas Sotirchos * Update the README with build commands Signed-off-by: Kimonas Sotirchos * review: use port 8080 instead of 80 in backend Signed-off-by: Kimonas Sotirchos * review: use lowercase fields when fetchin exps Signed-off-by: Kimonas Sotirchos * review: Add seconds to the x-axis of Trial info Signed-off-by: Kimonas Sotirchos * review: Unify the npm run build commands Signed-off-by: Kimonas Sotirchos * review: Move TypeMeta values to a common place Signed-off-by: Kimonas Sotirchos * review: Remove section for max_old_space_size in README Co-authored-by: Andrey Velichkevich * review: add katib prefix to docs link Co-authored-by: Andrey Velichkevich * review: Correct link for new UI in README Co-authored-by: Andrey Velichkevich * review: Remove unused 'format' npm script Signed-off-by: Kimonas Sotirchos * review: Ensure format checks work with Travis Signed-off-by: Kimonas Sotirchos * review: Remove unused space Co-authored-by: Andrey Velichkevich * review: Use create_experiment route Signed-off-by: Kimonas Sotirchos * review: fix travis govet test Signed-off-by: Kimonas Sotirchos * review: Rename the Bayesian settings Co-authored-by: Andrey Velichkevich * review: Rename the ParametersSpec Co-authored-by: Andrey Velichkevich * review: Remove setting TypeMeta and ObjectMeta Signed-off-by: Kimonas Sotirchos * review: Update README for build:watch Co-authored-by: Andrey Velichkevich * review: Fix a typo Co-authored-by: Andrey Velichkevich * review: Remove unused css Signed-off-by: Kimonas Sotirchos * review: Use types from k8s.models file Signed-off-by: Kimonas Sotirchos * Add kfp-run column if UID is present in trials With Kale a Trial can launch a distinct KF Pipeline. The UID of this pipeline will be also set as an annotation to the Trial owning the Pipeline. In this case the UI should have one extra column for this UID. Signed-off-by: Kimonas Sotirchos * Properly expose the NAS fields Signed-off-by: Kimonas Sotirchos * review: Move MetricCollector enums to global enums file Signed-off-by: Kimonas Sotirchos * review: Remove unused volume enum Co-authored-by: Andrey Velichkevich * review: Don't send empty settings Signed-off-by: Kimonas Sotirchos * review: Add parameters for TPE Signed-off-by: Kimonas Sotirchos Co-authored-by: Andrey Velichkevich --- .dockerignore | 2 + cmd/new-ui/v1beta1/Dockerfile | 55 + cmd/new-ui/v1beta1/main.go | 58 + pkg/new-ui/v1beta1/README.md | 126 + pkg/new-ui/v1beta1/backend.go | 331 + pkg/new-ui/v1beta1/frontend/.gitignore | 49 + pkg/new-ui/v1beta1/frontend/.prettierrc.yaml | 5 + pkg/new-ui/v1beta1/frontend/angular.json | 124 + pkg/new-ui/v1beta1/frontend/browserslist | 12 + .../v1beta1/frontend/e2e/protractor.conf.js | 32 + .../v1beta1/frontend/e2e/src/app.e2e-spec.ts | 23 + pkg/new-ui/v1beta1/frontend/e2e/src/app.po.ts | 11 + pkg/new-ui/v1beta1/frontend/e2e/tsconfig.json | 13 + pkg/new-ui/v1beta1/frontend/karma.conf.js | 32 + pkg/new-ui/v1beta1/frontend/package-lock.json | 12970 ++++++++++++++++ pkg/new-ui/v1beta1/frontend/package.json | 69 + pkg/new-ui/v1beta1/frontend/proxy.conf.json | 8 + .../frontend/scripts/check-format-error.js | 6 + .../frontend/src/app/app-routing.module.ts | 17 + .../frontend/src/app/app.component.html | 3 + .../frontend/src/app/app.component.spec.ts | 31 + .../v1beta1/frontend/src/app/app.component.ts | 30 + .../v1beta1/frontend/src/app/app.module.ts | 26 + .../constants/algorithms-settings.const.ts | 228 + .../app/constants/algorithms-types.const.ts | 15 + .../app/constants/objective-types.const.ts | 6 + .../src/app/enumerations/algorithms.enum.ts | 16 + .../src/app/enumerations/metrics-collector.ts | 8 + .../app/enumerations/objective-type.enum.ts | 4 + .../src/app/enumerations/status.enum.ts | 7 + .../src/app/models/experiment.k8s.model.ts | 186 + .../src/app/models/experiment.model.ts | 49 + .../src/app/models/trial-templates.model.ts | 18 + .../algorithm/algorithm.component.html | 89 + .../algorithm/algorithm.component.scss | 6 + .../algorithm/algorithm.component.spec.ts | 24 + .../algorithm/algorithm.component.ts | 86 + .../algorithm/algorithm.module.ts | 14 + .../experiment-creation.component.html | 112 + .../experiment-creation.component.scss | 21 + .../experiment-creation.component.spec.ts | 24 + .../experiment-creation.component.ts | 149 + .../experiment-creation.module.ts | 43 + .../hyper-parameters.component.html | 16 + .../hyper-parameters.component.scss | 0 .../hyper-parameters.component.spec.ts | 24 + .../hyper-parameters.component.ts | 67 + .../hyper-parameters.module.ts | 27 + .../metadata/metadata.component.html | 20 + .../metadata/metadata.component.scss | 0 .../metadata/metadata.component.spec.ts | 24 + .../metadata/metadata.component.ts | 31 + .../metadata/metadata.module.ts | 11 + .../metrics-collector.component.html | 133 + .../metrics-collector.component.scss | 4 + .../metrics-collector.component.spec.ts | 24 + .../metrics-collector.component.ts | 19 + .../metrics-collector.module.ts | 13 + .../nas-graph/nas-graph.component.html | 32 + .../nas-graph/nas-graph.component.scss | 0 .../nas-graph/nas-graph.component.spec.ts | 24 + .../nas-graph/nas-graph.component.ts | 15 + .../nas-graph/nas-graph.module.ts | 12 + .../nas-operations.component.html | 35 + .../nas-operations.component.scss | 9 + .../nas-operations.component.spec.ts | 24 + .../nas-operations.component.ts | 27 + .../nas-operations/nas-operations.module.ts | 14 + .../operation/operation.component.html | 19 + .../operation/operation.component.scss | 0 .../operation/operation.component.spec.ts | 24 + .../operation/operation.component.ts | 13 + .../objective/objective.component.html | 78 + .../objective/objective.component.scss | 0 .../objective/objective.component.spec.ts | 24 + .../objective/objective.component.ts | 75 + .../objective/objective.module.ts | 24 + .../trial-template.component.html | 95 + .../trial-template.component.scss | 7 + .../trial-template.component.spec.ts | 24 + .../trial-template.component.ts | 98 + .../trial-template/trial-template.module.ts | 21 + .../trial-thresholds.component.html | 44 + .../trial-thresholds.component.scss | 0 .../trial-thresholds.component.spec.ts | 24 + .../trial-thresholds.component.ts | 12 + .../trial-thresholds.module.ts | 11 + .../yaml-modal/yaml-modal.component.html | 12 + .../yaml-modal/yaml-modal.component.scss | 4 + .../yaml-modal/yaml-modal.component.spec.ts | 24 + .../yaml-modal/yaml-modal.component.ts | 31 + .../yaml-modal/yaml-modal.module.ts | 13 + .../experiment-details-tab.component.html | 52 + .../experiment-details-tab.component.scss | 8 + .../experiment-details-tab.component.spec.ts | 24 + .../experiment-details-tab.component.ts | 86 + .../details/experiment-details-tab.module.ts | 12 + .../experiment-details.component.html | 65 + .../experiment-details.component.scss | 13 + .../experiment-details.component.spec.ts | 24 + .../experiment-details.component.ts | 238 + .../experiment-details.module.ts | 39 + .../experiment-overview.component.html | 43 + .../experiment-overview.component.spec.ts | 24 + .../overview/experiment-overview.component.ts | 162 + .../overview/experiment-overview.module.ts | 12 + .../trials-graph/d3.parcoords.js | 2907 ++++ .../trials-graph/trials-graph.component.html | 7 + .../trials-graph/trials-graph.component.scss | 43 + .../trials-graph.component.spec.ts | 24 + .../trials-graph/trials-graph.component.ts | 243 + .../trials-graph/trials-graph.module.ts | 11 + .../trial-modal/trial-modal.component.html | 31 + .../trial-modal/trial-modal.component.scss | 21 + .../trial-modal/trial-modal.component.spec.ts | 24 + .../trial-modal/trial-modal.component.ts | 104 + .../trials-table/trials-table.component.html | 64 + .../trials-table/trials-table.component.scss | 65 + .../trials-table.component.spec.ts | 24 + .../trials-table/trials-table.component.ts | 78 + .../trials-table/trials-table.module.ts | 27 + .../yaml/experiment-yaml.component.html | 8 + .../yaml/experiment-yaml.component.scss | 8 + .../yaml/experiment-yaml.component.spec.ts | 24 + .../yaml/experiment-yaml.component.ts | 17 + .../yaml/experiment-yaml.module.ts | 12 + .../src/app/pages/experiments/config.ts | 92 + .../pages/experiments/delete-modal-config.ts | 18 + .../experiments/experiments.component.html | 11 + .../experiments/experiments.component.scss | 0 .../experiments/experiments.component.spec.ts | 23 + .../experiments/experiments.component.ts | 142 + .../pages/experiments/experiments.module.ts | 28 + .../experiment-optimal-trial.component.html | 37 + .../experiment-optimal-trial.component.scss | 12 + ...experiment-optimal-trial.component.spec.ts | 24 + .../experiment-optimal-trial.component.ts | 25 + .../experiment-optimal-trial.module.ts | 12 + .../pages/experiments/optimal-trial/utils.ts | 58 + .../src/app/pages/experiments/utils.ts | 45 + .../src/app/services/backend.service.spec.ts | 12 + .../src/app/services/backend.service.ts | 99 + .../app/services/experiment-form.service.ts | 412 + .../list-input/list-input.component.html | 22 + .../list-input/list-input.component.scss | 7 + .../list-input/list-input.component.spec.ts | 24 + .../shared/list-input/list-input.component.ts | 28 + .../shared/list-input/list-input.module.ts | 14 + .../list-key-value.component.html | 32 + .../list-key-value.component.scss | 19 + .../list-key-value.component.spec.ts | 24 + .../list-key-value.component.ts | 28 + .../list-key-value/list-key-value.module.ts | 14 + .../add-modal/add-modal.component.html | 66 + .../add-modal/add-modal.component.scss | 23 + .../add-modal/add-modal.component.spec.ts | 24 + .../add-modal/add-modal.component.ts | 69 + .../parameter/parameter.component.html | 74 + .../parameter/parameter.component.scss | 42 + .../parameter/parameter.component.spec.ts | 24 + .../parameter/parameter.component.ts | 89 + .../params-list/params-list.component.html | 21 + .../params-list/params-list.component.scss | 30 + .../params-list/params-list.component.spec.ts | 24 + .../params-list/params-list.component.ts | 51 + .../shared/params-list/params-list.module.ts | 35 + .../v1beta1/frontend/src/app/shared/utils.ts | 135 + .../v1beta1/frontend/src/assets/.gitkeep | 0 .../frontend/src/assets/css/d3.parcoords.css | 33 + .../frontend/src/assets/pipeline-centered.svg | 23 + .../src/environments/environment.prod.ts | 3 + .../frontend/src/environments/environment.ts | 16 + pkg/new-ui/v1beta1/frontend/src/favicon.ico | Bin 0 -> 948 bytes pkg/new-ui/v1beta1/frontend/src/index.html | 18 + pkg/new-ui/v1beta1/frontend/src/kubeflow.css | 9 + pkg/new-ui/v1beta1/frontend/src/main.ts | 13 + pkg/new-ui/v1beta1/frontend/src/polyfills.ts | 62 + pkg/new-ui/v1beta1/frontend/src/styles.scss | 15 + pkg/new-ui/v1beta1/frontend/src/test.ts | 20 + pkg/new-ui/v1beta1/frontend/tsconfig.app.json | 10 + pkg/new-ui/v1beta1/frontend/tsconfig.json | 24 + .../v1beta1/frontend/tsconfig.spec.json | 18 + pkg/new-ui/v1beta1/hp.go | 223 + pkg/new-ui/v1beta1/nas.go | 89 + pkg/new-ui/v1beta1/types.go | 86 + pkg/new-ui/v1beta1/util.go | 287 + 186 files changed, 23882 insertions(+) create mode 100644 cmd/new-ui/v1beta1/Dockerfile create mode 100644 cmd/new-ui/v1beta1/main.go create mode 100755 pkg/new-ui/v1beta1/README.md create mode 100644 pkg/new-ui/v1beta1/backend.go create mode 100644 pkg/new-ui/v1beta1/frontend/.gitignore create mode 100644 pkg/new-ui/v1beta1/frontend/.prettierrc.yaml create mode 100644 pkg/new-ui/v1beta1/frontend/angular.json create mode 100644 pkg/new-ui/v1beta1/frontend/browserslist create mode 100644 pkg/new-ui/v1beta1/frontend/e2e/protractor.conf.js create mode 100644 pkg/new-ui/v1beta1/frontend/e2e/src/app.e2e-spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/e2e/src/app.po.ts create mode 100644 pkg/new-ui/v1beta1/frontend/e2e/tsconfig.json create mode 100644 pkg/new-ui/v1beta1/frontend/karma.conf.js create mode 100644 pkg/new-ui/v1beta1/frontend/package-lock.json create mode 100644 pkg/new-ui/v1beta1/frontend/package.json create mode 100644 pkg/new-ui/v1beta1/frontend/proxy.conf.json create mode 100644 pkg/new-ui/v1beta1/frontend/scripts/check-format-error.js create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/app-routing.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/app.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/app.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/app.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/app.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-settings.const.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-types.const.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/constants/objective-types.const.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/enumerations/algorithms.enum.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/enumerations/metrics-collector.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/enumerations/objective-type.enum.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/enumerations/status.enum.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/models/experiment.k8s.model.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/models/experiment.model.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/models/trial-templates.model.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/d3.parcoords.js create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/config.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/delete-modal-config.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/utils.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/utils.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/services/experiment-form.service.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.spec.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.module.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/app/shared/utils.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/assets/.gitkeep create mode 100644 pkg/new-ui/v1beta1/frontend/src/assets/css/d3.parcoords.css create mode 100644 pkg/new-ui/v1beta1/frontend/src/assets/pipeline-centered.svg create mode 100644 pkg/new-ui/v1beta1/frontend/src/environments/environment.prod.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/environments/environment.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/favicon.ico create mode 100644 pkg/new-ui/v1beta1/frontend/src/index.html create mode 100644 pkg/new-ui/v1beta1/frontend/src/kubeflow.css create mode 100644 pkg/new-ui/v1beta1/frontend/src/main.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/polyfills.ts create mode 100644 pkg/new-ui/v1beta1/frontend/src/styles.scss create mode 100644 pkg/new-ui/v1beta1/frontend/src/test.ts create mode 100644 pkg/new-ui/v1beta1/frontend/tsconfig.app.json create mode 100644 pkg/new-ui/v1beta1/frontend/tsconfig.json create mode 100644 pkg/new-ui/v1beta1/frontend/tsconfig.spec.json create mode 100644 pkg/new-ui/v1beta1/hp.go create mode 100644 pkg/new-ui/v1beta1/nas.go create mode 100644 pkg/new-ui/v1beta1/types.go create mode 100644 pkg/new-ui/v1beta1/util.go diff --git a/.dockerignore b/.dockerignore index a35766d72d6..320ecdadf4f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,5 @@ examples manifests pkg/ui/*/frontend/node_modules pkg/ui/*/frontend/build +pkg/new-ui/*/frontend/node_modules +pkg/new-ui/*/frontend/build diff --git a/cmd/new-ui/v1beta1/Dockerfile b/cmd/new-ui/v1beta1/Dockerfile new file mode 100644 index 00000000000..f6a042bb3da --- /dev/null +++ b/cmd/new-ui/v1beta1/Dockerfile @@ -0,0 +1,55 @@ +# --- Clone the kubeflow/kubeflow code --- +FROM ubuntu AS fetch-kubeflow-kubeflow + +RUN apt-get update && apt-get install git -y + +WORKDIR /kf +RUN git clone https://github.com/kubeflow/kubeflow.git && \ + cd kubeflow && \ + git checkout a349284 + +# --- Build the frontend kubeflow library --- +FROM node:12 AS frontend-kubeflow-lib + +WORKDIR /src + +ARG LIB=/kf/kubeflow/components/crud-web-apps/common/frontend/kubeflow-common-lib +COPY --from=fetch-kubeflow-kubeflow $LIB/package*.json ./ +RUN npm ci + +COPY --from=fetch-kubeflow-kubeflow $LIB/ ./ +RUN npm run build + +# --- Build the frontend --- +FROM node:12 AS frontend + +WORKDIR /src +COPY ./pkg/new-ui/v1beta1/frontend/package*.json ./ +RUN npm ci + +COPY ./pkg/new-ui/v1beta1/frontend/ . +COPY --from=frontend-kubeflow-lib /src/dist/kubeflow/ ./node_modules/kubeflow/ + +RUN npm run build:prod + +# --- Build the backend --- +FROM golang:alpine AS go-build + +# The GOPATH in the image is /go. +ADD . /go/src/github.com/kubeflow/katib +WORKDIR /go/src/github.com/kubeflow/katib/cmd/new-ui +RUN if [ "$(uname -m)" = "ppc64le" ] || [ "$(uname -m)" = "aarch64" ]; then \ + apk --update add gcc musl-dev && \ + go build -o katib-ui ./v1beta1; \ + else \ + go build -o katib-ui ./v1beta1; \ + fi + +# --- compose the web app --- +FROM alpine:3.7 +WORKDIR /app +COPY --from=go-build /go/src/github.com/kubeflow/katib/cmd/new-ui/katib-ui /app/ +COPY --from=frontend /src/dist/static /app/build/static/ + +USER 1000 +ENTRYPOINT ["./katib-ui"] diff --git a/cmd/new-ui/v1beta1/main.go b/cmd/new-ui/v1beta1/main.go new file mode 100644 index 00000000000..6d288a542a4 --- /dev/null +++ b/cmd/new-ui/v1beta1/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + + common_v1beta1 "github.com/kubeflow/katib/pkg/common/v1beta1" + ui "github.com/kubeflow/katib/pkg/new-ui/v1beta1" +) + +var ( + port, host, buildDir, dbManagerAddr *string +) + +func init() { + port = flag.String("port", "8080", "The port to listen to for incoming HTTP connections") + host = flag.String("host", "0.0.0.0", "The host to listen to for incoming HTTP connections") + buildDir = flag.String("build-dir", "/app/build", "The dir of frontend") + dbManagerAddr = flag.String("db-manager-address", common_v1beta1.GetDBManagerAddr(), "The address of Katib DB manager") +} + +func main() { + flag.Parse() + kuh := ui.NewKatibUIHandler(*dbManagerAddr) + + log.Printf("Serving the frontend dir %s", *buildDir) + frontend := http.FileServer(http.Dir(*buildDir)) + http.HandleFunc("/katib/", kuh.ServeIndex(*buildDir)) + http.Handle("/katib/static/", http.StripPrefix("/katib/", frontend)) + + http.HandleFunc("/katib/fetch_experiments/", kuh.FetchAllExperiments) + + http.HandleFunc("/katib/create_experiment/", kuh.CreateExperiment) + + http.HandleFunc("/katib/delete_experiment/", kuh.DeleteExperiment) + + http.HandleFunc("/katib/fetch_experiment/", kuh.FetchExperiment) + http.HandleFunc("/katib/fetch_suggestion/", kuh.FetchSuggestion) + + http.HandleFunc("/katib/fetch_hp_job_info/", kuh.FetchHPJobInfo) + http.HandleFunc("/katib/fetch_hp_job_trial_info/", kuh.FetchHPJobTrialInfo) + http.HandleFunc("/katib/fetch_nas_job_info/", kuh.FetchNASJobInfo) + + http.HandleFunc("/katib/fetch_trial_templates/", kuh.FetchTrialTemplates) + http.HandleFunc("/katib/add_template/", kuh.AddTemplate) + http.HandleFunc("/katib/edit_template/", kuh.EditTemplate) + http.HandleFunc("/katib/delete_template/", kuh.DeleteTemplate) + http.HandleFunc("/katib/fetch_namespaces", kuh.FetchNamespaces) + + log.Printf("Serving at %s:%s", *host, *port) + if err := http.ListenAndServe(fmt.Sprintf("%s:%s", *host, *port), nil); err != nil { + panic(err) + } +} diff --git a/pkg/new-ui/v1beta1/README.md b/pkg/new-ui/v1beta1/README.md new file mode 100755 index 00000000000..09979b17e7f --- /dev/null +++ b/pkg/new-ui/v1beta1/README.md @@ -0,0 +1,126 @@ +# Katib User Interface + +This is the source code for the Katib UI. Current version of Katib UI is v1beta1. On the official Kubeflow website [here](https://www.kubeflow.org/docs/components/katib/experiment/#running-the-experiment-from-the-katib-ui) you can find information how to use Katib UI. +We are using [Angular](https://angular.io/) framework to create frontend and Go as a backend. + +We are using [Material UI](https://material.angular.io/) to design frontend. Try to use Material UI components to implement new Katib UI features. + +## Folder structure + +1. You can find `Dockerfile` and `main.go` - file to serve the UI under `cmd/new-ui/v1beta1` + +1. You can find Go backend under `pkg/new-ui/v1beta1` + +1. You can find Angular frontend under `pkg/new-ui/v1beta1/frontend` + +## Requirements + +To make changes to the UI you need to install: + +- Tools, defined [here](https://github.com/kubeflow/katib/blob/master/docs/developer-guide.md#requirements). + +- `node` (v12 or later) and `npm` (v6.13 or later). Recommended to install `node` and `npm` using [`nvm`](https://github.com/nvm-sh/nvm). After installing `nvm`, you can run `nvm install 12.18.1` to install `node` version 12.18.1 and run `nvm use 12.18.1` to use that version. + +## Development + +While development you have different ways to run Katib UI. +1. Build and serve only the frontend. The dev server will also be proxying requests to the backend +2. Build the frontend and serve it via the backend locally + +### Serve only the frontend + +You can run a webpack dev server that only exposes the frontend files, which can be useful for testing only the UI of the app. There's also a `proxy.conf.json` file which configures the dev server to send the backend requests to port `8000`. + +In order to build the UI locally, and expose it with a webpack dev server you will need to: +1. Create a module from the [common library](https://github.com/kubeflow/kubeflow/tree/master/components/crud-web-apps/common/frontend/kubeflow-common-lib) +2. Install the node modules of the app and also link the common-library module + + + +You can build the common library with: +```bash +cd /tmp && git clone https://github.com/kubeflow/kubeflow.git \ + && cd kubeflow \ + && git checkout a349284 \ + && cd components/crud-web-apps/common/frontend/kubeflow-common-lib + +# build the common library module +npm i +npm run build + +# link the module to your npm packages +# depending on where you npm stores the global packages you +# might need to use sudo +npm link dist/kubeflow +``` + +And then build and run the UI locally, on `localhost:4200`, with: +```bash +# If you've already cloned the repo then skip this step and just +# navigate to the pkg/new-ui/v1beta1/frontend dir +cd /tmp && git clone https://github.com/kubeflow/katib.git \ + && cd katib/pkg/new-ui/v1beta1/frontend + +npm i +npm link kubeflow +npm run start +``` + +### Serve the UI from the backend + +This is the recommended way to test the web app e2e. In order to build the UI and serve it via the backend, locally, you will need to: + +1. Build the UI locally. You have to follow the steps from the previous section, but instead of running `npm run start` you need to run `npm run build:prod`. It builds the frontend artifacts under `frontend/dist/static` folder. + + Moreover, you are able to run `npm run build:watch` instead of `npm run build:prod`. In that case, it starts a process which is watching the source code changes and building the frontend artifacts under `frontend/dist/static` folder. + + Learn more about Angular scripts in the [official guide](https://angular.io/cli/build). +1. Run `kubectl port-forward svc/katib-db-manager 6789 -n kubeflow` to expose `katib-db-manager` service for external access. You can use [different ways](https://kubernetes.io/docs/tasks/access-application-cluster/) to get external address for Kubernetes service. After exposing service, you should be able to receive information by running `wget :`. In case of port-forwarding above, you have to run `wget localhost:6789`. + +1. Go to `cmd/new-ui/v1beta1`. + +1. Run `main.go` file with appropriate flags, where: + + - `--build-dir` - directory with the frontend artifacts. + - `--port` - port to access Katib UI. + - `--db-manager-address` - Katib DB manager external IP and port address. + + For example, if you use port-forwarding to expose `katib-db-manager`, run this command: + + ``` + go run main.go --build-dir=../../../pkg/new-ui/v1beta1/frontend/dist --port=8080 --db-manager-address=localhost:6789 + ``` + +After that, you can access the UI using this URL: `http://localhost:8080/katib/`. + +## Production + +To run Katib UI in Production, after all changes in frontend and backend, you need to create an image for the UI. Under `/katib` directory run this: `docker build . -f cmd/new-ui/v1beta1/Dockerfile -t ` to build the image. + +After that, you can modify UI [deployment](https://github.com/kubeflow/katib/blob/master/manifests/v1beta1/ui/deployment.yaml#L24) with your new image. Then, follow [these steps](https://www.kubeflow.org/docs/components/katib/hyperparameter/#accessing-the-katib-ui) to access Katib UI. + +## Code style + +To make frontend code consistent and easy to review we use [Prettier](https://prettier.io/). You can find Prettier config [here](https://github.com/kubeflow/katib/tree/master/pkg/new-ui/v1beta1/frontend/.prettierrc.yaml). +Check [here](https://prettier.io/docs/en/install.html), how to install Prettier CLI to check and format your code. + +### IDE integration + +For VSCode you can install plugin: "Prettier - Code formatter" and it picks Prettier config automatically. + +You can edit [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations) file for VSCode to autoformat on save. + +```json + "settings": { + "editor.formatOnSave": true + } +``` + +For others IDE see [this](https://prettier.io/docs/en/editors.html). + +### Check and format code + +Before submitting PR check and format your code. To check your code run `npm run format:check` under `/frontend` folder. To format your code run `npm run format:write` under `/frontend` folder. +If all files formatted you can submit the PR. + +If you don't want to format some code, [here](https://prettier.io/docs/en/ignore.html) is an instruction how to disable Prettier. diff --git a/pkg/new-ui/v1beta1/backend.go b/pkg/new-ui/v1beta1/backend.go new file mode 100644 index 00000000000..8c8f884f6a8 --- /dev/null +++ b/pkg/new-ui/v1beta1/backend.go @@ -0,0 +1,331 @@ +package v1beta1 + +import ( + "encoding/json" + "log" + "net/http" + "path/filepath" + + "google.golang.org/grpc" + "sigs.k8s.io/controller-runtime/pkg/client" + + experimentv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1beta1" + api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1" + "github.com/kubeflow/katib/pkg/util/v1beta1/katibclient" +) + +func NewKatibUIHandler(dbManagerAddr string) *KatibUIHandler { + kclient, err := katibclient.NewClient(client.Options{}) + if err != nil { + log.Printf("NewClient for Katib failed: %v", err) + panic(err) + } + return &KatibUIHandler{ + katibClient: kclient, + dbManagerAddr: dbManagerAddr, + } +} + +// ServeIndex will return index.html for any non-API URL +func (k *KatibUIHandler) ServeIndex(buildDir string) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + fp := filepath.Join(buildDir, "static/index.html") + log.Printf("Sending file %s for url: %s", fp, r.URL) + + // never cache index.html + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0") + + // return the contents of index.html + http.ServeFile(w, r, fp) + } +} + +func (k *KatibUIHandler) connectManager() (*grpc.ClientConn, api_pb_v1beta1.DBManagerClient) { + conn, err := grpc.Dial(k.dbManagerAddr, grpc.WithInsecure()) + if err != nil { + log.Printf("Dial to GRPC failed: %v", err) + return nil, nil + } + c := api_pb_v1beta1.NewDBManagerClient(conn) + return conn, c +} + +func (k *KatibUIHandler) CreateExperiment(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + var data map[string]interface{} + + json.NewDecoder(r.Body).Decode(&data) + dataJSON, ok := data["postData"] + if !ok { + msg := "Couldn't load the 'postData' field of the request's data" + log.Printf(msg) + http.Error(w, msg, http.StatusInternalServerError) + return + } + + jsonbody, err := json.Marshal(dataJSON) + if err != nil { + log.Printf("Marshal data for experiment failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + job := experimentv1beta1.Experiment{} + if err := json.Unmarshal(jsonbody, &job); err != nil { + log.Printf("Unmarshal experiment failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = k.katibClient.CreateRuntimeObject(&job) + if err != nil { + log.Printf("CreateRuntimeObject from parameters failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +// FetchAllExperiments gets HP and NAS experiments in all namespaces. +func (k *KatibUIHandler) FetchAllExperiments(w http.ResponseWriter, r *http.Request) { + // At first, try to list experiments in cluster scope + experiments, err := k.getExperiments([]string{""}) + if err != nil { + // If failed, just try to list experiments from own namespace + experiments, err = k.getExperiments([]string{}) + } + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + response, err := json.Marshal(experiments) + if err != nil { + log.Printf("Marshal experiments failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request) { + experimentName := r.URL.Query()["experimentName"][0] + namespace := r.URL.Query()["namespace"][0] + + experiment, err := k.katibClient.GetExperiment(experimentName, namespace) + if err != nil { + log.Printf("GetExperiment failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = k.katibClient.DeleteRuntimeObject(experiment) + if err != nil { + log.Printf("DeleteRuntimeObject failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + isExperimentDeleted := false + + var experiments []ExperimentView + + // Waiting until experiment will be deleted + for !isExperimentDeleted { + // At first, try to list experiments in cluster scope + experiments, err = k.getExperiments([]string{""}) + if err != nil { + // If failed, just try to list experiments from own namespace + experiments, err = k.getExperiments([]string{}) + } + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + isExperimentDeleted = true + for _, experiment := range experiments { + if experiment.Name == experimentName { + isExperimentDeleted = false + break + } + } + } + + response, err := json.Marshal(experiments) + if err != nil { + log.Printf("Marshal HP and NAS experiments failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +// FetchTrialTemplates gets all trial templates in all namespaces +func (k *KatibUIHandler) FetchTrialTemplates(w http.ResponseWriter, r *http.Request) { + + trialTemplatesViewList, err := k.getTrialTemplatesViewList() + if err != nil { + log.Printf("getTrialTemplatesViewList failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + TrialTemplatesResponse := TrialTemplatesResponse{ + Data: trialTemplatesViewList, + } + response, err := json.Marshal(TrialTemplatesResponse) + if err != nil { + log.Printf("Marshal templates failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +//AddTemplate adds template to ConfigMap +func (k *KatibUIHandler) AddTemplate(w http.ResponseWriter, r *http.Request) { + var data map[string]interface{} + json.NewDecoder(r.Body).Decode(&data) + + updatedConfigMapNamespace := data["updatedConfigMapNamespace"].(string) + updatedConfigMapName := data["updatedConfigMapName"].(string) + updatedConfigMapPath := data["updatedConfigMapPath"].(string) + updatedTemplateYaml := data["updatedTemplateYaml"].(string) + + newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, updatedTemplateYaml, ActionTypeAdd) + if err != nil { + log.Printf("updateTrialTemplates failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + TrialTemplatesResponse := TrialTemplatesResponse{ + Data: newTemplates, + } + response, err := json.Marshal(TrialTemplatesResponse) + if err != nil { + log.Printf("Marhal failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) + +} + +// EditTemplate edits template in ConfigMap +func (k *KatibUIHandler) EditTemplate(w http.ResponseWriter, r *http.Request) { + + var data map[string]interface{} + json.NewDecoder(r.Body).Decode(&data) + + updatedConfigMapNamespace := data["updatedConfigMapNamespace"].(string) + updatedConfigMapName := data["updatedConfigMapName"].(string) + configMapPath := data["configMapPath"].(string) + updatedConfigMapPath := data["updatedConfigMapPath"].(string) + updatedTemplateYaml := data["updatedTemplateYaml"].(string) + + newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, configMapPath, updatedConfigMapPath, updatedTemplateYaml, ActionTypeEdit) + if err != nil { + log.Printf("updateTrialTemplates failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + TrialTemplatesResponse := TrialTemplatesResponse{ + Data: newTemplates, + } + response, err := json.Marshal(TrialTemplatesResponse) + if err != nil { + log.Printf("Marhal failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +// DeleteTemplate deletes template in ConfigMap +func (k *KatibUIHandler) DeleteTemplate(w http.ResponseWriter, r *http.Request) { + + var data map[string]interface{} + json.NewDecoder(r.Body).Decode(&data) + + updatedConfigMapNamespace := data["updatedConfigMapNamespace"].(string) + updatedConfigMapName := data["updatedConfigMapName"].(string) + updatedConfigMapPath := data["updatedConfigMapPath"].(string) + + newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, "", ActionTypeDelete) + if err != nil { + log.Printf("updateTrialTemplates failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + TrialTemplatesResponse := TrialTemplatesResponse{ + Data: newTemplates, + } + + response, err := json.Marshal(TrialTemplatesResponse) + if err != nil { + log.Printf("Marhal failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +func (k *KatibUIHandler) FetchNamespaces(w http.ResponseWriter, r *http.Request) { + + // Get all available namespaces + namespaces, err := k.getAvailableNamespaces() + if err != nil { + log.Printf("GetAvailableNamespaces failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + response, err := json.Marshal(namespaces) + if err != nil { + log.Printf("Marshal namespaces failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +// FetchExperiment gets experiment in specific namespace. +func (k *KatibUIHandler) FetchExperiment(w http.ResponseWriter, r *http.Request) { + experimentName := r.URL.Query()["experimentName"][0] + namespace := r.URL.Query()["namespace"][0] + + experiment, err := k.katibClient.GetExperiment(experimentName, namespace) + if err != nil { + log.Printf("GetExperiment failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + response, err := json.Marshal(experiment) + if err != nil { + log.Printf("Marshal Experiment failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +// FetchSuggestion gets suggestion in specific namespace +func (k *KatibUIHandler) FetchSuggestion(w http.ResponseWriter, r *http.Request) { + suggestionName := r.URL.Query()["suggestionName"][0] + namespace := r.URL.Query()["namespace"][0] + + suggestion, err := k.katibClient.GetSuggestion(suggestionName, namespace) + if err != nil { + log.Printf("GetSuggestion failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + response, err := json.Marshal(suggestion) + if err != nil { + log.Printf("Marshal Suggestion failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} diff --git a/pkg/new-ui/v1beta1/frontend/.gitignore b/pkg/new-ui/v1beta1/frontend/.gitignore new file mode 100644 index 00000000000..c0cef758801 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/.gitignore @@ -0,0 +1,49 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc +# Only exists if Bazel was run +/bazel-out + +# dependencies +/node_modules + +# profiling files +chrome-profiler-events*.json +speed-measure-plugin*.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +# Rok files +**/browse-in-rok* diff --git a/pkg/new-ui/v1beta1/frontend/.prettierrc.yaml b/pkg/new-ui/v1beta1/frontend/.prettierrc.yaml new file mode 100644 index 00000000000..774373e5aed --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/.prettierrc.yaml @@ -0,0 +1,5 @@ +singleQuote: true +printWidth: 80 +trailingComma: all +tabWidth: 2 +arrowParens: avoid diff --git a/pkg/new-ui/v1beta1/frontend/angular.json b/pkg/new-ui/v1beta1/frontend/angular.json new file mode 100644 index 00000000000..f01f2121e9d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/angular.json @@ -0,0 +1,124 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "frontend": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "preserveSymlinks": true, + "outputPath": "dist/frontend", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "aot": false, + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss", "src/assets/css/d3.parcoords.css"], + "scripts": [ + "node_modules/d3/d3.js", + "node_modules/ace-builds/src-min/ace.js", + "node_modules/ace-builds/src-min/mode-yaml.js", + "node_modules/ace-builds/src-min/theme-xcode.js" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "frontend:build" + }, + "configurations": { + "production": { + "browserTarget": "frontend:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "frontend:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], + "scripts": [] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "tsconfig.app.json", + "tsconfig.spec.json", + "e2e/tsconfig.json" + ], + "exclude": ["**/node_modules/**"] + } + }, + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "frontend:serve" + }, + "configurations": { + "production": { + "devServerTarget": "frontend:serve:production" + } + } + } + } + } + }, + "defaultProject": "frontend" +} diff --git a/pkg/new-ui/v1beta1/frontend/browserslist b/pkg/new-ui/v1beta1/frontend/browserslist new file mode 100644 index 00000000000..80848532e47 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/browserslist @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/pkg/new-ui/v1beta1/frontend/e2e/protractor.conf.js b/pkg/new-ui/v1beta1/frontend/e2e/protractor.conf.js new file mode 100644 index 00000000000..7c798cfff07 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/e2e/protractor.conf.js @@ -0,0 +1,32 @@ +// @ts-check +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +/** + * @type { import("protractor").Config } + */ +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + browserName: 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; \ No newline at end of file diff --git a/pkg/new-ui/v1beta1/frontend/e2e/src/app.e2e-spec.ts b/pkg/new-ui/v1beta1/frontend/e2e/src/app.e2e-spec.ts new file mode 100644 index 00000000000..f8b8268fb25 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/e2e/src/app.e2e-spec.ts @@ -0,0 +1,23 @@ +import { AppPage } from './app.po'; +import { browser, logging } from 'protractor'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getTitleText()).toEqual('frontend app is running!'); + }); + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + expect(logs).not.toContain(jasmine.objectContaining({ + level: logging.Level.SEVERE, + } as logging.Entry)); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/e2e/src/app.po.ts b/pkg/new-ui/v1beta1/frontend/e2e/src/app.po.ts new file mode 100644 index 00000000000..b8498c26f24 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get(browser.baseUrl) as Promise; + } + + getTitleText() { + return element(by.css('app-root .content span')).getText() as Promise; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/e2e/tsconfig.json b/pkg/new-ui/v1beta1/frontend/e2e/tsconfig.json new file mode 100644 index 00000000000..39b800f7896 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/e2e/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} diff --git a/pkg/new-ui/v1beta1/frontend/karma.conf.js b/pkg/new-ui/v1beta1/frontend/karma.conf.js new file mode 100644 index 00000000000..d26663c10d4 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, './coverage/frontend'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/pkg/new-ui/v1beta1/frontend/package-lock.json b/pkg/new-ui/v1beta1/frontend/package-lock.json new file mode 100644 index 00000000000..701a2915a6f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/package-lock.json @@ -0,0 +1,12970 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.803.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.29.tgz", + "integrity": "sha512-yHBud/fZHTelX24yjQg5lefZrfIebruoFTGeOwF0JdX8+KiHcTIxS4LOnUTYriasfHarcHRFXBAV/bRm+wv5ow==", + "dev": true, + "requires": { + "@angular-devkit/core": "8.3.29", + "rxjs": "6.4.0" + } + }, + "@angular-devkit/build-angular": { + "version": "0.803.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.803.29.tgz", + "integrity": "sha512-XAgfP1gi0rEJ3oVt+8ipvS5RfPNbeK5r2n8Ll2H3xkKjU0p1PN8+S6/0XVBtmMfeQ06SJWEAKFcAYqrxXhVTzw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.803.29", + "@angular-devkit/build-optimizer": "0.803.29", + "@angular-devkit/build-webpack": "0.803.29", + "@angular-devkit/core": "8.3.29", + "@babel/core": "7.8.7", + "@babel/preset-env": "7.8.7", + "@ngtools/webpack": "8.3.29", + "ajv": "6.12.3", + "autoprefixer": "9.6.1", + "browserslist": "4.10.0", + "cacache": "12.0.2", + "caniuse-lite": "1.0.30001035", + "circular-dependency-plugin": "5.2.0", + "clean-css": "4.2.1", + "copy-webpack-plugin": "6.0.3", + "core-js": "3.6.4", + "coverage-istanbul-loader": "2.0.3", + "file-loader": "4.2.0", + "find-cache-dir": "3.0.0", + "glob": "7.1.4", + "jest-worker": "24.9.0", + "karma-source-map-support": "1.4.0", + "less": "3.9.0", + "less-loader": "5.0.0", + "license-webpack-plugin": "2.1.2", + "loader-utils": "1.2.3", + "mini-css-extract-plugin": "0.8.0", + "minimatch": "3.0.4", + "open": "6.4.0", + "parse5": "4.0.0", + "postcss": "7.0.17", + "postcss-import": "12.0.1", + "postcss-loader": "3.0.0", + "raw-loader": "3.1.0", + "regenerator-runtime": "0.13.3", + "rxjs": "6.4.0", + "sass": "1.22.9", + "sass-loader": "7.2.0", + "semver": "6.3.0", + "source-map": "0.7.3", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.13", + "speed-measure-webpack-plugin": "1.3.1", + "style-loader": "1.0.0", + "stylus": "0.54.5", + "stylus-loader": "3.0.2", + "terser": "4.6.3", + "terser-webpack-plugin": "3.0.3", + "tree-kill": "1.2.2", + "webpack": "4.39.2", + "webpack-dev-middleware": "3.7.2", + "webpack-dev-server": "3.11.0", + "webpack-merge": "4.2.1", + "webpack-sources": "1.4.3", + "webpack-subresource-integrity": "1.1.0-rc.6", + "worker-plugin": "3.2.0" + }, + "dependencies": { + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + } + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.803.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.803.29.tgz", + "integrity": "sha512-E/MXtKc3oaP7UvQm0g4ayfH8ImEoQnRWseKD4jjYG6TbTIqfIyHCZRcKIr3svY28hzASbro5IZI6SugG+llvFw==", + "dev": true, + "requires": { + "loader-utils": "1.2.3", + "source-map": "0.7.3", + "tslib": "1.10.0", + "typescript": "3.5.3", + "webpack-sources": "1.4.3" + }, + "dependencies": { + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.803.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.803.29.tgz", + "integrity": "sha512-3dJ3iEGU6AFT8VFTe72T9uNLobfd18Sq5Hz22UCCYji9K3ZyVc/bn5uXVVX+/Yj91MFtXuhOjLj7Z+XDeNy+OQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.803.29", + "@angular-devkit/core": "8.3.29", + "rxjs": "6.4.0" + } + }, + "@angular-devkit/core": { + "version": "8.3.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.29.tgz", + "integrity": "sha512-4jdja9QPwR6XG14ZSunyyOWT3nE2WtZC5IMDIBZADxujXvhzOU0n4oWpy6/JVHLUAxYNNgzLz+/LQORRWndcPg==", + "dev": true, + "requires": { + "ajv": "6.12.3", + "fast-json-stable-stringify": "2.0.0", + "magic-string": "0.25.3", + "rxjs": "6.4.0", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "8.3.29", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.29.tgz", + "integrity": "sha512-AFJ9EK0XbcNlO5Dm9vr0OlBo1Nw6AaFXPR+DmHGBdcDDHxqEmYYLWfT+JU/8U2YFIdgrtlwvdtf6UQ3V2jdz1g==", + "dev": true, + "requires": { + "@angular-devkit/core": "8.3.29", + "rxjs": "6.4.0" + } + }, + "@angular/animations": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.14.tgz", + "integrity": "sha512-3Vc9TnNpKdtvKIXcWDFINSsnwgEMiDmLzjceWg1iYKwpeZGQahUXPoesLwQazBMmxJzQiA4HOMj0TTXKZ+Jzkg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/cdk": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.2.3.tgz", + "integrity": "sha512-ZwO5Sn720RA2YvBqud0JAHkZXjmjxM0yNzCO8RVtRE9i8Gl26Wk0j0nQeJkVm4zwv2QO8MwbKUKGTMt8evsokA==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^1.7.1" + } + }, + "@angular/cdk-experimental": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-8.2.3.tgz", + "integrity": "sha512-pGWLh+njlSK2HavyES1g9xZQXmnqj9HJalL+EJii1gLEyvEatdJfsBxidxvdxrXaIYrY0ZfkiG2qW/4wOOKRHA==", + "requires": { + "tslib": "^1.7.1" + } + }, + "@angular/cli": { + "version": "8.3.29", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.29.tgz", + "integrity": "sha512-pW+iU0eKHIae+A1b9W5g8DKefMQcehZ+drGKs4Hryh8G+XGFS00BIWkmh6c1mydWTEhdsFlhdjD/rXCem7MAQQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.803.29", + "@angular-devkit/core": "8.3.29", + "@angular-devkit/schematics": "8.3.29", + "@schematics/angular": "8.3.29", + "@schematics/update": "0.803.29", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "^4.1.1", + "ini": "1.3.5", + "inquirer": "6.5.1", + "npm-package-arg": "6.1.0", + "npm-pick-manifest": "3.0.2", + "open": "6.4.0", + "pacote": "9.5.5", + "read-package-tree": "5.3.1", + "rimraf": "3.0.0", + "semver": "6.3.0", + "symbol-observable": "1.2.0", + "universal-analytics": "^0.4.20", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "@angular/common": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-8.2.14.tgz", + "integrity": "sha512-Qmt+aX2quUW54kaNT7QH7WGXnFxr/cC2C6sf5SW5SdkZfDQSiz8IaItvieZfXVQUbBOQKFRJ7TlSkt0jI/yjvw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.14.tgz", + "integrity": "sha512-ABZO4E7eeFA1QyJ2trDezxeQM5ZFa1dXw1Mpl/+1vuXDKNjJgNyWYwKp/NwRkLmrsuV0yv4UDCDe4kJOGbPKnw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler-cli": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-8.2.14.tgz", + "integrity": "sha512-XDrTyrlIZM+0NquVT+Kbg5bn48AaWFT+B3bAT288PENrTdkuxuF9AhjFRZj8jnMdmaE4O2rioEkXBtl6z3zptA==", + "dev": true, + "requires": { + "canonical-path": "1.0.0", + "chokidar": "^2.1.1", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.7.2", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "source-map": "^0.6.1", + "tslib": "^1.9.0", + "yargs": "13.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": 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, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "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" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": 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, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/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" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/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": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "yargs": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.1.0.tgz", + "integrity": "sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==", + "dev": true, + "requires": { + "cliui": "^4.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.0.0" + } + } + } + }, + "@angular/core": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.14.tgz", + "integrity": "sha512-zeePkigi+hPh3rN7yoNENG/YUBUsIvUXdxx+AZq+QPaFeKEA2FBSrKn36ojHFrdJUjKzl0lPMEiGC2b6a6bo6g==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/forms": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-8.2.14.tgz", + "integrity": "sha512-zhyKL3CFIqcyHJ/TQF/h1OZztK611a6rxuPHCrt/5Sn1SuBTJJQ1pPTkOYIDy6IrCrtyANc8qB6P17Mao71DNQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/language-service": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-8.2.14.tgz", + "integrity": "sha512-7EhN9JJbAJcH2xCa+rIOmekjiEuB0qwPdHuD5qn/wwMfRzMZo+Db4hHbR9KHrLH6H82PTwYKye/LLpDaZqoHOA==", + "dev": true + }, + "@angular/material": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-8.2.3.tgz", + "integrity": "sha512-SOczkIaqes+r+9XF/UUiokidfFKBpHkOPIaFK857sFD0FBNPvPEpOr5oHKCG3feERRwAFqHS7Wo2ohVEWypb5A==", + "requires": { + "tslib": "^1.7.1" + } + }, + "@angular/platform-browser": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.14.tgz", + "integrity": "sha512-MtJptptyKzsE37JZ2VB/tI4cvMrdAH+cT9pMBYZd66YSZfKjIj5s+AZo7z8ncoskQSB1o3HMfDjSK7QXGx1mLQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.2.14.tgz", + "integrity": "sha512-mO2JPR5kLU/A3AQngy9+R/Q5gaF9csMStBQjwsCRI0wNtlItOIGL6+wTYpiTuh/ux+WVN1F2sLcEYU4Zf1ud9A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/router": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-8.2.14.tgz", + "integrity": "sha512-DHA2BhODqV7F0g6ZKgFaZgbsqzHHWRcfWchCOrOVKu2rYiKUTwwHVLBgZAhrpNeinq2pWanVYSIhMr7wy+LfEA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/compat-data": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.0.tgz", + "integrity": "sha512-jAbCtMANC9ptXxbSVXIqV/3H0bkh7iyyv6JS5lu10av45bcc2QmDNJXkASZCFwbBt75Q0AEq/BB+bNa3x1QgYQ==", + "dev": true + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.0.tgz", + "integrity": "sha512-8lnf4QcyiQMf5XQp47BltuMTocsOh6P0z/vueEh8GzhmWWlDbdvOoI5Ziddg0XYhmnx35HyByUW51/9NprF8cA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.0.tgz", + "integrity": "sha512-NbDFJNjDgxE7IkrHp5gq2+Tr8bEdCLKYN90YDQEjMiTMUAFAcShNkaH8kydcmU0mEQTiQY0Ydy/+1xfS2OCEnw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.12.0", + "@babel/helper-validator-option": "^7.12.0", + "browserslist": "^4.12.0", + "semver": "^5.5.0" + }, + "dependencies": { + "browserslist": { + "version": "4.14.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz", + "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001135", + "electron-to-chromium": "^1.3.571", + "escalade": "^3.1.0", + "node-releases": "^1.1.61" + } + }, + "caniuse-lite": { + "version": "1.0.30001148", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", + "integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.0.tgz", + "integrity": "sha512-YBqH+3wLcom+tko8/JLgRcG8DMqORgmjqNRNI751gTioJSZHWFybO1mRoLtJtWIlYSHY+zT9LqqnbbK1c3KIVQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", + "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.0.tgz", + "integrity": "sha512-I0d/bgzgzgLsJMk7UZ0TN2KV3OGjC/t/9Saz8PKb9jrcEAXhgjGysOgp4PDKydIKjUv/gj2St4ae+ov8l+T9Xg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.0.tgz", + "integrity": "sha512-1ZTMoCiLSzTJLbq7mSaTHki4oIrBIf/dUbzdhwTrvtMU3ZNVKwQmGae3gSiqppo7G8HAgnXmc43rfEaD8yYLLQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.0", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.0", + "@babel/types": "^7.12.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.11.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", + "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.0.tgz", + "integrity": "sha512-9kycFdq2c9e7PXZOr2z/ZqTFF9OzFu287iFwYS+CiDVPuoTCfY8hoTsIqNQNetQjlqoRsRyJFrMG1uhGAR4EEw==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.0", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.0", + "@babel/types": "^7.12.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", + "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.0.tgz", + "integrity": "sha512-NRfKaAQw/JCMsTFUdJI6cp4MoJGGVBRQTRSiW1nwlGldNqzjB9jqWI0SZqQksC724dJoKqwG+QqfS9ib7SoVsw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", + "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.0.tgz", + "integrity": "sha512-dYmySMYnlus2jwl7JnnajAj11obRStZoW9cG04wh4ZuhozDn11tDUrhHcUZ9iuNHqALAhh60XqNaYXpvuuE/Gg==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", + "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", + "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", + "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.0.tgz", + "integrity": "sha512-JpNWix2VP2ue31r72fKytTE13nPX1fxl1mudfTaTwcDhl3iExz5NZjQBq012b/BQ6URWoc/onI73pZdYlAfihg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", + "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.10.4" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", + "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.0.tgz", + "integrity": "sha512-CXu9aw32FH/MksqdKvhpiH8pSvxnXJ33E7I7BGNE9VzNRpWgpNzvPpds/tW9E0pjmX9+D1zAHRyHbtyeTboo2g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", + "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", + "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", + "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", + "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", + "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", + "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", + "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", + "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", + "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", + "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", + "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", + "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", + "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", + "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", + "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", + "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", + "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", + "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.0.tgz", + "integrity": "sha512-h2fDMnwRwBiNMmTGAWqUo404Z3oLbrPE6hyATecyIbsEsrbM5gjLbfKQLb6hjiouMlGHH+yliYBbc4NPgWKE/g==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.0", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-identifier": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", + "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", + "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", + "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", + "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", + "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", + "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", + "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", + "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", + "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", + "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", + "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", + "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", + "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", + "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.7.tgz", + "integrity": "sha512-BYftCVOdAYJk5ASsznKAUl53EMhfBbr8CJ1X+AJLfGPscQkwJFiaV/Wn9DPH/7fzm2v6iRYJKYHSqyynTGw0nw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.8.6", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.8.3", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.8.3", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.8.6", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.8.3", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.8.6", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/plugin-transform-modules-commonjs": "^7.8.3", + "@babel/plugin-transform-modules-systemjs": "^7.8.3", + "@babel/plugin-transform-modules-umd": "^7.8.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.8.7", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/types": "^7.8.7", + "browserslist": "^4.8.5", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/runtime": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.0.tgz", + "integrity": "sha512-lS4QLXQ2Vbw2ubfQjeQcn+BZgZ5+ROHW9f+DWjEp5Y+NHYmkRGKqHSJ1tuhbUauKu2nhZNTBIvsIQ8dXfY5Gjw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.0.tgz", + "integrity": "sha512-ZU9e79xpOukCNPkQ1UzR4gJKCruGckr6edd8v8lmKpSk8iakgUIvb+5ZtaKKV9f7O+x5r+xbMDDIbzVpUoiIuw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.0", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.0", + "@babel/types": "^7.12.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.0.tgz", + "integrity": "sha512-ggIyFmT2zMaYRheOfPDQ4gz7QqV3B+t2rjqjbttDJxMcb7/LukvWCmlIl1sWcOxrvwpTDd+z0OytzqsbGeb3/g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@fortawesome/angular-fontawesome": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.5.0.tgz", + "integrity": "sha512-5IR/jNMddiEpgApcqSMfp7l5kZqOtxQYzkSLW6iZ4MZHbQQ+Kc9tu9qi2hro6VFSk/sPxeYEzkzGZUNrMYiwOg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.34.tgz", + "integrity": "sha512-XcIn3iYbTEzGIxD0/dY5+4f019jIcEIWBiHc3KrmK/ROahwxmZ/s+tdj97p/5K0klz4zZUiMfUlYP0ajhSJjmA==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.34", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.34.tgz", + "integrity": "sha512-0KNN0nc5eIzaJxlv43QcDmTkDY1CqeN6J7OCGSs+fwGPdtv0yOQqRjieopBCmw+yd7uD3N2HeNL3Zm5isDleLg==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.34" + } + }, + "@fortawesome/free-brands-svg-icons": { + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.2.tgz", + "integrity": "sha512-YPlVjE1cEO+OJ9I9ay3TQ3I88+XkxMTYwnnddqAboxLhPNGncsHV0DjWOVLCyuAY66yPfyndWwVn4v7vuqsO1g==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.34" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.2.tgz", + "integrity": "sha512-ZfCU+QjaFsdNZmOGmfqEWhzI3JOe37x5dF4kz9GeXvKn/sTxhqMtZ7mh3lBf76SvcYY5/GKFuyG7p1r4iWMQqw==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.34" + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@kubernetes/client-node": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.12.2.tgz", + "integrity": "sha512-J0UwyFl1Iv/IZ6WMP7LaizBEoKPnqwtc8tIO2q/X+EuDT7eGpPPAMHXSEOC/EI9JGIf0FaJEcDHhB/Dio/mKhw==", + "dev": true, + "requires": { + "@types/js-yaml": "^3.12.1", + "@types/node": "^10.12.0", + "@types/request": "^2.47.1", + "@types/stream-buffers": "^3.0.3", + "@types/tar": "^4.0.3", + "@types/underscore": "^1.8.9", + "@types/ws": "^6.0.1", + "byline": "^5.0.0", + "execa": "1.0.0", + "isomorphic-ws": "^4.0.1", + "js-yaml": "^3.13.1", + "jsonpath-plus": "^0.19.0", + "openid-client": "^4.1.1", + "request": "^2.88.0", + "rfc4648": "^1.3.0", + "shelljs": "^0.8.2", + "stream-buffers": "^3.0.2", + "tar": "^6.0.2", + "tmp-promise": "^3.0.2", + "tslib": "^1.9.3", + "underscore": "^1.9.1", + "ws": "^7.3.1" + }, + "dependencies": { + "@types/node": { + "version": "10.17.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.39.tgz", + "integrity": "sha512-dJLCxrpQmgyxYGcl0Ae9MTsQgI22qHHcGFj/8VKu7McJA5zQpnuGjoksnxbo1JxSjW/Nahnl13W8MYZf01CZHA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "dev": true + } + } + }, + "@ngtools/webpack": { + "version": "8.3.29", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.3.29.tgz", + "integrity": "sha512-7uB7dlAHR7RmxcQCYidnWRR1tFRJq7CzI+MM3725ibAvi4HnM5viC/HnKRTK7V+3iS1C0l0u0Gyo/769NsUDTQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "8.3.29", + "enhanced-resolve": "4.1.0", + "rxjs": "6.4.0", + "tree-kill": "1.2.2", + "webpack-sources": "1.4.3" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "@panva/asn1.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", + "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", + "dev": true + }, + "@schematics/angular": { + "version": "8.3.29", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.29.tgz", + "integrity": "sha512-If+UhCsQzCgnQymiiF8dQRoic34+RgJ6rV0n4k7Tm4N2xNYJOG7ajjzKM7PIeafsF50FKnFP8dqaNGxCMyq5Ew==", + "dev": true, + "requires": { + "@angular-devkit/core": "8.3.29", + "@angular-devkit/schematics": "8.3.29" + } + }, + "@schematics/update": { + "version": "0.803.29", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.29.tgz", + "integrity": "sha512-Syf6h6DYeu1WU9aLihMwIgVASpcHCxUYqhZyHfQABiK8NkdlZ+KAp4cOxihsZyDqIJNLWON+0/FLPAQF3BXh5Q==", + "dev": true, + "requires": { + "@angular-devkit/core": "8.3.29", + "@angular-devkit/schematics": "8.3.29", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "pacote": "9.5.5", + "rxjs": "6.4.0", + "semver": "6.3.0", + "semver-intersect": "1.4.0" + } + }, + "@sindresorhus/is": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", + "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==", + "dev": true + }, + "@swimlane/ngx-charts": { + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-13.0.4.tgz", + "integrity": "sha512-4EvwclbctVQ5VzPBC63DJsg+jwQU/NlR/yQ9ObVH5acefEbS8kupn3cp/gaWftLTQ0jdXqRqycHNkLOQZfa3XQ==", + "requires": { + "d3-array": "^2.4.0", + "d3-brush": "^1.1.5", + "d3-color": "^1.4.0", + "d3-format": "^1.4.2", + "d3-hierarchy": "^1.1.9", + "d3-interpolate": "^1.4.0", + "d3-scale": "^3.2.1", + "d3-selection": "^1.4.1", + "d3-shape": "^1.3.7", + "d3-time-format": "^2.2.2", + "d3-transition": "^1.3.2" + }, + "dependencies": { + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + } + } + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, + "@types/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==", + "dev": true + }, + "@types/d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-NLzD02m5PiD1KLEDjLN+MtqEcFYn4ZL9+Rqc9ZwARK1cpKZXd91zBETbe6wpBB6Ia0D0VZbpmbW3+BsGPGnCpA==", + "dev": true, + "requires": { + "@types/d3-path": "^1" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true + }, + "@types/jasmine": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.16.tgz", + "integrity": "sha512-Nveep4zKGby8uIvG2AEUyYOwZS8uVeHK9TgbuWYSawUDDdIgfhCKz28QzamTo//Jk7Ztt9PO3f+vzlB6a4GV1Q==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", + "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/js-yaml": { + "version": "3.12.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz", + "integrity": "sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/minipass": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz", + "integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", + "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/request": { + "version": "2.48.5", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", + "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", + "dev": true + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stream-buffers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.3.tgz", + "integrity": "sha512-Z7AVMMlkI8NTWF0qGhC4QIX0zkV/+y0J8x7b/RsHrN0310+YNjoJd8UrApCiGBCWtKjxS9QhNqLi2UJNToh5hA==", + "dev": true, + "requires": { + "@types/minipass": "*", + "@types/node": "*" + } + }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "dev": true + }, + "@types/underscore": { + "version": "1.10.24", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz", + "integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz", + "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/ws": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", + "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "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.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "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.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "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.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "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.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "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.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "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.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "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.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "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.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "ace-builds": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.12.tgz", + "integrity": "sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg==" + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "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.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "app-root-path": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", + "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", + "dev": true + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/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.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz", + "integrity": "sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==", + "dev": true, + "requires": { + "browserslist": "^4.6.3", + "caniuse-lite": "^1.0.30000980", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.17", + "postcss-value-parser": "^4.0.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", + "dev": true + }, + "axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/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" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "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": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": 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" + } + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "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" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "brace": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz", + "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "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": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.10.0.tgz", + "integrity": "sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001035", + "electron-to-chromium": "^1.3.378", + "node-releases": "^1.1.52", + "pkg-up": "^3.1.0" + } + }, + "browserstack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.0.tgz", + "integrity": "sha512-HJDJ0TSlmkwnt9RZ+v5gFpa1XZTBYTj0ywvLwJ3241J7vMw2jAsGNVhKHtmCOyg+VxeLZyaibO9UL71AsUeDIw==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.2.tgz", + "integrity": "sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg==", + "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" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "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" + } + }, + "cacheable-lookup": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", + "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + } + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001035", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz", + "integrity": "sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-dependency-plugin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", + "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "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.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "codelyzer": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz", + "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==", + "dev": true, + "requires": { + "app-root-path": "^2.2.1", + "aria-query": "^3.0.0", + "axobject-query": "2.0.2", + "css-selector-tokenizer": "^0.7.1", + "cssauron": "^1.4.0", + "damerau-levenshtein": "^1.0.4", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz", + "integrity": "sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA==", + "dev": true, + "requires": { + "cacache": "^15.0.4", + "fast-glob": "^3.2.4", + "find-cache-dir": "^3.3.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.1", + "schema-utils": "^2.7.0", + "serialize-javascript": "^4.0.0", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "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" + } + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", + "dev": true + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, + "coverage-istanbul-loader": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz", + "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==", + "dev": true, + "requires": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.0", + "loader-utils": "^1.2.3", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.6.1" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, + "css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d3": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", + "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" + }, + "d3-array": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.8.0.tgz", + "integrity": "sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw==" + }, + "d3-brush": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", + "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-scale": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz", + "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==", + "requires": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "1 - 2", + "d3-time-format": "2 - 3" + } + }, + "d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, + "d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-djpGlA779ua+rImicYyyjnOjeubyhql1Jyn1HK0bTyawuH76UQRWXd+pftr67H6Fa8hSwetkgb/0id3agKWykw==", + "requires": { + "d3-path": "1 - 2" + } + }, + "d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q==" + }, + "d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "requires": { + "d3-time": "1" + }, + "dependencies": { + "d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + } + } + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-fns": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", + "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==" + }, + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + } + }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/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.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "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.npmjs.org/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.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.580", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.580.tgz", + "integrity": "sha512-5flHTbRpptO6h3lQUG4zdSAxryAS3PrZOkLpLS0DL5/y2LBf+l9HJ8X6UBorNs1QRBrMR7u/QvkdK+GlekW1kQ==", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "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" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "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.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "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": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "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.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz", + "integrity": "sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.0" + } + }, + "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 + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "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" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "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" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", + "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "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.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/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.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/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.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "got": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.7.0.tgz", + "integrity": "sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg==", + "dev": true, + "requires": { + "@sindresorhus/is": "^3.1.1", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "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.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/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" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "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.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/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" + } + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/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" + } + }, + "http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz", + "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "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 + }, + "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 + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/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.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-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-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/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.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/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": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-api": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.7.tgz", + "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==", + "dev": true, + "requires": { + "async": "^2.6.2", + "compare-versions": "^3.4.0", + "fileset": "^2.0.3", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.5", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", + "minimatch": "^3.0.4", + "once": "^1.4.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + } + } + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0" + } + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.4.0.tgz", + "integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", + "dev": true, + "requires": { + "colors": "1.1.2" + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jose": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.2.tgz", + "integrity": "sha512-yD93lsiMA1go/qxSY/vXWBodmIZJIxeB7QhFi8z1yQ3KUwKENqI9UA8VCHlQ5h3x1zWuWZjoY87ByQzkQbIrQg==", + "dev": true, + "requires": { + "@panva/asn1.js": "^1.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsonpath-plus": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-0.19.0.tgz", + "integrity": "sha512-GSVwsrzW9LsA5lzsqe4CkuZ9wp+kxBb2GwNniaWzI2YFn5Ig42rSW8ZxVpWXaAfakXNrx5pgY5AbQq7kzX29kg==", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/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" + } + }, + "jszip": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "karma": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.1.0.tgz", + "integrity": "sha512-xckiDqyNi512U4dXGOOSyLKPwek6X/vUizSy2f3geYevbLj+UIdvNwbn7IwfUIL2g1GXEPWt/87qFD1fBbl/Uw==", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "braces": "^2.3.2", + "chokidar": "^2.0.3", + "colors": "^1.1.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.11", + "log4js": "^4.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": 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, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", + "dev": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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" + } + }, + "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" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": 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, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/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.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/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" + } + } + } + }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "dev": true, + "requires": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.6.tgz", + "integrity": "sha512-WFh77RI8bMIKdOvI/1/IBmgnM+Q7NOLhnwG91QJrM8lW+CIXCjTzhhUsT/svLvAkLmR10uWY4RyYbHMLkTglvg==", + "dev": true, + "requires": { + "istanbul-api": "^2.1.6", + "minimatch": "^3.0.4" + } + }, + "karma-jasmine": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", + "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", + "dev": true, + "requires": { + "jasmine-core": "^3.3" + } + }, + "karma-jasmine-html-reporter": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", + "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", + "dev": true + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "less": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", + "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", + "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^4.0.1" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "license-webpack-plugin": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.2.tgz", + "integrity": "sha512-7poZHRla+ae0eEButlwMrPpkXyhNVBf2EHePYWT0jyLnI6311/OXJkTI2sOIRungRpQgU2oDMpro5bSFPT5F0A==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "log4js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", + "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", + "dev": true, + "requires": { + "date-format": "^2.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.0", + "rfdc": "^1.1.4", + "streamroller": "^1.0.6" + } + }, + "loglevel": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", + "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "magic-string": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", + "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "make-fetch-happen": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "material-icons": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/material-icons/-/material-icons-0.3.1.tgz", + "integrity": "sha512-5Hbj76A6xDPcDZEbM4oxTknhWuMwGWnAHVLLPCEq9eVlcHb0fn4koU9ZeyMy1wjARtDEPAHfd5ZdL2Re5hf0zQ==" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "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.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "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.npmjs.org/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" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", + "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "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 + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "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" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "ng2-ace-editor": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/ng2-ace-editor/-/ng2-ace-editor-0.3.9.tgz", + "integrity": "sha512-e8Q4YCirlL/OEiekewmzupG+zV3prYsiYmQnRzQzd0wNgsPjOLOdb0it7cCbzFfIXKGyIIHKTW5584WxPr2LnQ==", + "requires": { + "ace-builds": "^1.4.2", + "brace": "^0.11.1" + } + }, + "ngx-json-viewer": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ngx-json-viewer/-/ngx-json-viewer-2.4.0.tgz", + "integrity": "sha512-26QmLp+0ds90aFug3KbSIwqtmQgCcJYFNNNcmcZHgPRj75nhKzbo4ceKxkhWmY5auKZClVO0HTZSs5bBhgb1Bw==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-fetch-npm": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz", + "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.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==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "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.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.63", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.63.tgz", + "integrity": "sha512-ukW3iCfQaoxJkSPN+iK7KznTeqDGVJatAEuXsJERYHa9tn/KaT5lBdIyxQjLEVTzSkyjJEuQ17/vaEjrOauDkg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "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.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/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-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-package-arg": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "npm-registry-fetch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz", + "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/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.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-hash": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", + "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==", + "dev": true + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", + "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "oidc-token-hash": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.0.tgz", + "integrity": "sha512-8Yr4CZSv+Tn8ZkN3iN2i2w2G92mUKClp4z7EGUfdsERiYSbj7P4i/NHm72ft+aUdsiFx9UdIPSTwbyzQ6C4URg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "openid-client": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-4.2.0.tgz", + "integrity": "sha512-1S7pLF4xkWkX3l0Gpt69jlXLiO+oRUBjMcc9MZCyoyzz5+KnHUs2D5xMx4RIH/g9QmAwob1zg7mKZLdvOB52Bw==", + "dev": true, + "requires": { + "base64url": "^3.0.1", + "got": "^11.6.2", + "jose": "^2.0.2", + "lru-cache": "^6.0.0", + "make-error": "^1.3.6", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.0", + "p-any": "^3.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-any": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-any/-/p-any-3.0.0.tgz", + "integrity": "sha512-5rqbqfsRWNb0sukt0awwgJMlaep+8jV45S15SKKB34z4UuzjcofIfnriCBhWjZP2jbVtjt9yRl7buB6RlKsu9w==", + "dev": true, + "requires": { + "p-cancelable": "^2.0.0", + "p-some": "^5.0.0" + } + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-some": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-some/-/p-some-5.0.0.tgz", + "integrity": "sha512-Js5XZxo6vHjB9NOYAzWDYAIyyiPvva0DWESAIWIK7uhSpGsyg5FwUPxipU/SOQx5x9EqhOh545d1jo6cVkitig==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0", + "p-cancelable": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pacote": { + "version": "9.5.5", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.5.tgz", + "integrity": "sha512-jAEP+Nqj4kyMWyNpfTU/Whx1jA7jEc5cCOlurm0/0oL+v8TAp1QSsK83N7bYe+2bEdFzMAtPG5TBebjzzGV0cA==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.2.3", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.8", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "npm-pick-manifest": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "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 + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/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.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "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.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", + "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + } + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "protractor": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.4.tgz", + "integrity": "sha512-BaL4vePgu3Vfa/whvTUAlgaCAId4uNSGxIFSCXMgj7LMYENPWLp85h5RBi9pdpX/bWQ8SF6flP7afmi2TC4eHw==", + "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.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/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.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/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" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/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" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", + "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" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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.npmjs.org/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.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "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" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/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.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + } + } + }, + "raw-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz", + "integrity": "sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^2.0.1" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "dev": true, + "requires": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "dev": true, + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "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" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfc4648": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.4.0.tgz", + "integrity": "sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg==", + "dev": true + }, + "rfdc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.22.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.22.9.tgz", + "integrity": "sha512-FzU1X2V8DlnqabrL4u7OBwD2vcOzNMongEJEx3xMEhWY/v26FFR3aG0hyeu2T965sfR0E9ufJwmG+Qjz78vFPQ==", + "dev": true, + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.2.0.tgz", + "integrity": "sha512-h8yUWaWtsbuIiOCgR9fd9c2lRXZ2uG+h8Dzg/AGNj+Hg/3TO8+BBAW9mEP+mh8ei+qBKqSJ0F1FLlYjNBc61OA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "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.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "requires": { + "semver": "^5.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "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.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "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.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "dev": true, + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", + "dev": true + }, + "socket.io-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "socks": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", + "dev": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "dev": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/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": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + } + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "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.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "speed-measure-webpack-plugin": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz", + "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "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": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "streamroller": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", + "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", + "dev": true, + "requires": { + "async": "^2.6.2", + "date-format": "^2.0.0", + "debug": "^3.2.6", + "fs-extra": "^7.0.1", + "lodash": "^4.17.14" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "style-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.0.tgz", + "integrity": "sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.1" + } + }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "terser": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", + "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-3.0.3.tgz", + "integrity": "sha512-bZFnotuIKq5Rqzrs+qIwFzGdKdffV9epG5vDSEbYzvKAhPeR5RbbrQysfPgbIIMhNAQtZD2hGwBfSKUXjXZZZw==", + "dev": true, + "requires": { + "cacache": "^15.0.4", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.0.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.6.13", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", + "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "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" + } + }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmp-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.2.tgz", + "integrity": "sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==", + "dev": true, + "requires": { + "tmp": "^0.2.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" + } + }, + "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" + } + } + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "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" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tslint": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.15.0.tgz", + "integrity": "sha512-6bIEujKR21/3nyeoX2uBnE8s+tMXCQXhqMmaIPJpHmXJoBJPTLcI7/VHRtUwMhnLVdwLqqY3zmd8Dxqa5CVdJA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.13.0", + "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": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "dev": true + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "underscore": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", + "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universal-analytics": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz", + "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "request": "^2.88.2", + "uuid": "^3.0.0" + } + }, + "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.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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.npmjs.org/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.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "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 + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "dev": true, + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util-promisify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "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 + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/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" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", + "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", + "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" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": 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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "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" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "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" + } + }, + "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" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/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-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": 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" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webpack": { + "version": "4.39.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.39.2.tgz", + "integrity": "sha512-AKgTfz3xPSsEibH00JfZ9sHXGUwIQ6eZ9tLN8+VLzachk1Cw2LVmy+4R7ZiwTa9cZZ15tzySjeMui/UnSCAZhA==", + "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": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "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": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/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" + } + } + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": 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, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "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" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": 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, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/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" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-merge": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz", + "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.1.0-rc.6", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.6.tgz", + "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==", + "dev": true, + "requires": { + "webpack-core": "^0.6.8" + } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "dev": true, + "requires": { + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "worker-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-3.2.0.tgz", + "integrity": "sha512-W5nRkw7+HlbsEt3qRP6MczwDDISjiRj2GYt9+bpe8A2La00TmJdwzG5bpdMXhRt1qcWmwAvl1TiKaHRa+XDS9Q==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "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.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.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": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zone.js": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.9.1.tgz", + "integrity": "sha512-GkPiJL8jifSrKReKaTZ5jkhrMEgXbXYC+IPo1iquBjayRa0q86w3Dipjn8b415jpitMExe9lV8iTsv8tk3DGag==" + } + } +} diff --git a/pkg/new-ui/v1beta1/frontend/package.json b/pkg/new-ui/v1beta1/frontend/package.json new file mode 100644 index 00000000000..d158467c136 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/package.json @@ -0,0 +1,69 @@ +{ + "name": "frontend", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "npm run copyLibAssets && ng serve --proxy-config proxy.conf.json", + "build:watch": "npm run copyLibAssets && ng build --base-href /katib/ --deploy-url /katib/static/ --outputPath dist/static --watch", + "build:prod": "npm run copyLibAssets && ng build --base-href /katib/ --deploy-url /katib/static/ --outputPath dist/static --prod", + "copyLibAssets": "cp ./node_modules/kubeflow/assets/* ./src/assets/", + "test": "ng test", + "lint": "ng lint", + "format:check": "prettier --check 'src/**/*.{jsx,js,ts,html,scss,css}' || node scripts/check-format-error.js", + "format:write": "prettier --write 'src/**/*.{jsx,js,ts,html,scss,css}'", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "~8.2.14", + "@angular/cdk": "^8.2.3", + "@angular/cdk-experimental": "^8.2.3", + "@angular/common": "~8.2.14", + "@angular/compiler": "~8.2.14", + "@angular/core": "~8.2.14", + "@angular/forms": "~8.2.14", + "@angular/material": "^8.2.3", + "@angular/platform-browser": "~8.2.14", + "@angular/platform-browser-dynamic": "~8.2.14", + "@angular/router": "~8.2.14", + "@fortawesome/angular-fontawesome": "^0.5.0", + "@fortawesome/fontawesome-svg-core": "^1.2.26", + "@fortawesome/free-brands-svg-icons": "^5.12.0", + "@fortawesome/free-solid-svg-icons": "^5.12.0", + "@swimlane/ngx-charts": "^13.0.4", + "d3": "^3.5.17", + "d3-shape": "^2.0.0", + "date-fns": "1.29.0", + "js-yaml": "^4.0.0", + "lodash-es": "4.17.11", + "material-icons": "^0.3.1", + "ng2-ace-editor": "^0.3.9", + "ngx-json-viewer": "^2.4.0", + "rxjs": "~6.4.0", + "tslib": "^1.10.0", + "zone.js": "~0.9.1" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.803.20", + "@angular/cli": "~8.3.22", + "@angular/compiler-cli": "~8.2.14", + "@angular/language-service": "~8.2.14", + "@kubernetes/client-node": "^0.12.2", + "@types/d3-shape": "^2.0.0", + "@types/jasmine": "~3.3.8", + "@types/jasminewd2": "~2.0.3", + "@types/node": "~8.9.4", + "codelyzer": "^5.0.0", + "jasmine-core": "~3.4.0", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~4.1.0", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "~2.0.1", + "karma-jasmine": "~2.0.1", + "karma-jasmine-html-reporter": "^1.4.0", + "protractor": "~5.4.0", + "ts-node": "~7.0.0", + "tslint": "~5.15.0", + "typescript": "~3.5.3" + } +} diff --git a/pkg/new-ui/v1beta1/frontend/proxy.conf.json b/pkg/new-ui/v1beta1/frontend/proxy.conf.json new file mode 100644 index 00000000000..269fd1580a3 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/proxy.conf.json @@ -0,0 +1,8 @@ +{ + "/katib/*": { + "target": "http://localhost:8000", + "secure": false, + "logLevel": "debug", + "changeOrigin": true + } +} diff --git a/pkg/new-ui/v1beta1/frontend/scripts/check-format-error.js b/pkg/new-ui/v1beta1/frontend/scripts/check-format-error.js new file mode 100644 index 00000000000..476196e26a3 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/scripts/check-format-error.js @@ -0,0 +1,6 @@ +console.error(); +console.error('-----------------------------------------------'); +console.error('Please run `npm run format:write` to format your code.'); +console.error('-----------------------------------------------'); +console.error(); +process.exit(1); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/app-routing.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/app-routing.module.ts new file mode 100644 index 00000000000..98591fec2d4 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/app-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { ExperimentsComponent } from './pages/experiments/experiments.component'; +import { ExperimentDetailsComponent } from './pages/experiment-details/experiment-details.component'; +import { ExperimentCreationComponent } from './pages/experiment-creation/experiment-creation.component'; + +const routes: Routes = [ + { path: '', component: ExperimentsComponent }, + { path: 'experiment/:experimentName', component: ExperimentDetailsComponent }, + { path: 'new', component: ExperimentCreationComponent }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], +}) +export class AppRoutingModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/app.component.html b/pkg/new-ui/v1beta1/frontend/src/app/app.component.html new file mode 100644 index 00000000000..6969378d43b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/app.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/app.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/app.component.spec.ts new file mode 100644 index 00000000000..986b2b14496 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/app.component.spec.ts @@ -0,0 +1,31 @@ +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AppComponent], + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'frontend'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('frontend'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('.content span').textContent).toContain( + 'frontend app is running!', + ); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/app.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/app.component.ts new file mode 100644 index 00000000000..dd6d8039901 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/app.component.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core'; +import { environment } from '@app/environment'; +import { MatIconRegistry } from '@angular/material/icon'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', +}) +export class AppComponent { + constructor( + private matIconRegistry: MatIconRegistry, + private domSanitizer: DomSanitizer, + ) { + this.matIconRegistry.addSvgIcon( + `pipeline-centered`, + domSanitizer.bypassSecurityTrustResourceUrl( + `${ + this.env.production + ? '/katib/static/assets/pipeline-centered.svg' + : '/assets/pipeline-centered.svg' + }`, + ), + ); + } + + title = 'frontend'; + + env = environment; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/app.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/app.module.ts new file mode 100644 index 00000000000..98af0db9cbd --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/app.module.ts @@ -0,0 +1,26 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { KubeflowModule } from 'kubeflow'; + +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { ExperimentsModule } from './pages/experiments/experiments.module'; +import { ExperimentDetailsModule } from './pages/experiment-details/experiment-details.module'; +import { ExperimentCreationModule } from './pages/experiment-creation/experiment-creation.module'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + BrowserModule, + AppRoutingModule, + KubeflowModule, + ExperimentsModule, + ExperimentDetailsModule, + ReactiveFormsModule, + ExperimentCreationModule, + ], + providers: [], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-settings.const.ts b/pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-settings.const.ts new file mode 100644 index 00000000000..c070f217e12 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-settings.const.ts @@ -0,0 +1,228 @@ +import { + AlgorithmsEnum, + AlgorithmSettingType, +} from '../enumerations/algorithms.enum'; + +export interface AlgorithmSetting { + name: string; + value: any; + values?: any[]; + type: AlgorithmSettingType; +} + +export const GridSettings: AlgorithmSetting[] = []; + +export const RandomSearchSettings: AlgorithmSetting[] = [ + { + name: 'random_state', + value: 'None', + type: AlgorithmSettingType.STRING, + }, +]; + +export const BayesianOptimizationSettings: AlgorithmSetting[] = [ + { + name: 'base_estimator', + value: 'GP', + values: ['GP', 'RF', 'ET', 'GBRT'], + type: AlgorithmSettingType.STRING, + }, + { + name: 'n_initial_points', + value: 10, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'acq_func', + value: 'gp_hedge', + values: ['gp_hedge', 'LCB', 'EI', 'PI', 'EIps', 'PIps'], + type: AlgorithmSettingType.STRING, + }, + { + name: 'acq_optimizer', + value: 'auto', + values: ['auto', 'sampling', 'lbfgs'], + type: AlgorithmSettingType.STRING, + }, + { + name: 'random_state', + value: 'None', + type: AlgorithmSettingType.INTEGER, + }, +]; + +export const HyperbandSettings: AlgorithmSetting[] = []; + +export const TPESettings: AlgorithmSetting[] = [ + { + name: 'gamma', + value: null, + type: AlgorithmSettingType.STRING, + }, + + { + name: 'prior_weight', + value: null, + type: AlgorithmSettingType.STRING, + }, + { + name: 'n_EI_candidates', + value: null, + type: AlgorithmSettingType.STRING, + }, + { + name: 'random_state', + value: null, + type: AlgorithmSettingType.STRING, + }, +]; + +export const CMAESSettings: AlgorithmSetting[] = [ + { + name: 'random_state', + value: null, + type: AlgorithmSettingType.STRING, + }, + { + name: 'sigma', + value: 0.001, + type: AlgorithmSettingType.FLOAT, + }, +]; + +export const ENASSettings: AlgorithmSetting[] = [ + { + name: 'controller_hidden_size', + value: 64, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'controller_temperature', + value: 5.0, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_tanh_const', + value: 2.25, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_entropy_weight', + value: 1e-5, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_baseline_decay', + value: 0.999, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_learning_rate', + value: 5e-5, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_skip_target', + value: 0.4, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_skip_weight', + value: 0.8, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'controller_train_steps', + value: 50, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'controller_log_every_steps', + value: 10, + type: AlgorithmSettingType.INTEGER, + }, +]; + +export const DartsSettings: AlgorithmSetting[] = [ + { + name: 'num_epochs', + value: 50, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'w_lr', + value: 0.025, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'w_lr_min', + value: 0.001, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'w_momentum', + value: 0.9, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'w_weight_decay', + value: 3e-4, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'w_grad_clip', + value: 5.0, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'alpha_lr', + value: 3e-4, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'alpha_weight_decay', + value: 1e-3, + type: AlgorithmSettingType.FLOAT, + }, + { + name: 'batch_size', + value: 128, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'num_workers', + value: 4, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'init_channels', + value: 16, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'print_step', + value: 50, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'num_nodes', + value: 4, + type: AlgorithmSettingType.INTEGER, + }, + { + name: 'stem_multiplier', + value: 3, + type: AlgorithmSettingType.INTEGER, + }, +]; + +export const AlgorithmSettingsMap: { [key: string]: AlgorithmSetting[] } = { + [AlgorithmsEnum.GRID]: GridSettings, + [AlgorithmsEnum.RANDOM]: RandomSearchSettings, + [AlgorithmsEnum.HYPERBAND]: HyperbandSettings, + [AlgorithmsEnum.BAYESIAN_OPTIMIZATION]: BayesianOptimizationSettings, + [AlgorithmsEnum.TPE]: TPESettings, + [AlgorithmsEnum.CMAES]: CMAESSettings, + [AlgorithmsEnum.ENAS]: ENASSettings, + [AlgorithmsEnum.DARTS]: DartsSettings, +}; diff --git a/pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-types.const.ts b/pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-types.const.ts new file mode 100644 index 00000000000..7ebe11f58c8 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/constants/algorithms-types.const.ts @@ -0,0 +1,15 @@ +import { AlgorithmsEnum } from '../enumerations/algorithms.enum'; + +export const AlgorithmNames = { + [AlgorithmsEnum.GRID]: 'Grid', + [AlgorithmsEnum.RANDOM]: 'Random', + [AlgorithmsEnum.HYPERBAND]: 'Hyperband', + [AlgorithmsEnum.BAYESIAN_OPTIMIZATION]: 'Bayesian Optimization', + [AlgorithmsEnum.TPE]: 'Tree of Parzen Estimators', + [AlgorithmsEnum.CMAES]: 'Covariance Matrix Adaptation: Evolution Strategy', +}; + +export const NasAlgorithmNames = { + [AlgorithmsEnum.ENAS]: 'Efficient Neural Architecture Search', + [AlgorithmsEnum.DARTS]: 'Differentiable Architecture Search', +}; diff --git a/pkg/new-ui/v1beta1/frontend/src/app/constants/objective-types.const.ts b/pkg/new-ui/v1beta1/frontend/src/app/constants/objective-types.const.ts new file mode 100644 index 00000000000..8cc23370fd7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/constants/objective-types.const.ts @@ -0,0 +1,6 @@ +import { ObjectiveTypeEnum } from '../enumerations/objective-type.enum'; + +export const ObjectiveTypes = { + [ObjectiveTypeEnum.minimize]: 'Minimize', + [ObjectiveTypeEnum.maximize]: 'Maximize', +}; diff --git a/pkg/new-ui/v1beta1/frontend/src/app/enumerations/algorithms.enum.ts b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/algorithms.enum.ts new file mode 100644 index 00000000000..ca98ef3b52e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/algorithms.enum.ts @@ -0,0 +1,16 @@ +export enum AlgorithmsEnum { + GRID = 'grid', + RANDOM = 'random', + HYPERBAND = 'hyperband', + BAYESIAN_OPTIMIZATION = 'bayesianoptimization', + TPE = 'tpe', + CMAES = 'cmaes', + ENAS = 'enas', + DARTS = 'darts', +} + +export enum AlgorithmSettingType { + STRING = 'string', + INTEGER = 'integer', + FLOAT = 'float', +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/enumerations/metrics-collector.ts b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/metrics-collector.ts new file mode 100644 index 00000000000..06e54d3a712 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/metrics-collector.ts @@ -0,0 +1,8 @@ +export enum CollectorKind { + STDOUT = 'StdOut', + FILE = 'File', + TFEVENT = 'TensorFlowEvent', + PROMETHEUS = 'PrometheusMetric', + CUSTOM = 'Custom', + NONE = 'None', +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/enumerations/objective-type.enum.ts b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/objective-type.enum.ts new file mode 100644 index 00000000000..ed7d9ef2b6e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/objective-type.enum.ts @@ -0,0 +1,4 @@ +export enum ObjectiveTypeEnum { + minimize = 'minimize', + maximize = 'maximize', +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/enumerations/status.enum.ts b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/status.enum.ts new file mode 100644 index 00000000000..d66796769ca --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/enumerations/status.enum.ts @@ -0,0 +1,7 @@ +export enum StatusEnum { + SUCCEEDED = 'Succeeded', + RUNNING = 'Running', + FAILED = 'Failed', + RESTARTING = 'Restarting', + CREATED = 'Created', +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/models/experiment.k8s.model.ts b/pkg/new-ui/v1beta1/frontend/src/app/models/experiment.k8s.model.ts new file mode 100644 index 00000000000..0a9b808fb56 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/models/experiment.k8s.model.ts @@ -0,0 +1,186 @@ +import { K8sObject } from 'kubeflow'; +import { V1Container } from '@kubernetes/client-node'; + +/* + * K8s object definitions + */ +export const EXPERIMENT_KIND = 'Experiment'; +export const EXPERIMENT_APIVERSION = 'kubeflow.org/v1beta1'; + +export interface ExperimentK8s extends K8sObject { + spec?: ExperimentSpec; + status?: ExperimentStatus; +} + +export interface ExperimentSpec { + parallelTrialCount?: number; + maxTrialCount?: number; + maxFailedTrialCount?: number; + objective?: ObjectiveSpec; + algorithm?: AlgorithmSpec; + parameters?: ParameterSpec[]; + metricsCollectorSpec?: MetricsCollectorSpec; + trialTemplate?: TrialTemplateSpec; + nasConfig?: NasConfig; +} + +export interface ObjectiveSpec { + type: ObjectiveType; + goal: number; + objectiveMetricName: string; + additionalMetricNames: string[]; + metricStrategies: MetricStrategy[]; +} + +export type ObjectiveType = 'maximize' | 'minimize'; + +export interface AlgorithmSpec { + algorithmName: string; + algorithmSettings: AlgorithmSetting[]; +} + +export interface AlgorithmSetting { + name: string; + value: string; +} + +export interface MetricStrategy { + name: string; + value: string; +} +export interface FeasibleSpaceMinMax { + max: string; + min: string; + step: string; +} + +export interface FeasibleSpaceList { + list: string[]; +} + +export interface ParameterSpec { + name: string; + parameterType: ParameterType; + feasibleSpace: FeasibleSpace; +} + +export type FeasibleSpace = FeasibleSpaceMinMax | FeasibleSpaceList; + +export type ParameterType = 'int' | 'double' | 'discrete' | 'categorical'; + +export interface NasConfig { + graphConfig?: GraphConfig; + operations?: NasOperation[]; +} + +export interface GraphConfig { + numLayers?: number; + inputSizes?: number[]; + outputSizes?: number[]; +} + +export interface NasOperation { + operationType: string; + parameters: ParameterSpec[]; +} + +export interface MetricsCollectorSpec { + source?: SourceSpec; + collector?: CollectorSpec; +} + +export interface SourceSpec { + httpGet?: HttpGet; + fileSystemPath?: FileSystemPath; + filter?: FilterSpec; +} + +export interface CollectorSpec { + kind: CollectorKind; + customCollector: V1Container; +} + +export type CollectorKind = + | 'StdOut' + | 'File' + | 'TensorFlowEvent' + | 'PrometheusMetric' + | 'Custom' + | 'None'; + +export interface HttpGet { + host?: string; + port?: number; + path?: string; + scheme?: string; + httpHeaders?: HttpHeader[]; +} + +export interface HttpHeader { + name: string; + value: string; +} + +export interface FileSystemPath { + path: string; + kind: FileSystemKind; +} + +export type FileSystemKind = 'Directory' | 'File'; + +export interface FilterSpec { + metricsFormat?: string[]; +} + +export interface TrialTemplateSpec { + retain: boolean; + trialSource: K8sObject; + trialParameters: TrialParameter[]; + primaryPodLabels: { [key: string]: string }; + primaryContainerName: string; + successCondition: string; + failureCondition: string; +} + +export interface TrialParameter { + name: string; + description: string; + reference: string; +} + +/* + * status + */ +interface ExperimentStatusCondition { + type: string; + status: boolean; + reason: string; + message: string; + lastUpdateTime: string; + lastTransitionTime: string; +} + +interface CurrentOptimalTrial { + bestTrialName: string; + parameterAssignments: { name: string; value: number }[]; + observation: { + metrics: { + name: string; + latest: number; + min: number; + max: string; + }[]; + }; +} + +interface ExperimentStatus { + startTime: string; + completionTime: string; + conditions: ExperimentStatusCondition[]; + currentOptimalTrial: CurrentOptimalTrial; + succeededTrialList: string[]; + runningTrialList: string[]; + failedTrialList: string[]; + trials: number; + trialsSucceeded: number; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/models/experiment.model.ts b/pkg/new-ui/v1beta1/frontend/src/app/models/experiment.model.ts new file mode 100644 index 00000000000..a49fffbd977 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/models/experiment.model.ts @@ -0,0 +1,49 @@ +/* + * UI relative types + */ +interface ExperimentCondition { + type: string; + status: string; + reason: string; + message: string; + lastUpdateTime: string; + lastTransitionTime: string; +} + +export interface TrialParameterAssignments { + name: string; + value: string; +} + +export interface TrialObservationMetrics { + name: string; + min: number; + max: number; + latest: number; +} + +interface ExperimentCurrentOptimalTrial { + bestTrialName: string; + parameterAssignments: TrialParameterAssignments[]; + observation: { + metrics: TrialObservationMetrics[]; + }; +} + +export interface Experiment { + name: string; + namespace: string; + status: string; + startTime: string; + conditions: ExperimentCondition[]; + currentOptimalTrial: ExperimentCurrentOptimalTrial; + runningTrialList: string[]; + failedTrialList: string[]; + succeededTrialList: string[]; + trials: number; + trialsSucceeded: number; + trialsFailed: number; + trialsRunning: number; +} + +export type Experiments = Experiment[]; diff --git a/pkg/new-ui/v1beta1/frontend/src/app/models/trial-templates.model.ts b/pkg/new-ui/v1beta1/frontend/src/app/models/trial-templates.model.ts new file mode 100644 index 00000000000..f250656685d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/models/trial-templates.model.ts @@ -0,0 +1,18 @@ +export interface ConfigMapTemplateResponse { + Path: 'string'; + Yaml: 'string'; +} + +export interface ConfigMapBody { + ConfigMapName: string; + Templates: ConfigMapTemplateResponse[]; +} + +export interface ConfigMapResponse { + ConfigMapNamespace: string; + ConfigMaps: ConfigMapBody[]; +} + +export interface TrialTemplateResponse { + Data: ConfigMapResponse[]; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.html new file mode 100644 index 00000000000..edbbc1c89ba --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.html @@ -0,0 +1,89 @@ +
+
+
+
+ + Hyper Parameter tuning + + Neural Architecture Search + + + + + Name + + {{ algo.value }} + + +
+
+ + + +
+
+ + {{ setting.get('name').value }} + + + + + + + + + + + + + + + + {{ param_value }} + + + + +
+
+
+
+ + +
+ + The Search Algorithm is responsible for navigating throught the + optimization search space and creating Trial CRs for each step. + The algorithms' code will be executed from the underlying + Suggestion CR. + You can read the + official docs + for a full list of supported algorithms and their configurations. + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.scss new file mode 100644 index 00000000000..698e0fb0872 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.scss @@ -0,0 +1,6 @@ +.algo-options-group { + * { + margin-bottom: 12px; + margin-right: 8px; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.spec.ts new file mode 100644 index 00000000000..d82aefd9b6a --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AlgorithmComponent } from './algorithm.component'; + +describe('AlgorithmComponent', () => { + let component: AlgorithmComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AlgorithmComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AlgorithmComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.ts new file mode 100644 index 00000000000..4366234b4d7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.component.ts @@ -0,0 +1,86 @@ +import { Component, OnInit, Input, OnDestroy } from '@angular/core'; +import { + AlgorithmNames, + NasAlgorithmNames, +} from 'src/app/constants/algorithms-types.const'; +import { FormArray, FormGroup, FormControl, Validators } from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { AlgorithmSettingsMap } from 'src/app/constants/algorithms-settings.const'; +import { + AlgorithmsEnum, + AlgorithmSettingType, +} from 'src/app/enumerations/algorithms.enum'; + +@Component({ + selector: 'app-form-algorithm', + templateUrl: './algorithm.component.html', + styleUrls: ['./algorithm.component.scss'], +}) +export class FormAlgorithmComponent implements OnInit, OnDestroy { + algorithmSettings: FormArray; + algorithms: { [key: string]: string } = AlgorithmNames; + disableAddButton: boolean; + algorithmHasSettings = false; + + private subscriptions: Subscription = new Subscription(); + + @Input() + algorithmForm: FormGroup; + + ngOnInit(): void { + this.algorithmSettings = this.algorithmForm.get( + 'algorithmSettings', + ) as FormArray; + + this.subscriptions.add( + this.algorithmForm.get('algorithm').valueChanges.subscribe(algo => { + this.algorithmSettings.clear(); + this.algorithmHasSettings = AlgorithmSettingsMap[algo].length !== 0; + + // create the settings + for (const setting of AlgorithmSettingsMap[algo]) { + this.addSetting( + setting.name, + setting.value, + setting.type, + setting.values, + ); + } + }), + ); + + this.subscriptions.add( + this.algorithmForm.get('type').valueChanges.subscribe(type => { + if (type === 'nas') { + this.algorithms = NasAlgorithmNames; + this.algorithmForm.get('algorithm').setValue(AlgorithmsEnum.ENAS); + return; + } + + this.algorithms = AlgorithmNames; + this.algorithmForm.get('algorithm').setValue(AlgorithmsEnum.RANDOM); + }), + ); + } + + // form helpers + addSetting( + name: string, + value: any, + type: AlgorithmSettingType, + values: any[], + ) { + this.algorithmSettings.push( + new FormGroup({ + name: new FormControl(name, Validators.required), + value: new FormControl(value, []), + type: new FormControl(type, Validators.required), + values: new FormControl(values, Validators.required), + }), + ); + } + + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.module.ts new file mode 100644 index 00000000000..a4a1de6b653 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/algorithm/algorithm.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormAlgorithmComponent } from './algorithm.component'; +import { FormModule } from 'kubeflow'; + +import { MatIconModule } from '@angular/material/icon'; +import { MatRadioModule } from '@angular/material/radio'; + +@NgModule({ + declarations: [FormAlgorithmComponent], + imports: [CommonModule, FormModule, MatIconModule, MatRadioModule], + exports: [FormAlgorithmComponent], +}) +export class FormAlgorithmModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.html new file mode 100644 index 00000000000..b42199109fe --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.html @@ -0,0 +1,112 @@ +
+
+ + + +
+ + + +
+ Metadata + + + +
+
+ + +
+ Trial Thresholds + + +
+
+ + +
+ Objective + + +
+
+ + +
+ Search Algorithm + + +
+
+ + + + +
+ Hyper Parameters + + + +
+
+
+ + + + + Neural Architecture Graph + + + + + + + Neural Architecture Operations + + + + + + + + + +
+ Metrics Collector + + + +
+
+ + + +
+ Trial Template + + +
+
+
+
+ + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.scss new file mode 100644 index 00000000000..5e0d2b18c63 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.scss @@ -0,0 +1,21 @@ +.form-body { + max-width: 600px; +} + +.step-info { + display: flex; + flex-direction: column; +} + +.step-wrapper { + display: flex; + + > .step-info-wrapper { + margin-left: auto; + width: 30%; + } + + > .step-content { + max-width: 600px; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.spec.ts new file mode 100644 index 00000000000..811478b0b9f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentCreationComponent } from './experiment-creation.component'; + +describe('ExperimentCreationComponent', () => { + let component: ExperimentCreationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentCreationComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentCreationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.ts new file mode 100644 index 00000000000..890529bda4d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.component.ts @@ -0,0 +1,149 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormArray, FormControl } from '@angular/forms'; +import { Router } from '@angular/router'; + +import { MatDialog } from '@angular/material/dialog'; + +import { ExperimentFormService } from '../../services/experiment-form.service'; +import { YamlModalComponent } from './yaml-modal/yaml-modal.component'; +import { + ExperimentK8s, + ExperimentSpec, + FeasibleSpaceMinMax, + EXPERIMENT_APIVERSION, + EXPERIMENT_KIND, +} from 'src/app/models/experiment.k8s.model'; +import { KWABackendService } from 'src/app/services/backend.service'; +import { NamespaceService, SnackType, SnackBarService } from 'kubeflow'; +import { pipe } from 'rxjs'; +import { take } from 'rxjs/operators'; + +@Component({ + selector: 'app-experiment-creation', + templateUrl: './experiment-creation.component.html', + styleUrls: ['./experiment-creation.component.scss'], +}) +export class ExperimentCreationComponent implements OnInit { + metadataForm: FormGroup; + trialThresholdsForm: FormGroup; + objectiveForm: FormGroup; + algorithmForm: FormGroup; + hyperParamsArray: FormArray; + nasGraphForm: FormGroup; + nasOperationsForm: FormArray; + metricsForm: FormGroup; + trialTemplateForm: FormGroup; + + constructor( + private formSvc: ExperimentFormService, + private router: Router, + private dialog: MatDialog, + private backend: KWABackendService, + private ns: NamespaceService, + private snack: SnackBarService, + ) {} + + ngOnInit() { + this.metadataForm = this.formSvc.createMetadataForm('kubeflow-user'); + this.trialThresholdsForm = this.formSvc.createTrialThresholdForm(); + this.objectiveForm = this.formSvc.createObjectiveForm(); + this.algorithmForm = this.formSvc.createAlgorithmObjectiveForm(); + this.hyperParamsArray = this.formSvc.createHyperParametersForm(); + this.nasGraphForm = this.formSvc.createNasGraphForm(); + this.nasOperationsForm = this.formSvc.createNasOperationsForm(); + this.metricsForm = this.formSvc.createMetricsForm(); + this.trialTemplateForm = this.formSvc.createTrialTemplateForm(); + } + + /** + * Create an Experiment CR json obj from the form's values + */ + getFormYaml(): any { + const metadata = { name: '', namespace: '' }; + const spec: ExperimentSpec = {}; + + metadata.name = this.metadataForm.value.name; + + const thresholds = this.trialThresholdsForm.value; + spec.maxTrialCount = thresholds.maxTrialCount; + spec.parallelTrialCount = thresholds.parallelTrialCount; + spec.maxFailedTrialCount = thresholds.maxFailedTrialCount; + + spec.objective = this.formSvc.objectiveFromCtrl(this.objectiveForm); + spec.algorithm = this.formSvc.algorithmFromCtrl(this.algorithmForm); + + const algoType = this.algorithmForm.value.type; + if (algoType === 'hp') { + spec.parameters = this.formSvc.hyperParamsFromCtrl(this.hyperParamsArray); + } else if (algoType === 'nas') { + spec.nasConfig = { + graphConfig: this.nasGraphForm.value, + operations: this.formSvc.nasOpsFromCtrl(this.nasOperationsForm), + }; + } + + spec.metricsCollectorSpec = this.formSvc.metricsCollectorFromCtrl( + this.metricsForm, + ); + spec.trialTemplate = this.formSvc.trialTemplateFromCtrl( + this.trialTemplateForm, + ); + + return { + apiVersion: EXPERIMENT_APIVERSION, + kind: EXPERIMENT_KIND, + metadata, + spec, + }; + } + + private submitExperiment(exp: ExperimentK8s) { + this.ns + .getSelectedNamespace() + .pipe(take(1)) + .subscribe(ns => { + exp.metadata.namespace = ns; + console.log(exp); + + this.backend.createExperiment(exp).subscribe({ + next: () => { + this.snack.open( + 'Experiment submitted successfully.', + SnackType.Success, + 3000, + ); + this.returnToExperiments(); + }, + error: err => { + console.warn('could not submit experiment'); + }, + }); + }); + } + + showYAML() { + const formYaml = this.getFormYaml(); + + // show the dialog + const ref = this.dialog.open(YamlModalComponent, { + data: formYaml, + }); + + ref.afterClosed().subscribe((res: ExperimentK8s) => { + if (!res) { + return; + } + + this.submitExperiment(res); + }); + } + + create() { + const exp = this.getFormYaml(); + this.submitExperiment(exp); + } + + returnToExperiments() { + this.router.navigate(['']); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.module.ts new file mode 100644 index 00000000000..116d816018f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/experiment-creation.module.ts @@ -0,0 +1,43 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatIconModule } from '@angular/material/icon'; +import { MatStepperModule } from '@angular/material/stepper'; + +import { TitleActionsToolbarModule, FormModule } from 'kubeflow'; + +import { ExperimentCreationComponent } from './experiment-creation.component'; +import { ExperimentFormService } from '../../services/experiment-form.service'; +import { FormMetadataModule } from './metadata/metadata.module'; +import { FormTrialThresholdsModule } from './trial-thresholds/trial-thresholds.module'; +import { FormObjectiveModule } from './objective/objective.module'; +import { FormAlgorithmModule } from './algorithm/algorithm.module'; +import { FormHyperParametersModule } from './hyper-parameters/hyper-parameters.module'; +import { FormNasGraphModule } from './nas-graph/nas-graph.module'; +import { FormNasOperationsModule } from './nas-operations/nas-operations.module'; +import { FormMetricsCollectorModule } from './metrics-collector/metrics-collector.module'; +import { FormTrialTemplateModule } from './trial-template/trial-template.module'; +import { YamlModalModule } from './yaml-modal/yaml-modal.module'; + +@NgModule({ + declarations: [ExperimentCreationComponent], + imports: [ + CommonModule, + MatIconModule, + MatStepperModule, + FormModule, + TitleActionsToolbarModule, + FormMetadataModule, + FormTrialThresholdsModule, + FormObjectiveModule, + FormAlgorithmModule, + FormHyperParametersModule, + FormNasGraphModule, + FormNasOperationsModule, + FormMetricsCollectorModule, + FormTrialTemplateModule, + YamlModalModule, + ], + providers: [ExperimentFormService], + exports: [ExperimentCreationComponent], +}) +export class ExperimentCreationModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.html new file mode 100644 index 00000000000..5ebb016d962 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.html @@ -0,0 +1,16 @@ +
+ There are {{ combinations }} different combinations from these Hyper + Parameters. +
+ +
+ + + + The Hyper Parameters will be used to construct the optimization + search space. Katib will be generating Trials to test different + combinations of these parameters in order to find the optimal set. + +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.spec.ts new file mode 100644 index 00000000000..634721bcb6f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HyperParametersComponent } from './hyper-parameters.component'; + +describe('HyperParametersComponent', () => { + let component: HyperParametersComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [HyperParametersComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HyperParametersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.ts new file mode 100644 index 00000000000..26fd552ae40 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.component.ts @@ -0,0 +1,67 @@ +import { Component, Input } from '@angular/core'; +import { FormArray } from '@angular/forms'; +import { + ParameterSpec, + FeasibleSpaceList, + FeasibleSpaceMinMax, +} from 'src/app/models/experiment.k8s.model'; + +@Component({ + selector: 'app-form-hyper-parameters', + templateUrl: './hyper-parameters.component.html', + styleUrls: ['./hyper-parameters.component.scss'], +}) +export class FormHyperParametersComponent { + @Input() hyperParamsArray: FormArray; + + constructor() {} + + get combinations() { + const params = this.hyperParamsArray.value as ParameterSpec[]; + if (!params.length) { + return 0; + } + + let confs = 1; + let currentConfs = 0; + for (const param of params) { + if ( + param.parameterType === 'discrete' || + param.parameterType === 'categorical' + ) { + currentConfs = (param.feasibleSpace as FeasibleSpaceList).list.length; + } else { + const fs = param.feasibleSpace as FeasibleSpaceMinMax; + + const min = fs.min; + const max = fs.max; + const step = fs.step; + + // don't calculate the combinations is step is omitted + if (step === '') { + return null; + } + + try { + parseFloat(min); + parseFloat(max); + parseFloat(step); + } catch (e) { + console.log('Could not convert min/max/step to number'); + return null; + } + + currentConfs = + Math.ceil((parseFloat(max) - parseFloat(min)) / parseFloat(step)) + 1; + } + + if (currentConfs === 0) { + continue; + } + + confs *= currentConfs; + } + + return confs; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.module.ts new file mode 100644 index 00000000000..9775e18303b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/hyper-parameters/hyper-parameters.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { MatIconModule } from '@angular/material/icon'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatRadioModule } from '@angular/material/radio'; + +import { FormModule, PopoverModule, DetailsListModule } from 'kubeflow'; + +import { FormHyperParametersComponent } from './hyper-parameters.component'; +import { ParamsListModule } from 'src/app/shared/params-list/params-list.module'; + +@NgModule({ + declarations: [FormHyperParametersComponent], + imports: [ + CommonModule, + FormModule, + MatIconModule, + MatDividerModule, + MatRadioModule, + PopoverModule, + DetailsListModule, + ParamsListModule, + ], + exports: [FormHyperParametersComponent], +}) +export class FormHyperParametersModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.html new file mode 100644 index 00000000000..3c89fbb5e01 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.html @@ -0,0 +1,20 @@ +
+
+ + Name + + + + + Namespace + + +
+ +
+ + The name of the Experiment CR that will be submitted in the Kubernetes + Cluster. It must be DNS1123 compliant. + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.spec.ts new file mode 100644 index 00000000000..965c2331d85 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MetadataComponent } from './metadata.component'; + +describe('MetadataComponent', () => { + let component: MetadataComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MetadataComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MetadataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.ts new file mode 100644 index 00000000000..9787dd86684 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.component.ts @@ -0,0 +1,31 @@ +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { NamespaceService } from 'kubeflow'; +import { Subscription } from 'rxjs'; + +@Component({ + selector: 'app-form-metadata', + templateUrl: './metadata.component.html', + styleUrls: ['./metadata.component.scss'], +}) +export class FormMetadataComponent implements OnInit, OnDestroy { + @Input() + metadataForm: FormGroup; + sub: Subscription; + + constructor(private ns: NamespaceService) {} + + ngOnInit() { + this.metadataForm.get('namespace').disable(); + + this.sub = this.ns.getSelectedNamespace().subscribe(namespace => { + this.metadataForm.get('namespace').enable(); + this.metadataForm.get('namespace').setValue(namespace); + this.metadataForm.get('namespace').disable(); + }); + } + + ngOnDestroy() { + this.sub.unsubscribe(); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.module.ts new file mode 100644 index 00000000000..fcd4720a262 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metadata/metadata.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormMetadataComponent } from './metadata.component'; +import { FormModule } from 'kubeflow'; + +@NgModule({ + declarations: [FormMetadataComponent], + imports: [CommonModule, FormModule], + exports: [FormMetadataComponent], +}) +export class FormMetadataModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.html new file mode 100644 index 00000000000..f1e063fac03 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.html @@ -0,0 +1,133 @@ +
+
+
+ + Kind + + Stdout + File + TensorFlow Event + Prometheus + Custom + None + + + +
+
+ + + + + Metrics file + + + + + + + TensorFlow Events Directory + + + + + +
+ + HTTP Port + + + + + HTTP Path + + +
+ +
+ + Scheme + + HTTP + HTTPS + + + + + Host Name (optional) + + +
+ + +
+ + +
+
+
+
+ +
+ + Define how Katib should collect the metrics from each trial, such as the + accuracy and loss metrics. + + + + + + Katib collects the metrics from an arbitrary file, where the Training + Container should log them. You can also check an + example YAML . + + + + + + Katib collects the metrics from the operating system’s default output + location (standard output). + + + + + + Katib collects the metrics from a directory path containing a + tf.Event. You can also check the + TFJob example. + + + + + + You must define your own container that will be responsible for + gathering the metrics. You can also check the + custom metrics collector example. + + + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.scss new file mode 100644 index 00000000000..e5c4a2c9962 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.scss @@ -0,0 +1,4 @@ +.ace-editor { + width: 375px; + height: 300px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.spec.ts new file mode 100644 index 00000000000..c71e6410fc1 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MetricsCollectorComponent } from './metrics-collector.component'; + +describe('MetricsCollectorComponent', () => { + let component: MetricsCollectorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MetricsCollectorComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MetricsCollectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.ts new file mode 100644 index 00000000000..e8cf704cda6 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.component.ts @@ -0,0 +1,19 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { CollectorKind } from 'src/app/enumerations/metrics-collector'; + +@Component({ + selector: 'app-form-metrics-collector', + templateUrl: './metrics-collector.component.html', + styleUrls: ['./metrics-collector.component.scss'], +}) +export class FormMetricsCollectorComponent implements OnInit { + @Input() formGroup: FormGroup; + kind = CollectorKind; + customYaml = + 'name: metrics-collector\nimage: \nresources: {}'; + + constructor() {} + + ngOnInit() {} +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.module.ts new file mode 100644 index 00000000000..75a0edf26f7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/metrics-collector/metrics-collector.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormMetricsCollectorComponent } from './metrics-collector.component'; +import { FormModule } from 'kubeflow'; +import { ListKeyValueModule } from 'src/app/shared/list-key-value/list-key-value.module'; +import { AceEditorModule } from 'ng2-ace-editor'; + +@NgModule({ + declarations: [FormMetricsCollectorComponent], + imports: [CommonModule, FormModule, ListKeyValueModule, AceEditorModule], + exports: [FormMetricsCollectorComponent], +}) +export class FormMetricsCollectorModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.html new file mode 100644 index 00000000000..a021c787747 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.html @@ -0,0 +1,32 @@ +
+
+ + Number of Layers + + + + + + +
+ + +
+ + The graph config that defines structure for a directed acyclic graph of + the neural network. You can also check the low-level Golang API + here. + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.spec.ts new file mode 100644 index 00000000000..c55999338e2 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NasGraphComponent } from './nas-graph.component'; + +describe('NasGraphComponent', () => { + let component: NasGraphComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [NasGraphComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NasGraphComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.ts new file mode 100644 index 00000000000..1683671546e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-form-nas-graph', + templateUrl: './nas-graph.component.html', + styleUrls: ['./nas-graph.component.scss'], +}) +export class FormNasGraphComponent implements OnInit { + @Input() formGroup: FormGroup; + + constructor() {} + + ngOnInit() {} +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.module.ts new file mode 100644 index 00000000000..0c75e90b0f8 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-graph/nas-graph.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormNasGraphComponent } from './nas-graph.component'; +import { FormModule } from 'kubeflow'; +import { ListInputModule } from 'src/app/shared/list-input/list-input.module'; + +@NgModule({ + declarations: [FormNasGraphComponent], + imports: [CommonModule, FormModule, ListInputModule], + exports: [FormNasGraphComponent], +}) +export class FormNasGraphModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.html new file mode 100644 index 00000000000..388041220d9 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.html @@ -0,0 +1,35 @@ +
+
+ + +
+ + + + + +
+
+ + +
+ + The graph config that defines structure for a directed acyclic graph of + the neural network. You can also check the low-level Golang API + here. + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.scss new file mode 100644 index 00000000000..9fe982e9e49 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.scss @@ -0,0 +1,9 @@ +.add-btn { + margin-bottom: 12px; +} + +.divider { + min-width: 400px; + margin-top: 16px; + margin-bottom: 24px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.spec.ts new file mode 100644 index 00000000000..dc22ef8fea6 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NasOperationsComponent } from './nas-operations.component'; + +describe('NasOperationsComponent', () => { + let component: NasOperationsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [NasOperationsComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NasOperationsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.ts new file mode 100644 index 00000000000..f81aa559904 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { FormArray } from '@angular/forms'; +import { createNasOperationGroup } from 'src/app/shared/utils'; + +@Component({ + selector: 'app-form-nas-operations', + templateUrl: './nas-operations.component.html', + styleUrls: ['./nas-operations.component.scss'], +}) +export class FormNasOperationsComponent { + @Input() formArray: FormArray; + + constructor() {} + + addCtrl() { + this.formArray.push( + createNasOperationGroup({ + operationType: '', + parameters: [], + }), + ); + } + + removeCtrl(i) { + this.formArray.removeAt(i); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.module.ts new file mode 100644 index 00000000000..eacd94adbb7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/nas-operations.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormNasOperationsComponent } from './nas-operations.component'; +import { OperationComponent } from './operation/operation.component'; +import { FormModule } from 'kubeflow'; +import { ParamsListModule } from 'src/app/shared/params-list/params-list.module'; +import { MatDividerModule } from '@angular/material/divider'; + +@NgModule({ + declarations: [FormNasOperationsComponent, OperationComponent], + imports: [CommonModule, FormModule, ParamsListModule, MatDividerModule], + exports: [FormNasOperationsComponent], +}) +export class FormNasOperationsModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.html new file mode 100644 index 00000000000..905b9833018 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.html @@ -0,0 +1,19 @@ +
+ + Operation Type + + + + + +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.spec.ts new file mode 100644 index 00000000000..c74729dc3bc --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OperationComponent } from './operation.component'; + +describe('OperationComponent', () => { + let component: OperationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [OperationComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(OperationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.ts new file mode 100644 index 00000000000..345b3f1d864 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/nas-operations/operation/operation.component.ts @@ -0,0 +1,13 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { EventsV1beta1Api } from '@kubernetes/client-node'; + +@Component({ + selector: 'app-nas-operation', + templateUrl: './operation.component.html', + styleUrls: ['./operation.component.scss'], +}) +export class OperationComponent { + @Input() formGroup: FormGroup; + @Output() removeCtrl = new EventEmitter(); +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.html new file mode 100644 index 00000000000..ac92988b94d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.html @@ -0,0 +1,78 @@ +
+
+ + Type + + {{ type.value }} + + + +
+ + Metric + + + + + Goal + + +
+ + + + + Set metric strategies + + + + + + Strategy for: {{ objectiveForm.get('metricName').value }} + + + Max + Min + Latest + + + + + + Strategy for: {{ strategy.value.metric }} + + Max + Min + Latest + + + + +
+ + +
+ + Katib will use the Objective's metric to monitor the performance + of the model. It will create Trials until either the goal is reached or + the Controller has run the maximum defined number of Trials. + + + + You can run the Experiment without specifying the goal. In that + case, Katib runs the Experiment until the corresponding successful Trials + reach maximum number of allowed Trials. This number is defined in the the + Trial Thresholds section above. + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.spec.ts new file mode 100644 index 00000000000..da318b6d0cb --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ObjectiveComponent } from './objective.component'; + +describe('ObjectiveComponent', () => { + let component: ObjectiveComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ObjectiveComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ObjectiveComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.ts new file mode 100644 index 00000000000..4308e975564 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.component.ts @@ -0,0 +1,75 @@ +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { FormArray, FormGroup, FormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ObjectiveTypes } from 'src/app/constants/objective-types.const'; +import { Subscription } from 'rxjs'; +import { MetricStrategy } from 'src/app/models/experiment.k8s.model'; + +@Component({ + selector: 'app-form-objective', + templateUrl: './objective.component.html', + styleUrls: ['./objective.component.scss'], +}) +export class FormObjectiveComponent implements OnInit, OnDestroy { + @Input() + objectiveForm: FormGroup; + strategiesArray: FormArray; + objectiveTypes = ObjectiveTypes; + subs = new Subscription(); + + get objectiveStrategy() { + return this.objectiveForm.get('type').value.substring(0, 3); + } + + constructor(public dialog: MatDialog) {} + + ngOnInit(): void { + this.strategiesArray = this.objectiveForm.get( + 'metricStrategies', + ) as FormArray; + + this.subs.add( + this.objectiveForm.get('type').valueChanges.subscribe(type => { + this.objectiveForm + .get('metricStrategy') + .setValue(this.objectiveStrategy); + + for (const strategy of this.strategiesArray.controls) { + strategy.get('strategy').setValue(this.objectiveStrategy); + } + }), + ); + + this.subs.add( + this.objectiveForm + .get('additionalMetricNames') + .valueChanges.subscribe(metrics => this.setStrategies(metrics)), + ); + + this.setStrategies(this.objectiveForm.get('additionalMetricNames').value); + } + + ngOnDestroy(): void { + this.subs.unsubscribe(); + } + + setStrategies(metrics: MetricStrategy[]) { + this.strategiesArray.clear(); + + for (const additionalMetric of metrics) { + if (!additionalMetric) { + continue; + } + + this.strategiesArray.push( + new FormGroup( + { + metric: new FormControl(additionalMetric), + strategy: new FormControl(this.objectiveStrategy), + }, + [], + ), + ); + } + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.module.ts new file mode 100644 index 00000000000..bdf48befe80 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/objective/objective.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormObjectiveComponent } from './objective.component'; +import { FormModule } from 'kubeflow'; +import { MatIconModule } from '@angular/material/icon'; +import { ListInputModule } from 'src/app/shared/list-input/list-input.module'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +@NgModule({ + declarations: [FormObjectiveComponent], + imports: [ + CommonModule, + FormModule, + MatIconModule, + ListInputModule, + MatDividerModule, + MatCheckboxModule, + ], + exports: [FormObjectiveComponent], + entryComponents: [], +}) +export class FormObjectiveModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.html new file mode 100644 index 00000000000..e4cca54a4bc --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.html @@ -0,0 +1,95 @@ +
+
+ + Primary Container Name + + + + + + + + Success Condition + + + + + Failure Condition + + + + + Retain + + False + True + + + + + + + + Source Type + + ConfigMap + YAML + + + + + + ConfigMap Namespace + + {{ tpl.ConfigMapNamespace }} + + + +
+ + ConfigMap Name + + + {{ cm.ConfigMapName }} + + + + + + ConfigMap Path + + + {{ p }} + + + +
+
+ +
+
+ +
+ + You can either use a predefined YAML from a ConfigMap or write your own. + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.scss new file mode 100644 index 00000000000..9a1dd9c96a0 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.scss @@ -0,0 +1,7 @@ +.margin-bottom { + margin-bottom: 12px; +} + +[ace-editor] { + height: 350px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.spec.ts new file mode 100644 index 00000000000..13fd38e69e0 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TrialTemplateComponent } from './trial-template.component'; + +describe('TrialTemplateComponent', () => { + let component: TrialTemplateComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TrialTemplateComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TrialTemplateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.ts new file mode 100644 index 00000000000..238b3e3ca36 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.component.ts @@ -0,0 +1,98 @@ +import { Component, OnInit, Input, OnDestroy } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { KWABackendService } from 'src/app/services/backend.service'; +import { + TrialTemplateResponse, + ConfigMapResponse, + ConfigMapBody, +} from 'src/app/models/trial-templates.model'; +import { temporaryDeclaration } from '@angular/compiler/src/compiler_util/expression_converter'; +import { Subscription } from 'rxjs'; + +@Component({ + selector: 'app-form-trial-template', + templateUrl: './trial-template.component.html', + styleUrls: ['./trial-template.component.scss'], +}) +export class FormTrialTemplateComponent implements OnInit, OnDestroy { + public templates: ConfigMapResponse[] = []; + public configmaps: ConfigMapBody[] = []; + public paths: string[] = []; + private selectedConfigMap: ConfigMapBody; + private subs = new Subscription(); + private yamlPrv = ''; + + @Input() formGroup: FormGroup; + + get yaml(): string { + return this.yamlPrv; + } + set yaml(str: string) { + this.formGroup.get('yaml').setValue(str); + this.yamlPrv = str; + } + + constructor(private backend: KWABackendService) {} + + ngOnInit() { + this.subs.add( + this.formGroup.get('type').valueChanges.subscribe(tp => { + if (tp === 'yaml') { + this.yaml = ''; + return; + } + + if (this.templates.length && this.templates.length) { + this.formGroup.get('cmNamespace').setValue('kubeflow'); + } + }), + ); + + this.subs.add( + this.backend.getTrialTemplates('').subscribe(templates => { + this.templates = templates.Data; + this.formGroup.get('cmNamespace').setValue('kubeflow'); + }), + ); + + this.subs.add( + this.formGroup.get('cmNamespace').valueChanges.subscribe(ns => { + const ts = this.templates.filter(t => t.ConfigMapNamespace === ns); + this.configmaps = ts.map(t => t.ConfigMaps)[0] || []; + + if (this.configmaps.length > 0) { + this.formGroup + .get('cmName') + .setValue(this.configmaps[0].ConfigMapName); + } + }), + ); + + this.subs.add( + this.formGroup.get('cmName').valueChanges.subscribe(nm => { + const cm = this.configmaps.filter(c => c.ConfigMapName === nm)[0]; + this.paths = cm.Templates.map(t => t.Path); + this.selectedConfigMap = cm; + + if (this.paths.length > 0) { + this.formGroup.get('cmTrialPath').setValue(this.paths[0]); + } + }), + ); + + this.subs.add( + this.formGroup.get('cmTrialPath').valueChanges.subscribe(path => { + const t = this.selectedConfigMap.Templates.filter( + tpl => tpl.Path === path, + )[0]; + + this.formGroup.get('yaml').setValue(t.Yaml); + this.yaml = t.Yaml; + }), + ); + } + + ngOnDestroy() { + this.subs.unsubscribe(); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.module.ts new file mode 100644 index 00000000000..5556fc2a46e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-template/trial-template.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatDividerModule } from '@angular/material/divider'; + +import { FormTrialTemplateComponent } from './trial-template.component'; +import { FormModule } from 'kubeflow'; +import { ListKeyValueModule } from 'src/app/shared/list-key-value/list-key-value.module'; +import { AceEditorModule } from 'ng2-ace-editor'; + +@NgModule({ + declarations: [FormTrialTemplateComponent], + imports: [ + CommonModule, + FormModule, + ListKeyValueModule, + MatDividerModule, + AceEditorModule, + ], + exports: [FormTrialTemplateComponent], +}) +export class FormTrialTemplateModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.html new file mode 100644 index 00000000000..d48ba21d72b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.html @@ -0,0 +1,44 @@ +
+
+
+ + Parallel Trials + + + + + Max Trials + + + + + Max failed Trials + + +
+ +
+ + The number of maximum Trials to run. If this value is omitted, your + Experiment will be running until the objective goal is reached or the + Experiment reaches a maximum number of failed Trials. + + + + The maximum number of failed Trials before Katib should stop the + Experiment. In case the failed Trials exceed this number then the + Experiment will be marked as Failed. + +
+
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.spec.ts new file mode 100644 index 00000000000..d55483836d5 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TrialThresholdsComponent } from './trial-thresholds.component'; + +describe('TrialThresholdsComponent', () => { + let component: TrialThresholdsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TrialThresholdsComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TrialThresholdsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.ts new file mode 100644 index 00000000000..d9a93bca43a --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.component.ts @@ -0,0 +1,12 @@ +import { Component, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-form-trial-thresholds', + templateUrl: './trial-thresholds.component.html', + styleUrls: ['./trial-thresholds.component.scss'], +}) +export class FormTrialThresholdsComponent { + @Input() + formGroup: FormGroup; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.module.ts new file mode 100644 index 00000000000..2efeaf816f6 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/trial-thresholds/trial-thresholds.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormTrialThresholdsComponent } from './trial-thresholds.component'; +import { FormModule } from 'kubeflow'; + +@NgModule({ + declarations: [FormTrialThresholdsComponent], + imports: [CommonModule, FormModule], + exports: [FormTrialThresholdsComponent], +}) +export class FormTrialThresholdsModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.html new file mode 100644 index 00000000000..90d848f19ad --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.html @@ -0,0 +1,12 @@ +

Edit YAML

+ +
+
+
+ +
+ + +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.scss new file mode 100644 index 00000000000..50034b9fb8a --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.scss @@ -0,0 +1,4 @@ +[ace-editor] { + height: 350px; + width: 700px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.spec.ts new file mode 100644 index 00000000000..7dd51549a4b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { YamlModalComponent } from './yaml-modal.component'; + +describe('YamlModalComponent', () => { + let component: YamlModalComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [YamlModalComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(YamlModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.ts new file mode 100644 index 00000000000..f4046ae9f38 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.component.ts @@ -0,0 +1,31 @@ +import { Component, Input, Inject } from '@angular/core'; +import { load, dump } from 'js-yaml'; +import { + MatDialog, + MatDialogRef, + MAT_DIALOG_DATA, +} from '@angular/material/dialog'; + +@Component({ + selector: 'app-yaml-modal', + templateUrl: './yaml-modal.component.html', + styleUrls: ['./yaml-modal.component.scss'], +}) +export class YamlModalComponent { + public yaml = ''; + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + ) { + this.yaml = dump(data); + } + + save() { + this.dialogRef.close(load(this.yaml)); + } + + close() { + this.dialogRef.close(); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.module.ts new file mode 100644 index 00000000000..24324295630 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-creation/yaml-modal/yaml-modal.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { YamlModalComponent } from './yaml-modal.component'; +import { MatDialogModule } from '@angular/material/dialog'; +import { FormModule } from 'kubeflow'; +import { AceEditorModule } from 'ng2-ace-editor'; + +@NgModule({ + declarations: [YamlModalComponent], + imports: [CommonModule, MatDialogModule, FormModule, AceEditorModule], + entryComponents: [YamlModalComponent], +}) +export class YamlModalModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.html new file mode 100644 index 00000000000..454765fbc49 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.html @@ -0,0 +1,52 @@ + + + + {{ experiment?.spec?.objective.objectiveMetricName }} + + + + {{ experiment?.spec?.objective.type }} + + + + {{ experiment?.spec?.objective.goal }} + + + + {{ objectiveAdditionalMetrics }} + + + + + + {{ experiment?.spec?.maxFailedTrialCount }} + + + + {{ experiment?.spec?.maxTrialCount }} + + + + {{ experiment?.spec?.parallelTrialCount }} + + + + + + + + + + + {{ experiment?.spec?.algorithm.algorithmName }} + + + + + + {{ experiment?.spec?.metricsCollectorSpec.collector.kind }} + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.scss new file mode 100644 index 00000000000..8aebdb13415 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.scss @@ -0,0 +1,8 @@ +:host { + display: block; + overflow: auto; +} + +lib-heading-row { + margin-top: 16px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.spec.ts new file mode 100644 index 00000000000..3a0f695b0d3 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentDetailsTabComponent } from './experiment-details-tab.component'; + +describe('ExperimentDetailsTabComponent', () => { + let component: ExperimentDetailsTabComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentDetailsTabComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentDetailsTabComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.ts new file mode 100644 index 00000000000..72a11ff1e3d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.component.ts @@ -0,0 +1,86 @@ +import { + ChangeDetectionStrategy, + Component, + Input, + OnChanges, +} from '@angular/core'; + +import { ChipDescriptor, ListEntry } from 'kubeflow'; +import { + ExperimentK8s, + ExperimentSpec, + FeasibleSpaceList, + FeasibleSpaceMinMax, +} from 'src/app/models/experiment.k8s.model'; + +@Component({ + selector: 'app-experiment-details-tab', + templateUrl: './experiment-details-tab.component.html', + styleUrls: ['./experiment-details-tab.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ExperimentDetailsTabComponent implements OnChanges { + @Input() + experiment: ExperimentK8s; + experimentAlgorithmList: ListEntry[] = []; + metricsCollectorSpecList: ListEntry[] = []; + objectiveAdditionalMetrics: string; + parameters: { chips: ChipDescriptor[]; key: string }[] = []; + + ngOnChanges(): void { + if (!this.experiment) { + return; + } + + const objective = this.experiment.spec.objective; + let metrics = 'No additional metrics'; + if ( + objective.additionalMetricNames && + objective.additionalMetricNames.length + ) { + metrics = objective.additionalMetricNames.join(', '); + } + + this.objectiveAdditionalMetrics = metrics; + this.parameters = this.generateParametersList(this.experiment.spec); + } + + generateParametersList( + spec: ExperimentSpec, + ): { chips: ChipDescriptor[]; key: string }[] { + const { parameters } = spec; + + return parameters.map(parameter => { + const feasibleSpaceList = parameter.feasibleSpace as FeasibleSpaceList; + const feasibleSpaceMinMax = parameter.feasibleSpace as FeasibleSpaceMinMax; + + const chips: ChipDescriptor[] = [ + { + value: `Parameter type: ${parameter.parameterType}`, + color: 'primary', + }, + ]; + + if (!!feasibleSpaceList.list) { + chips.push({ + value: `${feasibleSpaceList.list.join(', ')}`, + color: 'primary', + }); + } else { + chips.push({ + value: `Min: ${feasibleSpaceMinMax.min}`, + color: 'primary', + }); + chips.push({ + value: `Max: ${feasibleSpaceMinMax.max}`, + color: 'primary', + }); + } + + return { + key: parameter.name, + chips, + }; + }); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.module.ts new file mode 100644 index 00000000000..332be2da8a8 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/details/experiment-details-tab.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DetailsListModule, HeadingSubheadingRowModule } from 'kubeflow'; + +import { ExperimentDetailsTabComponent } from './experiment-details-tab.component'; + +@NgModule({ + declarations: [ExperimentDetailsTabComponent], + imports: [CommonModule, DetailsListModule, HeadingSubheadingRowModule], + exports: [ExperimentDetailsTabComponent], +}) +export class ExperimentDetailsTabModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.html new file mode 100644 index 00000000000..4d1233302cf --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.html @@ -0,0 +1,65 @@ +
+ + + + +
+ + + + + + +
+
+ + + + + + + Couldn't find information for the underlying Trials. + + + +
+ + + + + + + + + + + + + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.scss new file mode 100644 index 00000000000..52f4b6b8133 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.scss @@ -0,0 +1,13 @@ +.panel { + margin: 16px 0; + display: block; +} + +.graph { + margin: 16px 0; +} + +// https://github.com/angular/components/issues/9592 +.tab-height-fix { + min-height: calc(100vh - 600px - 52px); +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.spec.ts new file mode 100644 index 00000000000..cdf3fb794f2 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentDetailsComponent } from './experiment-details.component'; + +describe('ExperimentDetailsComponent', () => { + let component: ExperimentDetailsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentDetailsComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.ts new file mode 100644 index 00000000000..33f129b07c7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.component.ts @@ -0,0 +1,238 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { + ConfirmDialogService, + DIALOG_RESP, + ExponentialBackoff, + getCondition, + NamespaceService, + ToolbarButton, +} from 'kubeflow'; + +import { KWABackendService } from '../../services/backend.service'; +import { StatusEnum } from '../../enumerations/status.enum'; +import { Subscription } from 'rxjs'; +import { + numberToExponential, + transformStringResponses, +} from '../../shared/utils'; +import { getDeleteDialogConfig } from '../experiments/delete-modal-config'; +import { ExperimentK8s } from '../../models/experiment.k8s.model'; + +@Component({ + selector: 'app-experiment-details', + templateUrl: './experiment-details.component.html', + styleUrls: ['./experiment-details.component.scss'], +}) +export class ExperimentDetailsComponent implements OnInit, OnDestroy { + name: string; + namespace: string; + columns: string[] = []; + details: string[][] = []; + experimentTrialsCsv: string; + hoveredTrial: number; + experimentDetails: ExperimentK8s; + showGraph: boolean; + bestTrialName: string; + pageLoading = true; + + constructor( + private activatedRoute: ActivatedRoute, + private router: Router, + private backendService: KWABackendService, + private confirmDialog: ConfirmDialogService, + private backend: KWABackendService, + private namespaceService: NamespaceService, + ) {} + + buttonsConfig: ToolbarButton[] = [ + new ToolbarButton({ + text: 'DELETE', + icon: 'delete', + fn: () => { + this.deleteExperiment(this.name, this.namespace); + }, + }), + ]; + + private poller: ExponentialBackoff; + + private subs = new Subscription(); + + ngOnInit() { + this.name = this.activatedRoute.snapshot.params.experimentName; + + this.subs.add( + this.namespaceService.getSelectedNamespace().subscribe(namespace => { + this.namespace = namespace; + this.updateExperimentInfo(); + }), + ); + } + + ngOnDestroy(): void { + this.subs.unsubscribe(); + } + + returnToExperiments() { + this.router.navigate(['']); + } + + mouseLeftTrial() { + this.hoveredTrial = null; + } + + mouseOverTrial = (index: number) => (this.hoveredTrial = index); + + private updateExperimentInfo() { + this.backendService + .getExperimentTrialsInfo(this.name, this.namespace) + .subscribe(response => { + this.experimentTrialsCsv = response; + const data = transformStringResponses(response); + this.columns = data.types; + this.details = this.parseTrialsDetails(data.details); + this.showGraph = true; + }); + this.backendService + .getExperiment(this.name, this.namespace) + .subscribe((response: ExperimentK8s) => { + this.experimentDetails = response; + this.bestTrialName = response.status.currentOptimalTrial + ? response.status.currentOptimalTrial.bestTrialName + : ''; + + const status = this.experimentStatus(response); + + if ( + status && + !(status === StatusEnum.FAILED || status === StatusEnum.SUCCEEDED) + ) { + // if the status of the experiment is not succeeded either failed + // then start polling the trials + this.startTrialsPolling(); + this.startExperimentsPolling(); + } + + this.pageLoading = false; + }); + } + + private deleteExperiment(name: string, namespace: string) { + const deleteDialogConfig = getDeleteDialogConfig(name, namespace); + const ref = this.confirmDialog.open(name, deleteDialogConfig); + + const delSub = ref.componentInstance.applying$.subscribe(applying => { + if (!applying) { + return; + } + + // Close the open dialog only if the DELETE request succeeded + this.backend.deleteExperiment(name, namespace).subscribe({ + next: _ => { + ref.close(DIALOG_RESP.ACCEPT); + }, + error: err => { + deleteDialogConfig.error = err; + ref.componentInstance.applying$.next(false); + }, + }); + + // DELETE request has succeeded + ref.afterClosed().subscribe(res => { + delSub.unsubscribe(); + if (res !== DIALOG_RESP.ACCEPT) { + return; + } + this.returnToExperiments(); + }); + }); + } + + private startTrialsPolling() { + this.poller = new ExponentialBackoff({ + interval: 5000, + retries: 1, + maxInterval: 5001, + }); + + // Poll for new data and reset the poller if different data is found + this.subs.add( + this.poller.start().subscribe(() => { + this.backendService + .getExperimentTrialsInfo(this.name, this.namespace) + .subscribe(trials => { + this.experimentTrialsCsv = trials; + const data = transformStringResponses(trials); + this.columns = data.types; + this.details = this.parseTrialsDetails(data.details); + this.showGraph = trials.split(/\r\n|\r|\n/).length > 1; + }); + }), + ); + } + + private startExperimentsPolling() { + this.poller = new ExponentialBackoff({ + interval: 5000, + retries: 1, + maxInterval: 5001, + }); + + // Poll for new data and reset the poller if different data is found + this.subs.add( + this.poller.start().subscribe(() => { + this.backendService + .getExperiment(this.name, this.namespace) + .subscribe(response => { + this.experimentDetails = response; + this.bestTrialName = response.status.currentOptimalTrial + ? response.status.currentOptimalTrial.bestTrialName + : ''; + }); + }), + ); + } + + private parseTrialsDetails(details: string[][]): string[][] { + return details.map((detail, index) => { + const updatedDetail = detail.map(value => + isNaN(+value) || value === '' ? value : numberToExponential(+value, 6), + ); + updatedDetail.push(index.toString()); + return updatedDetail; + }); + } + + private experimentStatus(experiment: ExperimentK8s): StatusEnum { + const succeededCondition = getCondition(experiment, StatusEnum.SUCCEEDED); + + if (succeededCondition && succeededCondition.status === 'True') { + return StatusEnum.SUCCEEDED; + } + + const failedCondition = getCondition(experiment, StatusEnum.FAILED); + + if (failedCondition && failedCondition.status === 'True') { + return StatusEnum.FAILED; + } + + const runningCondition = getCondition(experiment, StatusEnum.RUNNING); + + if (runningCondition && runningCondition.status === 'True') { + return StatusEnum.RUNNING; + } + + const restartingCondition = getCondition(experiment, StatusEnum.RESTARTING); + + if (restartingCondition && restartingCondition.status === 'True') { + return StatusEnum.RESTARTING; + } + + const createdCondition = getCondition(experiment, StatusEnum.CREATED); + + if (createdCondition && createdCondition.status === 'True') { + return StatusEnum.CREATED; + } + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.module.ts new file mode 100644 index 00000000000..541edb0af97 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/experiment-details.module.ts @@ -0,0 +1,39 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { + TitleActionsToolbarModule, + LoadingSpinnerModule, + PanelModule, +} from 'kubeflow'; + +import { ExperimentDetailsComponent } from './experiment-details.component'; +import { TrialsTableModule } from './trials-table/trials-table.module'; +import { ExperimentOverviewModule } from './overview/experiment-overview.module'; +import { ExperimentDetailsTabModule } from './details/experiment-details-tab.module'; +import { TrialsGraphModule } from './trials-graph/trials-graph.module'; +import { ExperimentYamlModule } from './yaml/experiment-yaml.module'; + +@NgModule({ + declarations: [ExperimentDetailsComponent], + imports: [ + CommonModule, + TrialsTableModule, + MatButtonModule, + MatTabsModule, + MatIconModule, + LoadingSpinnerModule, + PanelModule, + ExperimentOverviewModule, + ExperimentDetailsTabModule, + TrialsGraphModule, + MatProgressSpinnerModule, + ExperimentYamlModule, + TitleActionsToolbarModule, + ], + exports: [ExperimentDetailsComponent], +}) +export class ExperimentDetailsModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.html new file mode 100644 index 00000000000..5593359e764 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.html @@ -0,0 +1,43 @@ + + {{ experimentName }} + + + + {{ status }} + + + + {{ bestTrialName }} + + + + No optimal trial yet + + + + + + + {{ userGoal }} + + + + {{ runningTrials }} + + + + {{ failedTrials }} + + + + {{ succeededTrials }} + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.spec.ts new file mode 100644 index 00000000000..31783b6dafc --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentOverviewComponent } from './experiment-overview.component'; + +describe('ExperimentOverviewComponent', () => { + let component: ExperimentOverviewComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentOverviewComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentOverviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.ts new file mode 100644 index 00000000000..33171582682 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.component.ts @@ -0,0 +1,162 @@ +import { + ChangeDetectionStrategy, + Component, + Input, + OnChanges, +} from '@angular/core'; +import { ChipDescriptor, getCondition } from 'kubeflow'; +import { ExperimentK8s } from 'src/app/models/experiment.k8s.model'; +import { ObjectiveTypeEnum } from 'src/app/enumerations/objective-type.enum'; +import { StatusEnum } from 'src/app/enumerations/status.enum'; +import { numberToExponential } from 'src/app/shared/utils'; + +@Component({ + selector: 'app-experiment-overview', + templateUrl: './experiment-overview.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ExperimentOverviewComponent implements OnChanges { + status: string; + statusIcon: string; + bestTrialName: string; + bestTrialPerformance: ChipDescriptor[]; + userGoal: string; + runningTrials: number; + failedTrials: number; + succeededTrials: number; + parameters: ChipDescriptor[] = []; + + @Input() + experimentName: string; + + @Input() + experiment: ExperimentK8s; + + constructor() {} + + ngOnChanges(): void { + if (this.experiment) { + this.generateExperimentPropsList(this.experiment); + } + } + + private generateExperimentPropsList(experiment: ExperimentK8s): void { + const optimalTrialExists: boolean = + experiment.status.currentOptimalTrial && + !!experiment.status.currentOptimalTrial.bestTrialName; + + const { status, statusIcon } = this.generateExperimentStatus(experiment); + this.status = status; + this.statusIcon = statusIcon; + + this.bestTrialName = optimalTrialExists + ? experiment.status.currentOptimalTrial.bestTrialName + : 'No optimal trial yet'; + + this.generateExperimentBestParameters(experiment); + + this.bestTrialPerformance = this.generateExperimentBestMetrics( + experiment, + optimalTrialExists, + ); + + this.userGoal = `${experiment.spec.objective.objectiveMetricName} ${ + experiment.spec.objective.type === ObjectiveTypeEnum.maximize ? '>' : '<' + } ${experiment.spec.objective.goal}`; + + this.runningTrials = experiment.status.runningTrialList + ? experiment.status.runningTrialList.length + : 0; + + this.failedTrials = experiment.status.failedTrialList + ? experiment.status.failedTrialList.length + : 0; + + this.succeededTrials = experiment.status.succeededTrialList + ? experiment.status.succeededTrialList.length + : 0; + } + + private generateExperimentStatus( + experiment: ExperimentK8s, + ): { status: string; statusIcon: string } { + const succeededCondition = getCondition(experiment, StatusEnum.SUCCEEDED); + + if (succeededCondition && succeededCondition.status === 'True') { + return { status: succeededCondition.message, statusIcon: 'check_circle' }; + } + + const failedCondition = getCondition(experiment, StatusEnum.FAILED); + + if (failedCondition && failedCondition.status === 'True') { + return { status: failedCondition.message, statusIcon: 'warning' }; + } + + const runningCondition = getCondition(experiment, StatusEnum.RUNNING); + + if (runningCondition && runningCondition.status === 'True') { + return { status: runningCondition.message, statusIcon: 'schedule' }; + } + + const restartingCondition = getCondition(experiment, StatusEnum.RESTARTING); + + if (restartingCondition && restartingCondition.status === 'True') { + return { status: restartingCondition.message, statusIcon: 'loop' }; + } + + const createdCondition = getCondition(experiment, StatusEnum.CREATED); + + if (createdCondition && createdCondition.status === 'True') { + return { + status: createdCondition.message, + statusIcon: 'add_circle_outline', + }; + } + } + + private generateExperimentBestParameters( + experiment: ExperimentK8s, + ): ChipDescriptor[] { + this.parameters = []; + + if (!experiment.status.currentOptimalTrial.parameterAssignments) { + return; + } + + const parameters = experiment.status.currentOptimalTrial.parameterAssignments.map( + param => + `${param.name}: ${ + !isNaN(+param.value) + ? numberToExponential(+param.value, 6) + : param.value + }`, + ); + + for (const c of parameters) { + const chip: ChipDescriptor = { value: c, color: 'primary' }; + this.parameters.push(chip); + } + } + + private generateExperimentBestMetrics( + experiment: ExperimentK8s, + optimalTrialExists, + ): ChipDescriptor[] { + if (!optimalTrialExists) { + return []; + } + + const metrics = experiment.status.currentOptimalTrial.observation.metrics.map( + metric => + `${metric.name}: ${ + !isNaN(+metric.latest) + ? numberToExponential(+metric.latest, 6) + : metric.latest + }`, + ); + + return metrics.map(m => { + return { value: m, color: 'primary', tooltip: 'Latest value' }; + }); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.module.ts new file mode 100644 index 00000000000..2c091ed748a --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/overview/experiment-overview.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ConditionsTableModule, DetailsListModule } from 'kubeflow'; + +import { ExperimentOverviewComponent } from './experiment-overview.component'; + +@NgModule({ + declarations: [ExperimentOverviewComponent], + imports: [CommonModule, ConditionsTableModule, DetailsListModule], + exports: [ExperimentOverviewComponent], +}) +export class ExperimentOverviewModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/d3.parcoords.js b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/d3.parcoords.js new file mode 100644 index 00000000000..e4bcb760c0c --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/d3.parcoords.js @@ -0,0 +1,2907 @@ +/** + * Vector helpers + */ +function vectorAdd(as, bs) { + const cs = []; + + if (as.length !== bs.length) { + return null; + } + + for (let i = 0; i < as.length; i++) { + cs.push(as[i] + bs[i]); + } + + return cs; +} + +function vectorMultiply(as, k) { + const cs = []; + + for (let i = 0; i < as.length; i++) { + cs.push(as[i] * k); + } + + return cs; +} + +function vectorSubtract(as, bs) { + const cs = []; + + if (as.length !== bs.length) { + return null; + } + + for (let i = 0; i < as.length; i++) { + cs.push(as[i] - bs[i]); + } + + return cs; +} + +/** + * Create a d3 graph with Parallel Coordinates + */ +export function parcoords(config) { + var __ = { + data: [], + highlighted: [], + dimensions: {}, + dimensionTitleRotation: 0, + brushed: false, + brushedColor: null, + alphaOnBrushed: 0.0, + mode: 'default', + rate: 20, + width: 600, + height: 300, + margin: { top: 24, right: 0, bottom: 12, left: 0 }, + nullValueSeparator: 'undefined', // set to "top" or "bottom" + nullValueSeparatorPadding: { top: 8, right: 0, bottom: 8, left: 0 }, + color: '#069', + composite: 'source-over', + alpha: 0.7, + bundlingStrength: 0.5, + bundleDimension: null, + smoothness: 0.0, + showControlPoints: false, + hideAxis: [], + flipAxes: [], + animationTime: 1100, // How long it takes to flip the axis when you double click + rotateLabels: false, + }; + + extend(__, config); + + if (config && config.dimensionTitles) { + console.warn( + 'dimensionTitles passed in config is deprecated. Add title to dimension object.', + ); + d3.entries(config.dimensionTitles).forEach(function(d) { + if (__.dimensions[d.key]) { + __.dimensions[d.key].title = __.dimensions[d.key].title + ? __.dimensions[d.key].title + : d.value; + } else { + __.dimensions[d.key] = { + title: d.value, + }; + } + }); + } + var pc = function(selection) { + selection = pc.selection = d3.select(selection); + + __.width = selection[0][0].clientWidth; + __.height = selection[0][0].clientHeight; + + // canvas data layers + ['marks', 'foreground', 'brushed', 'highlight'].forEach(function(layer) { + canvas[layer] = selection.append('canvas').attr('class', layer)[0][0]; + ctx[layer] = canvas[layer].getContext('2d'); + }); + + // svg tick and brush layers + pc.svg = selection + .append('svg') + .attr('width', __.width) + .attr('height', __.height) + .style('font', '14px sans-serif') + .style('position', 'absolute') + + .append('svg:g') + .attr( + 'transform', + 'translate(' + __.margin.left + ',' + __.margin.top + ')', + ); + + return pc; + }; + var events = d3.dispatch.apply( + this, + [ + 'render', + 'resize', + 'highlight', + 'brush', + 'brushend', + 'brushstart', + 'axesreorder', + ].concat(d3.keys(__)), + ), + w = function() { + return __.width - __.margin.right - __.margin.left; + }, + h = function() { + return __.height - __.margin.top - __.margin.bottom; + }, + flags = { + brushable: false, + reorderable: false, + axes: false, + interactive: false, + debug: false, + }, + xscale = d3.scale.ordinal(), + dragging = {}, + line = d3.svg.line(), + axis = d3.svg + .axis() + .orient('left') + .ticks(5), + g, // groups for axes, brushes + ctx = {}, + canvas = {}, + clusterCentroids = []; + + // side effects for setters + var side_effects = d3.dispatch + .apply(this, d3.keys(__)) + .on('composite', function(d) { + ctx.foreground.globalCompositeOperation = d.value; + ctx.brushed.globalCompositeOperation = d.value; + }) + .on('alpha', function(d) { + ctx.foreground.globalAlpha = d.value; + ctx.brushed.globalAlpha = d.value; + }) + .on('brushedColor', function(d) { + ctx.brushed.strokeStyle = d.value; + }) + .on('width', function(d) { + pc.resize(); + }) + .on('height', function(d) { + pc.resize(); + }) + .on('margin', function(d) { + pc.resize(); + }) + .on('rate', function(d) { + brushedQueue.rate(d.value); + foregroundQueue.rate(d.value); + }) + .on('dimensions', function(d) { + __.dimensions = pc.applyDimensionDefaults(d3.keys(d.value)); + xscale.domain(pc.getOrderedDimensionKeys()); + pc.sortDimensions(); + if (flags.interactive) { + pc.render().updateAxes(); + } + }) + .on('bundleDimension', function(d) { + if (!d3.keys(__.dimensions).length) pc.detectDimensions(); + pc.autoscale(); + if (typeof d.value === 'number') { + if (d.value < d3.keys(__.dimensions).length) { + __.bundleDimension = __.dimensions[d.value]; + } else if (d.value < __.hideAxis.length) { + __.bundleDimension = __.hideAxis[d.value]; + } + } else { + __.bundleDimension = d.value; + } + + __.clusterCentroids = compute_cluster_centroids(__.bundleDimension); + if (flags.interactive) { + pc.render(); + } + }) + .on('hideAxis', function(d) { + pc.dimensions(pc.applyDimensionDefaults()); + pc.dimensions(without(__.dimensions, d.value)); + }) + .on('flipAxes', function(d) { + if (d.value && d.value.length) { + d.value.forEach(function(axis) { + flipAxisAndUpdatePCP(axis); + }); + pc.updateAxes(0); + } + }); + + // expose the state of the chart + pc.state = __; + pc.flags = flags; + + // create getter/setters + getset(pc, __, events); + + // expose events + d3.rebind(pc, events, 'on'); + + // getter/setter with event firing + function getset(obj, state, events) { + d3.keys(state).forEach(function(key) { + obj[key] = function(x) { + if (!arguments.length) { + return state[key]; + } + if ( + key === 'dimensions' && + Object.prototype.toString.call(x) === '[object Array]' + ) { + console.warn( + 'pc.dimensions([]) is deprecated, use pc.dimensions({})', + ); + x = pc.applyDimensionDefaults(x); + } + var old = state[key]; + state[key] = x; + side_effects[key].call(pc, { value: x, previous: old }); + events[key].call(pc, { value: x, previous: old }); + return obj; + }; + }); + } + + function extend(target, source) { + for (var key in source) { + target[key] = source[key]; + } + return target; + } + + function without(arr, items) { + items.forEach(function(el) { + delete arr[el]; + }); + return arr; + } + /** adjusts an axis' default range [h()+1, 1] if a NullValueSeparator is set */ + function getRange() { + if (__.nullValueSeparator == 'bottom') { + return [ + h() + + 1 - + __.nullValueSeparatorPadding.bottom - + __.nullValueSeparatorPadding.top, + 1, + ]; + } else if (__.nullValueSeparator == 'top') { + return [ + h() + 1, + 1 + + __.nullValueSeparatorPadding.bottom + + __.nullValueSeparatorPadding.top, + ]; + } + return [h() + 1, 1]; + } + + pc.autoscale = function() { + // yscale + var defaultScales = { + date: function(k) { + var extent = d3.extent(__.data, function(d) { + return d[k] ? d[k].getTime() : null; + }); + + // special case if single value + if (extent[0] === extent[1]) { + return d3.scale + .ordinal() + .domain([extent[0]]) + .rangePoints(getRange()); + } + + return d3.time + .scale() + .domain(extent) + .range(getRange()); + }, + number: function(k) { + var extent = d3.extent(__.data, function(d) { + return +d[k]; + }); + + // special case if single value + if (extent[0] === extent[1]) { + return d3.scale + .ordinal() + .domain([extent[0]]) + .rangePoints(getRange()); + } + + return d3.scale + .linear() + .domain(extent) + .range(getRange()); + }, + string: function(k) { + var counts = {}, + domain = []; + + // Let's get the count for each value so that we can sort the domain based + // on the number of items for each value. + __.data.map(function(p) { + if (p[k] === undefined && __.nullValueSeparator !== 'undefined') { + return; // null values will be drawn beyond the horizontal null value separator! + } + if (counts[p[k]] === undefined) { + counts[p[k]] = 1; + } else { + counts[p[k]] = counts[p[k]] + 1; + } + }); + + domain = Object.getOwnPropertyNames(counts).sort(function(a, b) { + return counts[a] - counts[b]; + }); + + return d3.scale + .ordinal() + .domain(domain) + .rangePoints(getRange()); + }, + }; + + d3.keys(__.dimensions).forEach(function(k) { + if (!__.dimensions[k].yscale) { + __.dimensions[k].yscale = defaultScales[__.dimensions[k].type](k); + } + }); + + // xscale + xscale.rangePoints([0, w()], 1); + + // Retina display, etc. + var devicePixelRatio = window.devicePixelRatio || 1; + + // canvas sizes + pc.selection + .selectAll('canvas') + .style('margin-top', __.margin.top + 'px') + .style('margin-left', __.margin.left + 'px') + .style('width', w() + 2 + 'px') + .style('height', h() + 2 + 'px') + .attr('width', (w() + 2) * devicePixelRatio) + .attr('height', (h() + 2) * devicePixelRatio); + + // default styles, needs to be set when canvas width changes + ctx.foreground.strokeStyle = __.color; + ctx.foreground.lineWidth = 1.4; + ctx.foreground.globalCompositeOperation = __.composite; + ctx.foreground.globalAlpha = __.alpha; + ctx.foreground.scale(devicePixelRatio, devicePixelRatio); + ctx.brushed.strokeStyle = __.brushedColor; + ctx.brushed.lineWidth = 1.4; + ctx.brushed.globalCompositeOperation = __.composite; + ctx.brushed.globalAlpha = __.alpha; + ctx.brushed.scale(devicePixelRatio, devicePixelRatio); + ctx.highlight.lineWidth = 3; + ctx.highlight.scale(devicePixelRatio, devicePixelRatio); + + return this; + }; + + pc.scale = function(d, domain) { + __.dimensions[d].yscale.domain(domain); + + return this; + }; + + pc.flip = function(d) { + //__.dimensions[d].yscale.domain().reverse(); // does not work + __.dimensions[d].yscale.domain(__.dimensions[d].yscale.domain().reverse()); // works + + return this; + }; + + pc.commonScale = function(global, type) { + var t = type || 'number'; + if (typeof global === 'undefined') { + global = true; + } + + // try to autodetect dimensions and create scales + if (!d3.keys(__.dimensions).length) { + pc.detectDimensions(); + } + pc.autoscale(); + + // scales of the same type + var scales = d3.keys(__.dimensions).filter(function(p) { + return __.dimensions[p].type == t; + }); + + if (global) { + var extent = d3.extent( + scales + .map(function(d, i) { + return __.dimensions[d].yscale.domain(); + }) + .reduce(function(a, b) { + return a.concat(b); + }), + ); + + scales.forEach(function(d) { + __.dimensions[d].yscale.domain(extent); + }); + } else { + scales.forEach(function(d) { + __.dimensions[d].yscale.domain( + d3.extent(__.data, function(d) { + return +d[k]; + }), + ); + }); + } + + // update centroids + if (__.bundleDimension !== null) { + pc.bundleDimension(__.bundleDimension); + } + + return this; + }; + pc.detectDimensions = function() { + pc.dimensions(pc.applyDimensionDefaults()); + return this; + }; + + pc.applyDimensionDefaults = function(dims) { + var types = pc.detectDimensionTypes(__.data); + dims = dims ? dims : d3.keys(types); + var newDims = {}; + var currIndex = 0; + dims.forEach(function(k) { + newDims[k] = __.dimensions[k] ? __.dimensions[k] : {}; + //Set up defaults + newDims[k].orient = newDims[k].orient ? newDims[k].orient : 'left'; + newDims[k].ticks = newDims[k].ticks != null ? newDims[k].ticks : 5; + newDims[k].innerTickSize = + newDims[k].innerTickSize != null ? newDims[k].innerTickSize : 6; + newDims[k].outerTickSize = + newDims[k].outerTickSize != null ? newDims[k].outerTickSize : 0; + newDims[k].tickPadding = + newDims[k].tickPadding != null ? newDims[k].tickPadding : 3; + newDims[k].type = newDims[k].type ? newDims[k].type : types[k]; + + newDims[k].index = + newDims[k].index != null ? newDims[k].index : currIndex; + currIndex++; + }); + return newDims; + }; + + pc.getOrderedDimensionKeys = function() { + return d3.keys(__.dimensions).sort(function(x, y) { + return d3.ascending(__.dimensions[x].index, __.dimensions[y].index); + }); + }; + + // a better "typeof" from this post: http://stackoverflow.com/questions/7390426/better-way-to-get-type-of-a-javascript-variable + pc.toType = function(v) { + return {}.toString + .call(v) + .match(/\s([a-zA-Z]+)/)[1] + .toLowerCase(); + }; + + // try to coerce to number before returning type + pc.toTypeCoerceNumbers = function(v) { + if (parseFloat(v) == v && v != null) { + return 'number'; + } + return pc.toType(v); + }; + + // attempt to determine types of each dimension based on first row of data + pc.detectDimensionTypes = function(data) { + var types = {}; + d3.keys(data[0]).forEach(function(col) { + types[isNaN(Number(col)) ? col : parseInt(col)] = pc.toTypeCoerceNumbers( + data[0][col], + ); + }); + return types; + }; + pc.render = function() { + // try to autodetect dimensions and create scales + if (!d3.keys(__.dimensions).length) { + pc.detectDimensions(); + } + pc.autoscale(); + + pc.render[__.mode](); + + events.render.call(this); + return this; + }; + + pc.renderBrushed = function() { + if (!d3.keys(__.dimensions).length) pc.detectDimensions(); + + pc.renderBrushed[__.mode](); + + events.render.call(this); + return this; + }; + + function isBrushed() { + if (__.brushed && __.brushed.length !== __.data.length) return true; + + var object = brush.currentMode().brushState(); + + for (var key in object) { + if (object.hasOwnProperty(key)) { + return true; + } + } + return false; + } + + pc.render.default = function() { + pc.clear('foreground'); + pc.clear('highlight'); + + pc.renderBrushed.default(); + + __.data.forEach(path_foreground); + }; + + var foregroundQueue = d3 + .renderQueue(path_foreground) + .rate(50) + .clear(function() { + pc.clear('foreground'); + pc.clear('highlight'); + }); + + pc.render.queue = function() { + pc.renderBrushed.queue(); + + foregroundQueue(__.data); + }; + + pc.renderBrushed.default = function() { + pc.clear('brushed'); + + if (isBrushed()) { + __.brushed.forEach(path_brushed); + } + }; + + var brushedQueue = d3 + .renderQueue(path_brushed) + .rate(50) + .clear(function() { + pc.clear('brushed'); + }); + + pc.renderBrushed.queue = function() { + if (isBrushed()) { + brushedQueue(__.brushed); + } else { + brushedQueue([]); // This is needed to clear the currently brushed items + } + }; + function compute_cluster_centroids(d) { + var clusterCentroids = d3.map(); + var clusterCounts = d3.map(); + // determine clusterCounts + __.data.forEach(function(row) { + var scaled = __.dimensions[d].yscale(row[d]); + if (!clusterCounts.has(scaled)) { + clusterCounts.set(scaled, 0); + } + var count = clusterCounts.get(scaled); + clusterCounts.set(scaled, count + 1); + }); + + __.data.forEach(function(row) { + d3.keys(__.dimensions).map(function(p, i) { + var scaled = __.dimensions[d].yscale(row[d]); + if (!clusterCentroids.has(scaled)) { + var map = d3.map(); + clusterCentroids.set(scaled, map); + } + if (!clusterCentroids.get(scaled).has(p)) { + clusterCentroids.get(scaled).set(p, 0); + } + var value = clusterCentroids.get(scaled).get(p); + value += __.dimensions[p].yscale(row[p]) / clusterCounts.get(scaled); + clusterCentroids.get(scaled).set(p, value); + }); + }); + + return clusterCentroids; + } + + function compute_centroids(row) { + var centroids = []; + + var p = d3.keys(__.dimensions); + var cols = p.length; + var a = 0.5; // center between axes + for (var i = 0; i < cols; ++i) { + // centroids on 'real' axes + var x = position(p[i]); + var y = __.dimensions[p[i]].yscale(row[p[i]]); + centroids.push([x, y]); + + // centroids on 'virtual' axes + if (i < cols - 1) { + var cx = x + a * (position(p[i + 1]) - x); + var cy = y + a * (__.dimensions[p[i + 1]].yscale(row[p[i + 1]]) - y); + if (__.bundleDimension !== null) { + var leftCentroid = __.clusterCentroids + .get( + __.dimensions[__.bundleDimension].yscale(row[__.bundleDimension]), + ) + .get(p[i]); + var rightCentroid = __.clusterCentroids + .get( + __.dimensions[__.bundleDimension].yscale(row[__.bundleDimension]), + ) + .get(p[i + 1]); + var centroid = 0.5 * (leftCentroid + rightCentroid); + cy = centroid + (1 - __.bundlingStrength) * (cy - centroid); + } + centroids.push([cx, cy]); + } + } + + return centroids; + } + + pc.compute_real_centroids = function(row) { + var realCentroids = []; + + var p = d3.keys(__.dimensions); + var cols = p.length; + var a = 0.5; + + for (var i = 0; i < cols; ++i) { + var x = position(p[i]); + var y = __.dimensions[p[i]].yscale(row[p[i]]); + realCentroids.push([x, y]); + } + + return realCentroids; + }; + + function compute_control_points(centroids) { + var cols = centroids.length; + var a = __.smoothness; + var cps = []; + + cps.push(centroids[0]); + cps.push([ + centroids[0][0] + a * 2 * (centroids[1][0] - centroids[0][0]), + centroids[0][1], + ]); + for (var col = 1; col < cols - 1; ++col) { + var mid = centroids[col]; + var left = centroids[col - 1]; + var right = centroids[col + 1]; + + var diff = vectorSubtract(left, right); + cps.push(vectorAdd(mid, vectorMultiply(diff, a))); + cps.push(mid); + cps.push(vectorSubtract(mid, vectorMultiply(diff, a))); + } + cps.push([ + centroids[cols - 1][0] + + a * 2 * (centroids[cols - 2][0] - centroids[cols - 1][0]), + centroids[cols - 1][1], + ]); + cps.push(centroids[cols - 1]); + + return cps; + } + pc.shadows = function() { + flags.shadows = true; + pc.alphaOnBrushed(0.1); + pc.render(); + return this; + }; + + // draw dots with radius r on the axis line where data intersects + pc.axisDots = function(r) { + var r = r || 0.1; + var ctx = pc.ctx.marks; + var startAngle = 0; + var endAngle = 2 * Math.PI; + ctx.globalAlpha = d3.min([1 / Math.pow(__.data.length, 1 / 2), 1]); + __.data.forEach(function(d) { + d3.entries(__.dimensions).forEach(function(p, i) { + ctx.beginPath(); + ctx.arc( + position(p), + __.dimensions[p.key].yscale(d[p]), + r, + startAngle, + endAngle, + ); + ctx.stroke(); + ctx.fill(); + }); + }); + return this; + }; + + // draw single cubic bezier curve + function single_curve(d, ctx) { + var centroids = compute_centroids(d); + var cps = compute_control_points(centroids); + + ctx.moveTo(cps[0][0], cps[0][1]); + for (var i = 1; i < cps.length; i += 3) { + if (__.showControlPoints) { + for (var j = 0; j < 3; j++) { + ctx.fillRect(cps[i + j][0], cps[i + j][1], 2, 2); + } + } + ctx.bezierCurveTo( + cps[i][0], + cps[i][1], + cps[i + 1][0], + cps[i + 1][1], + cps[i + 2][0], + cps[i + 2][1], + ); + } + } + + // draw single polyline + function color_path(d, ctx) { + ctx.beginPath(); + if ( + (__.bundleDimension !== null && __.bundlingStrength > 0) || + __.smoothness > 0 + ) { + single_curve(d, ctx); + } else { + single_path(d, ctx); + } + ctx.stroke(); + } + + // draw many polylines of the same color + function paths(data, ctx) { + ctx.clearRect(-1, -1, w() + 2, h() + 2); + ctx.beginPath(); + data.forEach(function(d) { + if ( + (__.bundleDimension !== null && __.bundlingStrength > 0) || + __.smoothness > 0 + ) { + single_curve(d, ctx); + } else { + single_path(d, ctx); + } + }); + ctx.stroke(); + } + + // returns the y-position just beyond the separating null value line + function getNullPosition() { + if (__.nullValueSeparator == 'bottom') { + return h() + 1; + } else if (__.nullValueSeparator == 'top') { + return 1; + } else { + console.log( + "A value is NULL, but nullValueSeparator is not set; set it to 'bottom' or 'top'.", + ); + } + return h() + 1; + } + + function single_path(d, ctx) { + d3.entries(__.dimensions).forEach(function(p, i) { + //p isn't really p + if (i == 0) { + ctx.moveTo( + position(p.key), + typeof d[p.key] == 'undefined' + ? getNullPosition() + : __.dimensions[p.key].yscale(d[p.key]), + ); + } else { + ctx.lineTo( + position(p.key), + typeof d[p.key] == 'undefined' + ? getNullPosition() + : __.dimensions[p.key].yscale(d[p.key]), + ); + } + }); + } + + function path_brushed(d, i) { + if (__.brushedColor !== null) { + ctx.brushed.strokeStyle = d3.functor(__.brushedColor)(d, i); + } else { + ctx.brushed.strokeStyle = d3.functor(__.color)(d, i); + } + return color_path(d, ctx.brushed); + } + + function path_foreground(d, i) { + ctx.foreground.strokeStyle = d3.functor(__.color)(d, i); + return color_path(d, ctx.foreground); + } + + function path_highlight(d, i) { + ctx.highlight.strokeStyle = d3.functor(__.color)(d, i); + return color_path(d, ctx.highlight); + } + pc.clear = function(layer) { + ctx[layer].clearRect(0, 0, w() + 2, h() + 2); + + // This will make sure that the foreground items are transparent + // without the need for changing the opacity style of the foreground canvas + // as this would stop the css styling from working + if (layer === 'brushed' && isBrushed()) { + ctx.brushed.fillStyle = pc.selection.style('background-color'); + ctx.brushed.globalAlpha = 1 - __.alphaOnBrushed; + ctx.brushed.fillRect(0, 0, w() + 2, h() + 2); + ctx.brushed.globalAlpha = __.alpha; + } + return this; + }; + d3.rebind( + pc, + axis, + 'ticks', + 'orient', + 'tickValues', + 'tickSubdivide', + 'tickSize', + 'tickPadding', + 'tickFormat', + ); + + function flipAxisAndUpdatePCP(dimension) { + var g = pc.svg.selectAll('.dimension'); + + pc.flip(dimension); + + d3.select(this.parentElement) + .transition() + .duration(__.animationTime) + .call(pc.applyAxisConfig(axis, __.dimensions[dimension])) + .call(axis.orient(__.dimensions[dimension].orient)) + .call(axis.ticks(__.dimensions[dimension].ticks)) + .call(axis.innerTickSize(__.dimensions[dimension].innerTickSize)) + .call(axis.outerTickSize(__.dimensions[dimension].outerTickSize)) + .call(axis.tickPadding(__.dimensions[dimension].tickPadding)) + .call(axis.tickFormat(__.dimensions[dimension].tickFormat)); + + pc.render(); + } + + function rotateLabels() { + if (!__.rotateLabels) return; + + var delta = d3.event.deltaY; + delta = delta < 0 ? -5 : delta; + delta = delta > 0 ? 5 : delta; + + __.dimensionTitleRotation += delta; + pc.svg + .selectAll('text.label') + .attr( + 'transform', + 'translate(0,-5) rotate(' + __.dimensionTitleRotation + ')', + ); + d3.event.preventDefault(); + } + + function dimensionLabels(d) { + return __.dimensions[d].title ? __.dimensions[d].title : d; // dimension display names + } + + pc.createAxes = function() { + if (g) pc.removeAxes(); + + // Add a group element for each dimension. + g = pc.svg + .selectAll('.dimension') + .data(pc.getOrderedDimensionKeys(), function(d) { + return d; + }) + .enter() + .append('svg:g') + .attr('class', 'dimension') + .attr('transform', function(d) { + return 'translate(' + xscale(d) + ')'; + }); + + // Add an axis and title. + g.append('svg:g') + .attr('class', 'axis') + .attr('transform', 'translate(0,0)') + .each(function(d) { + var axisElement = d3 + .select(this) + .call(pc.applyAxisConfig(axis, __.dimensions[d])); + + axisElement + .selectAll('path') + .style('fill', 'none') + .style('stroke', '#222') + .style('shape-rendering', 'crispEdges'); + + axisElement + .selectAll('line') + .style('fill', 'none') + .style('stroke', '#222') + .style('shape-rendering', 'crispEdges'); + }) + .append('svg:text') + .attr({ + 'text-anchor': 'middle', + y: 0, + transform: 'translate(0,-5) rotate(' + __.dimensionTitleRotation + ')', + x: 0, + class: 'label', + }) + .text(dimensionLabels) + .on('dblclick', flipAxisAndUpdatePCP) + .on('wheel', rotateLabels); + + if (__.nullValueSeparator == 'top') { + pc.svg + .append('line') + .attr('x1', 0) + .attr('y1', 1 + __.nullValueSeparatorPadding.top) + .attr('x2', w()) + .attr('y2', 1 + __.nullValueSeparatorPadding.top) + .attr('stroke-width', 1) + .attr('stroke', '#777') + .attr('fill', 'none') + .attr('shape-rendering', 'crispEdges'); + } else if (__.nullValueSeparator == 'bottom') { + pc.svg + .append('line') + .attr('x1', 0) + .attr('y1', h() + 1 - __.nullValueSeparatorPadding.bottom) + .attr('x2', w()) + .attr('y2', h() + 1 - __.nullValueSeparatorPadding.bottom) + .attr('stroke-width', 1) + .attr('stroke', '#777') + .attr('fill', 'none') + .attr('shape-rendering', 'crispEdges'); + } + + flags.axes = true; + return this; + }; + + pc.removeAxes = function() { + g.remove(); + g = undefined; + return this; + }; + + pc.updateAxes = function(animationTime) { + if (typeof animationTime === 'undefined') { + animationTime = __.animationTime; + } + + var g_data = pc.svg + .selectAll('.dimension') + .data(pc.getOrderedDimensionKeys()); + + // Enter + g_data + .enter() + .append('svg:g') + .attr('class', 'dimension') + .attr('transform', function(p) { + return 'translate(' + position(p) + ')'; + }) + .style('opacity', 0) + .append('svg:g') + .attr('class', 'axis') + .attr('transform', 'translate(0,0)') + .each(function(d) { + var axisElement = d3 + .select(this) + .call(pc.applyAxisConfig(axis, __.dimensions[d])); + + axisElement + .selectAll('path') + .style('fill', 'none') + .style('stroke', '#222') + .style('shape-rendering', 'crispEdges'); + + axisElement + .selectAll('line') + .style('fill', 'none') + .style('stroke', '#222') + .style('shape-rendering', 'crispEdges'); + }) + .append('svg:text') + .attr({ + 'text-anchor': 'middle', + y: 0, + transform: 'translate(0,-5) rotate(' + __.dimensionTitleRotation + ')', + x: 0, + class: 'label', + }) + .text(dimensionLabels) + .on('dblclick', flipAxisAndUpdatePCP) + .on('wheel', rotateLabels); + + // Update + g_data.attr('opacity', 0); + g_data + .select('.axis') + .transition() + .duration(animationTime) + .each(function(d) { + d3.select(this).call(pc.applyAxisConfig(axis, __.dimensions[d])); + }); + g_data + .select('.label') + .transition() + .duration(animationTime) + .text(dimensionLabels) + .attr( + 'transform', + 'translate(0,-5) rotate(' + __.dimensionTitleRotation + ')', + ); + + // Exit + g_data.exit().remove(); + + g = pc.svg.selectAll('.dimension'); + g.transition() + .duration(animationTime) + .attr('transform', function(p) { + return 'translate(' + position(p) + ')'; + }) + .style('opacity', 1); + + pc.svg + .selectAll('.axis') + .transition() + .duration(animationTime) + .each(function(d) { + d3.select(this).call(pc.applyAxisConfig(axis, __.dimensions[d])); + }); + + if (flags.brushable) pc.brushable(); + if (flags.reorderable) pc.reorderable(); + if (pc.brushMode() !== 'None') { + var mode = pc.brushMode(); + pc.brushMode('None'); + pc.brushMode(mode); + } + return this; + }; + + pc.applyAxisConfig = function(axis, dimension) { + return axis + .scale(dimension.yscale) + .orient(dimension.orient) + .ticks(dimension.ticks) + .tickValues(dimension.tickValues) + .innerTickSize(dimension.innerTickSize) + .outerTickSize(dimension.outerTickSize) + .tickPadding(dimension.tickPadding) + .tickFormat(dimension.tickFormat); + }; + + // Jason Davies, http://bl.ocks.org/1341281 + pc.reorderable = function() { + if (!g) pc.createAxes(); + + g.style('cursor', 'move').call( + d3.behavior + .drag() + .on('dragstart', function(d) { + dragging[d] = this.__origin__ = xscale(d); + }) + .on('drag', function(d) { + dragging[d] = Math.min( + w(), + Math.max(0, (this.__origin__ += d3.event.dx)), + ); + pc.sortDimensions(); + xscale.domain(pc.getOrderedDimensionKeys()); + pc.render(); + g.attr('transform', function(d) { + return 'translate(' + position(d) + ')'; + }); + }) + .on('dragend', function(d) { + // Let's see if the order has changed and send out an event if so. + var i = 0, + j = __.dimensions[d].index, + elem = this, + parent = this.parentElement; + + while ((elem = elem.previousElementSibling) != null) ++i; + if (i !== j) { + events.axesreorder.call(pc, pc.getOrderedDimensionKeys()); + // We now also want to reorder the actual dom elements that represent + // the axes. That is, the g.dimension elements. If we don't do this, + // we get a weird and confusing transition when updateAxes is called. + // This is due to the fact that, initially the nth g.dimension element + // represents the nth axis. However, after a manual reordering, + // without reordering the dom elements, the nth dom elements no longer + // necessarily represents the nth axis. + // + // i is the original index of the dom element + // j is the new index of the dom element + if (i > j) { + // Element moved left + parent.insertBefore(this, parent.children[j - 1]); + } else { + // Element moved right + if (j + 1 < parent.children.length) { + parent.insertBefore(this, parent.children[j + 1]); + } else { + parent.appendChild(this); + } + } + } + + delete this.__origin__; + delete dragging[d]; + d3.select(this) + .transition() + .attr('transform', 'translate(' + xscale(d) + ')'); + pc.render(); + }), + ); + flags.reorderable = true; + return this; + }; + + // Reorder dimensions, such that the highest value (visually) is on the left and + // the lowest on the right. Visual values are determined by the data values in + // the given row. + pc.reorder = function(rowdata) { + var firstDim = pc.getOrderedDimensionKeys()[0]; + + pc.sortDimensionsByRowData(rowdata); + // NOTE: this is relatively cheap given that: + // number of dimensions < number of data items + // Thus we check equality of order to prevent rerendering when this is the case. + var reordered = false; + reordered = firstDim !== pc.getOrderedDimensionKeys()[0]; + + if (reordered) { + xscale.domain(pc.getOrderedDimensionKeys()); + var highlighted = __.highlighted.slice(0); + pc.unhighlight(); + + g.transition() + .duration(1500) + .attr('transform', function(d) { + return 'translate(' + xscale(d) + ')'; + }); + pc.render(); + + // pc.highlight() does not check whether highlighted is length zero, so we do that here. + if (highlighted.length !== 0) { + pc.highlight(highlighted); + } + } + }; + + pc.sortDimensionsByRowData = function(rowdata) { + var copy = __.dimensions; + var positionSortedKeys = d3.keys(__.dimensions).sort(function(a, b) { + var pixelDifference = + __.dimensions[a].yscale(rowdata[a]) - + __.dimensions[b].yscale(rowdata[b]); + + // Array.sort is not necessarily stable, this means that if pixelDifference is zero + // the ordering of dimensions might change unexpectedly. This is solved by sorting on + // variable name in that case. + if (pixelDifference === 0) { + return a.localeCompare(b); + } // else + return pixelDifference; + }); + __.dimensions = {}; + positionSortedKeys.forEach(function(p, i) { + __.dimensions[p] = copy[p]; + __.dimensions[p].index = i; + }); + }; + + pc.sortDimensions = function() { + var copy = __.dimensions; + var positionSortedKeys = d3.keys(__.dimensions).sort(function(a, b) { + return position(a) - position(b); + }); + __.dimensions = {}; + positionSortedKeys.forEach(function(p, i) { + __.dimensions[p] = copy[p]; + __.dimensions[p].index = i; + }); + }; + + // pairs of adjacent dimensions + pc.adjacent_pairs = function(arr) { + var ret = []; + for (var i = 0; i < arr.length - 1; i++) { + ret.push([arr[i], arr[i + 1]]); + } + return ret; + }; + + var brush = { + modes: { + None: { + install: function(pc) {}, // Nothing to be done. + uninstall: function(pc) {}, // Nothing to be done. + selected: function() { + return []; + }, // Nothing to return + brushState: function() { + return {}; + }, + }, + }, + mode: 'None', + predicate: 'AND', + currentMode: function() { + return this.modes[this.mode]; + }, + }; + + // This function can be used for 'live' updates of brushes. That is, during the + // specification of a brush, this method can be called to update the view. + // + // @param newSelection - The new set of data items that is currently contained + // by the brushes + function brushUpdated(newSelection) { + __.brushed = newSelection; + events.brush.call(pc, __.brushed); + pc.renderBrushed(); + } + + function brushPredicate(predicate) { + if (!arguments.length) { + return brush.predicate; + } + + predicate = String(predicate).toUpperCase(); + if (predicate !== 'AND' && predicate !== 'OR') { + throw 'Invalid predicate ' + predicate; + } + + brush.predicate = predicate; + __.brushed = brush.currentMode().selected(); + pc.renderBrushed(); + return pc; + } + + pc.brushModes = function() { + return Object.getOwnPropertyNames(brush.modes); + }; + + pc.brushMode = function(mode) { + if (arguments.length === 0) { + return brush.mode; + } + + if (pc.brushModes().indexOf(mode) === -1) { + throw 'pc.brushmode: Unsupported brush mode: ' + mode; + } + + // Make sure that we don't trigger unnecessary events by checking if the mode + // actually changes. + if (mode !== brush.mode) { + // When changing brush modes, the first thing we need to do is clearing any + // brushes from the current mode, if any. + if (brush.mode !== 'None') { + pc.brushReset(); + } + + // Next, we need to 'uninstall' the current brushMode. + brush.modes[brush.mode].uninstall(pc); + // Finally, we can install the requested one. + brush.mode = mode; + brush.modes[brush.mode].install(); + if (mode === 'None') { + delete pc.brushPredicate; + } else { + pc.brushPredicate = brushPredicate; + } + } + + return pc; + }; + + // brush mode: 1D-Axes + + (function() { + var brushes = {}; + + function is_brushed(p) { + return !brushes[p].empty(); + } + + // data within extents + function selected() { + var actives = d3.keys(__.dimensions).filter(is_brushed), + extents = actives.map(function(p) { + return brushes[p].extent(); + }); + + // We don't want to return the full data set when there are no axes brushed. + // Actually, when there are no axes brushed, by definition, no items are + // selected. So, let's avoid the filtering and just return false. + //if (actives.length === 0) return false; + + // Resolves broken examples for now. They expect to get the full dataset back from empty brushes + if (actives.length === 0) return __.data; + + // test if within range + var within = { + date: function(d, p, dimension) { + if (typeof __.dimensions[p].yscale.rangePoints === 'function') { + // if it is ordinal + return ( + extents[dimension][0] <= __.dimensions[p].yscale(d[p]) && + __.dimensions[p].yscale(d[p]) <= extents[dimension][1] + ); + } else { + return ( + extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1] + ); + } + }, + number: function(d, p, dimension) { + if (typeof __.dimensions[p].yscale.rangePoints === 'function') { + // if it is ordinal + return ( + extents[dimension][0] <= __.dimensions[p].yscale(d[p]) && + __.dimensions[p].yscale(d[p]) <= extents[dimension][1] + ); + } else { + return ( + extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1] + ); + } + }, + string: function(d, p, dimension) { + return ( + extents[dimension][0] <= __.dimensions[p].yscale(d[p]) && + __.dimensions[p].yscale(d[p]) <= extents[dimension][1] + ); + }, + }; + + return __.data.filter(function(d) { + switch (brush.predicate) { + case 'AND': + return actives.every(function(p, dimension) { + return within[__.dimensions[p].type](d, p, dimension); + }); + case 'OR': + return actives.some(function(p, dimension) { + return within[__.dimensions[p].type](d, p, dimension); + }); + default: + throw 'Unknown brush predicate ' + __.brushPredicate; + } + }); + } + + function brushExtents(extents) { + if (typeof extents === 'undefined') { + var extents = {}; + d3.keys(__.dimensions).forEach(function(d) { + var brush = brushes[d]; + if (brush !== undefined && !brush.empty()) { + var extent = brush.extent(); + extent.sort(d3.ascending); + extents[d] = extent; + } + }); + return extents; + } else { + //first get all the brush selections + var brushSelections = {}; + g.selectAll('.brush').each(function(d) { + brushSelections[d] = d3.select(this); + }); + + // loop over each dimension and update appropriately (if it was passed in through extents) + d3.keys(__.dimensions).forEach(function(d) { + if (extents[d] === undefined) { + return; + } + + var brush = brushes[d]; + if (brush !== undefined) { + //update the extent + brush.extent(extents[d]); + + //redraw the brush + brushSelections[d] + .transition() + .duration(0) + .call(brush); + + //fire some events + brush.event(brushSelections[d]); + } + }); + + //redraw the chart + pc.renderBrushed(); + + return pc; + } + } + + function brushFor(axis) { + var brush = d3.svg.brush(); + + brush + .y(__.dimensions[axis].yscale) + .on('brushstart', function() { + if (d3.event.sourceEvent !== null) { + events.brushstart.call(pc, __.brushed); + d3.event.sourceEvent.stopPropagation(); + } + }) + .on('brush', function() { + brushUpdated(selected()); + }) + .on('brushend', function() { + events.brushend.call(pc, __.brushed); + }); + + brushes[axis] = brush; + return brush; + } + + function brushReset(dimension) { + if (dimension === undefined) { + __.brushed = false; + if (g) { + g.selectAll('.brush').each(function(d) { + d3.select(this) + .transition() + .duration(0) + .call(brushes[d].clear()); + }); + pc.renderBrushed(); + } + } else { + if (g) { + g.selectAll('.brush').each(function(d) { + if (d != dimension) return; + d3.select(this) + .transition() + .duration(0) + .call(brushes[d].clear()); + brushes[d].event(d3.select(this)); + }); + pc.renderBrushed(); + } + } + return this; + } + + function install() { + if (!g) pc.createAxes(); + + // Add and store a brush for each axis. + var brush = g + .append('svg:g') + .attr('class', 'brush') + .each(function(d) { + d3.select(this).call(brushFor(d)); + }); + + brush + .selectAll('rect') + .style('visibility', null) + .attr('x', -15) + .attr('width', 30); + + brush.selectAll('rect.background').style('fill', 'transparent'); + + brush + .selectAll('rect.extent') + .style('fill', 'rgba(255,255,255,0.25)') + .style('stroke', 'rgba(0,0,0,0.6)'); + + brush.selectAll('.resize rect').style('fill', 'rgba(0,0,0,0.1)'); + + pc.brushExtents = brushExtents; + pc.brushReset = brushReset; + return pc; + } + + brush.modes['1D-axes'] = { + install: install, + uninstall: function() { + g.selectAll('.brush').remove(); + brushes = {}; + delete pc.brushExtents; + delete pc.brushReset; + }, + selected: selected, + brushState: brushExtents, + }; + })(); + // brush mode: 2D-strums + // bl.ocks.org/syntagmatic/5441022 + + (function() { + var strums = {}, + strumRect; + + function drawStrum(strum, activePoint) { + var svg = pc.selection.select('svg').select('g#strums'), + id = strum.dims.i, + points = [strum.p1, strum.p2], + line = svg.selectAll('line#strum-' + id).data([strum]), + circles = svg.selectAll('circle#strum-' + id).data(points), + drag = d3.behavior.drag(); + + line + .enter() + .append('line') + .attr('id', 'strum-' + id) + .attr('class', 'strum'); + + line + .attr('x1', function(d) { + return d.p1[0]; + }) + .attr('y1', function(d) { + return d.p1[1]; + }) + .attr('x2', function(d) { + return d.p2[0]; + }) + .attr('y2', function(d) { + return d.p2[1]; + }) + .attr('stroke', 'black') + .attr('stroke-width', 2); + + drag + .on('drag', function(d, i) { + var ev = d3.event; + i = i + 1; + strum['p' + i][0] = Math.min( + Math.max(strum.minX + 1, ev.x), + strum.maxX, + ); + strum['p' + i][1] = Math.min(Math.max(strum.minY, ev.y), strum.maxY); + drawStrum(strum, i - 1); + }) + .on('dragend', onDragEnd()); + + circles + .enter() + .append('circle') + .attr('id', 'strum-' + id) + .attr('class', 'strum'); + + circles + .attr('cx', function(d) { + return d[0]; + }) + .attr('cy', function(d) { + return d[1]; + }) + .attr('r', 5) + .style('opacity', function(d, i) { + return activePoint !== undefined && i === activePoint ? 0.8 : 0; + }) + .on('mouseover', function() { + d3.select(this).style('opacity', 0.8); + }) + .on('mouseout', function() { + d3.select(this).style('opacity', 0); + }) + .call(drag); + } + + function dimensionsForPoint(p) { + var dims = { i: -1, left: undefined, right: undefined }; + d3.keys(__.dimensions).some(function(dim, i) { + if (xscale(dim) < p[0]) { + var next = d3.keys(__.dimensions)[ + pc.getOrderedDimensionKeys().indexOf(dim) + 1 + ]; + dims.i = i; + dims.left = dim; + dims.right = next; + return false; + } + return true; + }); + + if (dims.left === undefined) { + // Event on the left side of the first axis. + dims.i = 0; + dims.left = pc.getOrderedDimensionKeys()[0]; + dims.right = pc.getOrderedDimensionKeys()[1]; + } else if (dims.right === undefined) { + // Event on the right side of the last axis + dims.i = d3.keys(__.dimensions).length - 1; + dims.right = dims.left; + dims.left = pc.getOrderedDimensionKeys()[ + d3.keys(__.dimensions).length - 2 + ]; + } + + return dims; + } + + function onDragStart() { + // First we need to determine between which two axes the sturm was started. + // This will determine the freedom of movement, because a strum can + // logically only happen between two axes, so no movement outside these axes + // should be allowed. + return function() { + var p = d3.mouse(strumRect[0][0]), + dims, + strum; + + p[0] = p[0] - __.margin.left; + p[1] = p[1] - __.margin.top; + + (dims = dimensionsForPoint(p)), + (strum = { + p1: p, + dims: dims, + minX: xscale(dims.left), + maxX: xscale(dims.right), + minY: 0, + maxY: h(), + }); + + strums[dims.i] = strum; + strums.active = dims.i; + + // Make sure that the point is within the bounds + strum.p1[0] = Math.min(Math.max(strum.minX, p[0]), strum.maxX); + strum.p2 = strum.p1.slice(); + }; + } + + function onDrag() { + return function() { + var ev = d3.event, + strum = strums[strums.active]; + + // Make sure that the point is within the bounds + strum.p2[0] = Math.min( + Math.max(strum.minX + 1, ev.x - __.margin.left), + strum.maxX, + ); + strum.p2[1] = Math.min( + Math.max(strum.minY, ev.y - __.margin.top), + strum.maxY, + ); + drawStrum(strum, 1); + }; + } + + function containmentTest(strum, width) { + var p1 = [strum.p1[0] - strum.minX, strum.p1[1] - strum.minX], + p2 = [strum.p2[0] - strum.minX, strum.p2[1] - strum.minX], + m1 = 1 - width / p1[0], + b1 = p1[1] * (1 - m1), + m2 = 1 - width / p2[0], + b2 = p2[1] * (1 - m2); + + // test if point falls between lines + return function(p) { + var x = p[0], + y = p[1], + y1 = m1 * x + b1, + y2 = m2 * x + b2; + + if (y > Math.min(y1, y2) && y < Math.max(y1, y2)) { + return true; + } + + return false; + }; + } + + function selected() { + var ids = Object.getOwnPropertyNames(strums), + brushed = __.data; + + // Get the ids of the currently active strums. + ids = ids.filter(function(d) { + return !isNaN(d); + }); + + function crossesStrum(d, id) { + var strum = strums[id], + test = containmentTest(strum, strums.width(id)), + d1 = strum.dims.left, + d2 = strum.dims.right, + y1 = __.dimensions[d1].yscale, + y2 = __.dimensions[d2].yscale, + point = [y1(d[d1]) - strum.minX, y2(d[d2]) - strum.minX]; + return test(point); + } + + if (ids.length === 0) { + return brushed; + } + + return brushed.filter(function(d) { + switch (brush.predicate) { + case 'AND': + return ids.every(function(id) { + return crossesStrum(d, id); + }); + case 'OR': + return ids.some(function(id) { + return crossesStrum(d, id); + }); + default: + throw 'Unknown brush predicate ' + __.brushPredicate; + } + }); + } + + function removeStrum() { + var strum = strums[strums.active], + svg = pc.selection.select('svg').select('g#strums'); + + delete strums[strums.active]; + strums.active = undefined; + svg.selectAll('line#strum-' + strum.dims.i).remove(); + svg.selectAll('circle#strum-' + strum.dims.i).remove(); + } + + function onDragEnd() { + return function() { + var brushed = __.data, + strum = strums[strums.active]; + + // Okay, somewhat unexpected, but not totally unsurprising, a mousclick is + // considered a drag without move. So we have to deal with that case + if ( + strum && + strum.p1[0] === strum.p2[0] && + strum.p1[1] === strum.p2[1] + ) { + removeStrum(strums); + } + + brushed = selected(strums); + strums.active = undefined; + __.brushed = brushed; + pc.renderBrushed(); + events.brushend.call(pc, __.brushed); + }; + } + + function brushReset(strums) { + return function() { + var ids = Object.getOwnPropertyNames(strums).filter(function(d) { + return !isNaN(d); + }); + + ids.forEach(function(d) { + strums.active = d; + removeStrum(strums); + }); + onDragEnd(strums)(); + }; + } + + function install() { + var drag = d3.behavior.drag(); + + // Map of current strums. Strums are stored per segment of the PC. A segment, + // being the area between two axes. The left most area is indexed at 0. + strums.active = undefined; + // Returns the width of the PC segment where currently a strum is being + // placed. NOTE: even though they are evenly spaced in our current + // implementation, we keep for when non-even spaced segments are supported as + // well. + strums.width = function(id) { + var strum = strums[id]; + + if (strum === undefined) { + return undefined; + } + + return strum.maxX - strum.minX; + }; + + pc.on('axesreorder.strums', function() { + var ids = Object.getOwnPropertyNames(strums).filter(function(d) { + return !isNaN(d); + }); + + // Checks if the first dimension is directly left of the second dimension. + function consecutive(first, second) { + var length = d3.keys(__.dimensions).length; + return d3.keys(__.dimensions).some(function(d, i) { + return d === first + ? i + i < length && __.dimensions[i + 1] === second + : false; + }); + } + + if (ids.length > 0) { + // We have some strums, which might need to be removed. + ids.forEach(function(d) { + var dims = strums[d].dims; + strums.active = d; + // If the two dimensions of the current strum are not next to each other + // any more, than we'll need to remove the strum. Otherwise we keep it. + if (!consecutive(dims.left, dims.right)) { + removeStrum(strums); + } + }); + onDragEnd(strums)(); + } + }); + + // Add a new svg group in which we draw the strums. + pc.selection + .select('svg') + .append('g') + .attr('id', 'strums') + .attr( + 'transform', + 'translate(' + __.margin.left + ',' + __.margin.top + ')', + ); + + // Install the required brushReset function + pc.brushReset = brushReset(strums); + + drag + .on('dragstart', onDragStart(strums)) + .on('drag', onDrag(strums)) + .on('dragend', onDragEnd(strums)); + + // NOTE: The styling needs to be done here and not in the css. This is because + // for 1D brushing, the canvas layers should not listen to + // pointer-events. + strumRect = pc.selection + .select('svg') + .insert('rect', 'g#strums') + .attr('id', 'strum-events') + .attr('x', __.margin.left) + .attr('y', __.margin.top) + .attr('width', w()) + .attr('height', h() + 2) + .style('opacity', 0) + .call(drag); + } + + brush.modes['2D-strums'] = { + install: install, + uninstall: function() { + pc.selection + .select('svg') + .select('g#strums') + .remove(); + pc.selection + .select('svg') + .select('rect#strum-events') + .remove(); + pc.on('axesreorder.strums', undefined); + delete pc.brushReset; + + strumRect = undefined; + }, + selected: selected, + brushState: function() { + return strums; + }, + }; + })(); + + // brush mode: 1D-Axes with multiple extents + // requires d3.svg.multibrush + + (function() { + if (typeof d3.svg.multibrush !== 'function') { + return; + } + var brushes = {}; + + function is_brushed(p) { + return !brushes[p].empty(); + } + + // data within extents + function selected() { + var actives = d3.keys(__.dimensions).filter(is_brushed), + extents = actives.map(function(p) { + return brushes[p].extent(); + }); + + // We don't want to return the full data set when there are no axes brushed. + // Actually, when there are no axes brushed, by definition, no items are + // selected. So, let's avoid the filtering and just return false. + //if (actives.length === 0) return false; + + // Resolves broken examples for now. They expect to get the full dataset back from empty brushes + if (actives.length === 0) return __.data; + + // test if within range + var within = { + date: function(d, p, dimension, b) { + if (typeof __.dimensions[p].yscale.rangePoints === 'function') { + // if it is ordinal + return ( + b[0] <= __.dimensions[p].yscale(d[p]) && + __.dimensions[p].yscale(d[p]) <= b[1] + ); + } else { + return b[0] <= d[p] && d[p] <= b[1]; + } + }, + number: function(d, p, dimension, b) { + if (typeof __.dimensions[p].yscale.rangePoints === 'function') { + // if it is ordinal + return ( + b[0] <= __.dimensions[p].yscale(d[p]) && + __.dimensions[p].yscale(d[p]) <= b[1] + ); + } else { + return b[0] <= d[p] && d[p] <= b[1]; + } + }, + string: function(d, p, dimension, b) { + return ( + b[0] <= __.dimensions[p].yscale(d[p]) && + __.dimensions[p].yscale(d[p]) <= b[1] + ); + }, + }; + + return __.data.filter(function(d) { + switch (brush.predicate) { + case 'AND': + return actives.every(function(p, dimension) { + return extents[dimension].some(function(b) { + return within[__.dimensions[p].type](d, p, dimension, b); + }); + }); + case 'OR': + return actives.some(function(p, dimension) { + return extents[dimension].some(function(b) { + return within[__.dimensions[p].type](d, p, dimension, b); + }); + }); + default: + throw 'Unknown brush predicate ' + __.brushPredicate; + } + }); + } + + function brushExtents(extents) { + if (typeof extents === 'undefined') { + extents = {}; + d3.keys(__.dimensions).forEach(function(d) { + var brush = brushes[d]; + if (brush !== undefined && !brush.empty()) { + var extent = brush.extent(); + extents[d] = extent; + } + }); + return extents; + } else { + //first get all the brush selections + var brushSelections = {}; + g.selectAll('.brush').each(function(d) { + brushSelections[d] = d3.select(this); + }); + + // loop over each dimension and update appropriately (if it was passed in through extents) + d3.keys(__.dimensions).forEach(function(d) { + if (extents[d] === undefined) { + return; + } + + var brush = brushes[d]; + if (brush !== undefined) { + //update the extent + brush.extent(extents[d]); + + //redraw the brush + brushSelections[d] + .transition() + .duration(0) + .call(brush); + + //fire some events + brush.event(brushSelections[d]); + } + }); + + //redraw the chart + pc.renderBrushed(); + + return pc; + } + } + + //function brushExtents() { + // var extents = {}; + // d3.keys(__.dimensions).forEach(function(d) { + // var brush = brushes[d]; + // if (brush !== undefined && !brush.empty()) { + // var extent = brush.extent(); + // extents[d] = extent; + // } + // }); + // return extents; + //} + + function brushFor(axis) { + var brush = d3.svg.multibrush(); + + brush + .y(__.dimensions[axis].yscale) + .on('brushstart', function() { + if (d3.event.sourceEvent !== null) { + events.brushstart.call(pc, __.brushed); + d3.event.sourceEvent.stopPropagation(); + } + }) + .on('brush', function() { + brushUpdated(selected()); + }) + .on('brushend', function() { + // d3.svg.multibrush clears extents just before calling 'brushend' + // so we have to update here again. + // This fixes issue #103 for now, but should be changed in d3.svg.multibrush + // to avoid unnecessary computation. + brushUpdated(selected()); + events.brushend.call(pc, __.brushed); + }) + .extentAdaption(function(selection) { + selection + .style('visibility', null) + .attr('x', -15) + .attr('width', 30) + .style('fill', 'rgba(255,255,255,0.25)') + .style('stroke', 'rgba(0,0,0,0.6)'); + }) + .resizeAdaption(function(selection) { + selection + .selectAll('rect') + .attr('x', -15) + .attr('width', 30) + .style('visibility', null) + .style('fill', 'rgba(0,0,0,0.1)'); + }); + + brushes[axis] = brush; + return brush; + } + + function brushReset(dimension) { + __.brushed = false; + if (g) { + g.selectAll('.brush').each(function(d) { + d3.select(this).call(brushes[d].clear()); + }); + pc.renderBrushed(); + } + return this; + } + + function install() { + if (!g) pc.createAxes(); + + // Add and store a brush for each axis. + var brush = g + .append('svg:g') + .attr('class', 'brush') + .each(function(d) { + d3.select(this).call(brushFor(d)); + }); + + brush + .selectAll('rect') + .style('visibility', null) + .attr('x', -15) + .attr('width', 30); + + brush.selectAll('rect.background').style('fill', 'transparent'); + + brush + .selectAll('rect.extent') + .style('fill', 'rgba(255,255,255,0.25)') + .style('stroke', 'rgba(0,0,0,0.6)'); + + brush.selectAll('.resize rect').style('fill', 'rgba(0,0,0,0.1)'); + + pc.brushExtents = brushExtents; + pc.brushReset = brushReset; + return pc; + } + + brush.modes['1D-axes-multi'] = { + install: install, + uninstall: function() { + g.selectAll('.brush').remove(); + brushes = {}; + delete pc.brushExtents; + delete pc.brushReset; + }, + selected: selected, + brushState: brushExtents, + }; + })(); + // brush mode: angular + // code based on 2D.strums.js + + (function() { + var arcs = {}, + strumRect; + + function drawStrum(arc, activePoint) { + var svg = pc.selection.select('svg').select('g#arcs'), + id = arc.dims.i, + points = [arc.p2, arc.p3], + line = svg.selectAll('line#arc-' + id).data([ + { p1: arc.p1, p2: arc.p2 }, + { p1: arc.p1, p2: arc.p3 }, + ]), + circles = svg.selectAll('circle#arc-' + id).data(points), + drag = d3.behavior.drag(), + path = svg.selectAll('path#arc-' + id).data([arc]); + + path + .enter() + .append('path') + .attr('id', 'arc-' + id) + .attr('class', 'arc') + .style('fill', 'orange') + .style('opacity', 0.5); + + path + .attr('d', arc.arc) + .attr('transform', 'translate(' + arc.p1[0] + ',' + arc.p1[1] + ')'); + + line + .enter() + .append('line') + .attr('id', 'arc-' + id) + .attr('class', 'arc'); + + line + .attr('x1', function(d) { + return d.p1[0]; + }) + .attr('y1', function(d) { + return d.p1[1]; + }) + .attr('x2', function(d) { + return d.p2[0]; + }) + .attr('y2', function(d) { + return d.p2[1]; + }) + .attr('stroke', 'black') + .attr('stroke-width', 2); + + drag + .on('drag', function(d, i) { + var ev = d3.event, + angle = 0; + + i = i + 2; + + arc['p' + i][0] = Math.min(Math.max(arc.minX + 1, ev.x), arc.maxX); + arc['p' + i][1] = Math.min(Math.max(arc.minY, ev.y), arc.maxY); + + angle = i === 3 ? arcs.startAngle(id) : arcs.endAngle(id); + + if ( + (arc.startAngle < Math.PI && + arc.endAngle < Math.PI && + angle < Math.PI) || + (arc.startAngle >= Math.PI && + arc.endAngle >= Math.PI && + angle >= Math.PI) + ) { + if (i === 2) { + arc.endAngle = angle; + arc.arc.endAngle(angle); + } else if (i === 3) { + arc.startAngle = angle; + arc.arc.startAngle(angle); + } + } + + drawStrum(arc, i - 2); + }) + .on('dragend', onDragEnd()); + + circles + .enter() + .append('circle') + .attr('id', 'arc-' + id) + .attr('class', 'arc'); + + circles + .attr('cx', function(d) { + return d[0]; + }) + .attr('cy', function(d) { + return d[1]; + }) + .attr('r', 5) + .style('opacity', function(d, i) { + return activePoint !== undefined && i === activePoint ? 0.8 : 0; + }) + .on('mouseover', function() { + d3.select(this).style('opacity', 0.8); + }) + .on('mouseout', function() { + d3.select(this).style('opacity', 0); + }) + .call(drag); + } + + function dimensionsForPoint(p) { + var dims = { i: -1, left: undefined, right: undefined }; + d3.keys(__.dimensions).some(function(dim, i) { + if (xscale(dim) < p[0]) { + var next = d3.keys(__.dimensions)[ + pc.getOrderedDimensionKeys().indexOf(dim) + 1 + ]; + dims.i = i; + dims.left = dim; + dims.right = next; + return false; + } + return true; + }); + + if (dims.left === undefined) { + // Event on the left side of the first axis. + dims.i = 0; + dims.left = pc.getOrderedDimensionKeys()[0]; + dims.right = pc.getOrderedDimensionKeys()[1]; + } else if (dims.right === undefined) { + // Event on the right side of the last axis + dims.i = d3.keys(__.dimensions).length - 1; + dims.right = dims.left; + dims.left = pc.getOrderedDimensionKeys()[ + d3.keys(__.dimensions).length - 2 + ]; + } + + return dims; + } + + function onDragStart() { + // First we need to determine between which two axes the arc was started. + // This will determine the freedom of movement, because a arc can + // logically only happen between two axes, so no movement outside these axes + // should be allowed. + return function() { + var p = d3.mouse(strumRect[0][0]), + dims, + arc; + + p[0] = p[0] - __.margin.left; + p[1] = p[1] - __.margin.top; + + (dims = dimensionsForPoint(p)), + (arc = { + p1: p, + dims: dims, + minX: xscale(dims.left), + maxX: xscale(dims.right), + minY: 0, + maxY: h(), + startAngle: undefined, + endAngle: undefined, + arc: d3.svg.arc().innerRadius(0), + }); + + arcs[dims.i] = arc; + arcs.active = dims.i; + + // Make sure that the point is within the bounds + arc.p1[0] = Math.min(Math.max(arc.minX, p[0]), arc.maxX); + arc.p2 = arc.p1.slice(); + arc.p3 = arc.p1.slice(); + }; + } + + function onDrag() { + return function() { + var ev = d3.event, + arc = arcs[arcs.active]; + + // Make sure that the point is within the bounds + arc.p2[0] = Math.min( + Math.max(arc.minX + 1, ev.x - __.margin.left), + arc.maxX, + ); + arc.p2[1] = Math.min( + Math.max(arc.minY, ev.y - __.margin.top), + arc.maxY, + ); + arc.p3 = arc.p2.slice(); + // console.log(arcs.angle(arcs.active)); + // console.log(signedAngle(arcs.unsignedAngle(arcs.active))); + drawStrum(arc, 1); + }; + } + + // some helper functions + function hypothenuse(a, b) { + return Math.sqrt(a * a + b * b); + } + + var rad = (function() { + var c = Math.PI / 180; + return function(angle) { + return angle * c; + }; + })(); + + var deg = (function() { + var c = 180 / Math.PI; + return function(angle) { + return angle * c; + }; + })(); + + // [0, 2*PI] -> [-PI/2, PI/2] + var signedAngle = function(angle) { + var ret = angle; + if (angle > Math.PI) { + ret = angle - 1.5 * Math.PI; + ret = angle - 1.5 * Math.PI; + } else { + ret = angle - 0.5 * Math.PI; + ret = angle - 0.5 * Math.PI; + } + return -ret; + }; + + /** + * angles are stored in radians from in [0, 2*PI], where 0 in 12 o'clock. + * However, one can only select lines from 0 to PI, so we compute the + * 'signed' angle, where 0 is the horizontal line (3 o'clock), and +/- PI/2 + * are 12 and 6 o'clock respectively. + */ + function containmentTest(arc) { + var startAngle = signedAngle(arc.startAngle); + var endAngle = signedAngle(arc.endAngle); + + if (startAngle > endAngle) { + var tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + // test if segment angle is contained in angle interval + return function(a) { + if (a >= startAngle && a <= endAngle) { + return true; + } + + return false; + }; + } + + function selected() { + var ids = Object.getOwnPropertyNames(arcs), + brushed = __.data; + + // Get the ids of the currently active arcs. + ids = ids.filter(function(d) { + return !isNaN(d); + }); + + function crossesStrum(d, id) { + var arc = arcs[id], + test = containmentTest(arc), + d1 = arc.dims.left, + d2 = arc.dims.right, + y1 = __.dimensions[d1].yscale, + y2 = __.dimensions[d2].yscale, + a = arcs.width(id), + b = y1(d[d1]) - y2(d[d2]), + c = hypothenuse(a, b), + angle = Math.asin(b / c); // rad in [-PI/2, PI/2] + return test(angle); + } + + if (ids.length === 0) { + return brushed; + } + + return brushed.filter(function(d) { + switch (brush.predicate) { + case 'AND': + return ids.every(function(id) { + return crossesStrum(d, id); + }); + case 'OR': + return ids.some(function(id) { + return crossesStrum(d, id); + }); + default: + throw 'Unknown brush predicate ' + __.brushPredicate; + } + }); + } + + function removeStrum() { + var arc = arcs[arcs.active], + svg = pc.selection.select('svg').select('g#arcs'); + + delete arcs[arcs.active]; + arcs.active = undefined; + svg.selectAll('line#arc-' + arc.dims.i).remove(); + svg.selectAll('circle#arc-' + arc.dims.i).remove(); + svg.selectAll('path#arc-' + arc.dims.i).remove(); + } + + function onDragEnd() { + return function() { + var brushed = __.data, + arc = arcs[arcs.active]; + + // Okay, somewhat unexpected, but not totally unsurprising, a mousclick is + // considered a drag without move. So we have to deal with that case + if (arc && arc.p1[0] === arc.p2[0] && arc.p1[1] === arc.p2[1]) { + removeStrum(arcs); + } + + if (arc) { + var angle = arcs.startAngle(arcs.active); + + arc.startAngle = angle; + arc.endAngle = angle; + arc.arc + .outerRadius(arcs.length(arcs.active)) + .startAngle(angle) + .endAngle(angle); + } + + brushed = selected(arcs); + arcs.active = undefined; + __.brushed = brushed; + pc.renderBrushed(); + events.brushend.call(pc, __.brushed); + }; + } + + function brushReset(arcs) { + return function() { + var ids = Object.getOwnPropertyNames(arcs).filter(function(d) { + return !isNaN(d); + }); + + ids.forEach(function(d) { + arcs.active = d; + removeStrum(arcs); + }); + onDragEnd(arcs)(); + }; + } + + function install() { + var drag = d3.behavior.drag(); + + // Map of current arcs. arcs are stored per segment of the PC. A segment, + // being the area between two axes. The left most area is indexed at 0. + arcs.active = undefined; + // Returns the width of the PC segment where currently a arc is being + // placed. NOTE: even though they are evenly spaced in our current + // implementation, we keep for when non-even spaced segments are supported as + // well. + arcs.width = function(id) { + var arc = arcs[id]; + + if (arc === undefined) { + return undefined; + } + + return arc.maxX - arc.minX; + }; + + // returns angles in [-PI/2, PI/2] + angle = function(p1, p2) { + var a = p1[0] - p2[0], + b = p1[1] - p2[1], + c = hypothenuse(a, b); + + return Math.asin(b / c); + }; + + // returns angles in [0, 2 * PI] + arcs.endAngle = function(id) { + var arc = arcs[id]; + if (arc === undefined) { + return undefined; + } + var sAngle = angle(arc.p1, arc.p2), + uAngle = -sAngle + Math.PI / 2; + + if (arc.p1[0] > arc.p2[0]) { + uAngle = 2 * Math.PI - uAngle; + } + + return uAngle; + }; + + arcs.startAngle = function(id) { + var arc = arcs[id]; + if (arc === undefined) { + return undefined; + } + + var sAngle = angle(arc.p1, arc.p3), + uAngle = -sAngle + Math.PI / 2; + + if (arc.p1[0] > arc.p3[0]) { + uAngle = 2 * Math.PI - uAngle; + } + + return uAngle; + }; + + arcs.length = function(id) { + var arc = arcs[id]; + + if (arc === undefined) { + return undefined; + } + + var a = arc.p1[0] - arc.p2[0], + b = arc.p1[1] - arc.p2[1], + c = hypothenuse(a, b); + + return c; + }; + + pc.on('axesreorder.arcs', function() { + var ids = Object.getOwnPropertyNames(arcs).filter(function(d) { + return !isNaN(d); + }); + + // Checks if the first dimension is directly left of the second dimension. + function consecutive(first, second) { + var length = d3.keys(__.dimensions).length; + return d3.keys(__.dimensions).some(function(d, i) { + return d === first + ? i + i < length && __.dimensions[i + 1] === second + : false; + }); + } + + if (ids.length > 0) { + // We have some arcs, which might need to be removed. + ids.forEach(function(d) { + var dims = arcs[d].dims; + arcs.active = d; + // If the two dimensions of the current arc are not next to each other + // any more, than we'll need to remove the arc. Otherwise we keep it. + if (!consecutive(dims.left, dims.right)) { + removeStrum(arcs); + } + }); + onDragEnd(arcs)(); + } + }); + + // Add a new svg group in which we draw the arcs. + pc.selection + .select('svg') + .append('g') + .attr('id', 'arcs') + .attr( + 'transform', + 'translate(' + __.margin.left + ',' + __.margin.top + ')', + ); + + // Install the required brushReset function + pc.brushReset = brushReset(arcs); + + drag + .on('dragstart', onDragStart(arcs)) + .on('drag', onDrag(arcs)) + .on('dragend', onDragEnd(arcs)); + + // NOTE: The styling needs to be done here and not in the css. This is because + // for 1D brushing, the canvas layers should not listen to + // pointer-events. + strumRect = pc.selection + .select('svg') + .insert('rect', 'g#arcs') + .attr('id', 'arc-events') + .attr('x', __.margin.left) + .attr('y', __.margin.top) + .attr('width', w()) + .attr('height', h() + 2) + .style('opacity', 0) + .call(drag); + } + + brush.modes['angular'] = { + install: install, + uninstall: function() { + pc.selection + .select('svg') + .select('g#arcs') + .remove(); + pc.selection + .select('svg') + .select('rect#arc-events') + .remove(); + pc.on('axesreorder.arcs', undefined); + delete pc.brushReset; + + strumRect = undefined; + }, + selected: selected, + brushState: function() { + return arcs; + }, + }; + })(); + pc.interactive = function() { + flags.interactive = true; + return this; + }; + + // expose a few objects + pc.xscale = xscale; + pc.ctx = ctx; + pc.canvas = canvas; + pc.g = function() { + return g; + }; + + // rescale for height, width and margins + // TODO currently assumes chart is brushable, and destroys old brushes + pc.resize = function() { + // selection size + pc.selection + .select('svg') + .attr('width', __.width) + .attr('height', __.height); + pc.svg.attr( + 'transform', + 'translate(' + __.margin.left + ',' + __.margin.top + ')', + ); + + // FIXME: the current brush state should pass through + if (flags.brushable) pc.brushReset(); + + // scales + pc.autoscale(); + + // axes, destroys old brushes. + if (g) pc.createAxes(); + if (flags.brushable) pc.brushable(); + if (flags.reorderable) pc.reorderable(); + + events.resize.call(this, { + width: __.width, + height: __.height, + margin: __.margin, + }); + return this; + }; + + // highlight an array of data + pc.highlight = function(data) { + if (arguments.length === 0) { + return __.highlighted; + } + + __.highlighted = data; + pc.clear('highlight'); + d3.selectAll([canvas.foreground, canvas.brushed]).classed('faded', true); + data.forEach(path_highlight); + events.highlight.call(this, data); + return this; + }; + + // clear highlighting + pc.unhighlight = function() { + __.highlighted = []; + pc.clear('highlight'); + d3.selectAll([canvas.foreground, canvas.brushed]).classed('faded', false); + return this; + }; + + // calculate 2d intersection of line a->b with line c->d + // points are objects with x and y properties + pc.intersection = function(a, b, c, d) { + return { + x: + ((a.x * b.y - a.y * b.x) * (c.x - d.x) - + (a.x - b.x) * (c.x * d.y - c.y * d.x)) / + ((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x)), + y: + ((a.x * b.y - a.y * b.x) * (c.y - d.y) - + (a.y - b.y) * (c.x * d.y - c.y * d.x)) / + ((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x)), + }; + }; + + function position(d) { + if (xscale.range().length === 0) { + xscale.rangePoints([0, w()], 1); + } + var v = dragging[d]; + return v == null ? xscale(d) : v; + } + + // Merges the canvases and SVG elements into one canvas element which is then passed into the callback + // (so you can choose to save it to disk, etc.) + pc.mergeParcoords = function(callback) { + // Retina display, etc. + var devicePixelRatio = window.devicePixelRatio || 1; + + // Create a canvas element to store the merged canvases + var mergedCanvas = document.createElement('canvas'); + mergedCanvas.width = pc.canvas.foreground.clientWidth * devicePixelRatio; + mergedCanvas.height = + (pc.canvas.foreground.clientHeight + 30) * devicePixelRatio; + mergedCanvas.style.width = mergedCanvas.width / devicePixelRatio + 'px'; + mergedCanvas.style.height = mergedCanvas.height / devicePixelRatio + 'px'; + + // Give the canvas a white background + var context = mergedCanvas.getContext('2d'); + context.fillStyle = '#ffffff'; + context.fillRect(0, 0, mergedCanvas.width, mergedCanvas.height); + + // Merge all the canvases + for (var key in pc.canvas) { + context.drawImage( + pc.canvas[key], + 0, + 24 * devicePixelRatio, + mergedCanvas.width, + mergedCanvas.height - 30 * devicePixelRatio, + ); + } + + // Add SVG elements to canvas + var DOMURL = window.URL || window.webkitURL || window; + var serializer = new XMLSerializer(); + var svgStr = serializer.serializeToString(pc.selection.select('svg')[0][0]); + + // Create a Data URI. + var src = 'data:image/svg+xml;base64,' + window.btoa(svgStr); + var img = new Image(); + img.onload = function() { + context.drawImage( + img, + 0, + 0, + img.width * devicePixelRatio, + img.height * devicePixelRatio, + ); + if (typeof callback === 'function') { + callback(mergedCanvas); + } + }; + img.src = src; + }; + pc.version = '0.7.0'; + // this descriptive text should live with other introspective methods + pc.toString = function() { + return ( + 'Parallel Coordinates: ' + + d3.keys(__.dimensions).length + + ' dimensions (' + + d3.keys(__.data[0]).length + + ' total) , ' + + __.data.length + + ' rows' + ); + }; + + return pc; +} + +d3.renderQueue = function(func) { + var _queue = [], // data to be rendered + _rate = 10, // number of calls per frame + _clear = function() {}, // clearing function + _i = 0; // current iteration + + var rq = function(data) { + if (data) rq.data(data); + rq.invalidate(); + _clear(); + rq.render(); + }; + + rq.render = function() { + _i = 0; + var valid = true; + rq.invalidate = function() { + valid = false; + }; + + function doFrame() { + if (!valid) return true; + if (_i > _queue.length) return true; + + // Typical d3 behavior is to pass a data item *and* its index. As the + // render queue splits the original data set, we'll have to be slightly + // more carefull about passing the correct index with the data item. + var end = Math.min(_i + _rate, _queue.length); + for (var i = _i; i < end; i++) { + func(_queue[i], i); + } + _i += _rate; + } + + d3.timer(doFrame); + }; + + rq.data = function(data) { + rq.invalidate(); + _queue = data.slice(0); + return rq; + }; + + rq.rate = function(value) { + if (!arguments.length) return _rate; + _rate = value; + return rq; + }; + + rq.remaining = function() { + return _queue.length - _i; + }; + + // clear the canvas + rq.clear = function(func) { + if (!arguments.length) { + _clear(); + return rq; + } + _clear = func; + return rq; + }; + + rq.invalidate = function() {}; + + return rq; +}; diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.html new file mode 100644 index 00000000000..98eeca11b8f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.html @@ -0,0 +1,7 @@ +
+
+ +
+ +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.scss new file mode 100644 index 00000000000..dd0a9a618db --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.scss @@ -0,0 +1,43 @@ +:host { + display: block; +} + +.graph-wrapper { + position: relative; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.d3-graph { + width: 400px; + + @media (min-width: 768px) { + width: 700px; + } + + @media (min-width: 1024px) { + width: 1000px; + } + + @media (min-width: 1400px) { + width: 1300px; + } + + @media (min-width: 1650px) { + width: 1600px; + } + + @media (min-width: 2000px) { + width: 1900px; + } + + height: 600px; +} + +.spinner { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.spec.ts new file mode 100644 index 00000000000..5ef07df0512 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TrialsGraphComponent } from './trials-graph.component'; + +describe('TrialsGraphComponent', () => { + let component: TrialsGraphComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TrialsGraphComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TrialsGraphComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.ts new file mode 100644 index 00000000000..684c49082df --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.component.ts @@ -0,0 +1,243 @@ +import { + ChangeDetectionStrategy, + Component, + Input, + OnChanges, + SimpleChanges, +} from '@angular/core'; +import lowerCase from 'lodash-es/lowerCase'; +import { safeDivision } from 'src/app/shared/utils'; + +import { parcoords } from './d3.parcoords'; +declare let d3: any; + +@Component({ + selector: 'app-trials-graph', + templateUrl: './trials-graph.component.html', + styleUrls: ['./trials-graph.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TrialsGraphComponent implements OnChanges { + dataLoaded = false; + graph: any; + + private data = []; + + @Input() + experimentTrialsCsv: string[] = []; + + @Input() + hoveredTrial: number; + + constructor() {} + + ngOnChanges(changes: SimpleChanges): void { + if (changes.experimentTrialsCsv && this.experimentTrialsCsv) { + this.data = d3.csv.parse(this.experimentTrialsCsv); + + this.data.forEach(trial => { + delete trial['KFP Run']; + Object.keys(trial).forEach(key => !trial[key] && (trial[key] = 0)); + }); + + this.createOrUpdateGraph(); + } + + if (!changes.hoveredTrial || !this.graph) { + return; + } + + // highlight row in chart + if (isNaN(this.hoveredTrial) || this.hoveredTrial === null) { + this.graph.unhighlight(); + return; + } + + const d = this.graph.brushed() || this.data; + if (d[this.hoveredTrial]) { + this.graph.highlight([d[this.hoveredTrial]]); + } + } + + private createOrUpdateGraph() { + if (this.data.length && this.data.length > 0) { + if (!this.graph) { + this.createGraph(this.data); + this.dataLoaded = true; + return; + } + + this.graph + .dimensions({}) + .data([]) + .render(); + + const dimensions = this.buildDimensions(this.data); + this.graph + .data(this.data) + .hideAxis(['trialName', 'Status', 'id']) + .dimensions(dimensions) + .render() + .updateAxes(); + } + } + + private createGraph(data) { + const firstMetric = Object.keys(data[0])[2]; + data.forEach((trial, index) => (trial.id = index)); + + // color scale for zscores + const zcolorscale = d3.scale + .linear() + .domain([-1, -0.6, -0.3, 0, 0.3, 0.6, 1]) + .range([ + '#0e0787', + '#5e01a6', + '#9b169e', + '#c9447a', + '#ea7456', + '#fba238', + '#fccc25', + ]) + .interpolate(d3.interpolateLab); + + this.graph = parcoords()('#trial-graph').alpha(0.6); + + const dimensions = this.buildDimensions(data); + + this.graph + .data(data) + .hideAxis(['trialName', 'Status', 'id']) + .composite('darken') + .smoothness(0.1) + .bundlingStrength(0) // set bundling strength + .dimensions(dimensions) + .render() + .shadows() + .brushMode('1D-axes') // enable brushing + .interactive() // command line mode + .reorderable(); + + this.changeColor(this.graph, zcolorscale, firstMetric); + + // click label to activate coloring + this.graph.svg + .selectAll('.dimension') + .on('click', this.changeColor.bind(this, this.graph, zcolorscale)) + .selectAll('.label') + .style('font-size', '14px'); + } + + // update color + private changeColor(graph, zcolorscale, dimension) { + graph.svg + .selectAll('.dimension') + .style('font-weight', 'normal') + .filter(d => d === dimension) + .style('font-weight', 'bold'); + + graph.color(this.zcolor(zcolorscale, graph.data(), dimension)).render(); + } + + // return color function based on plot and dimension + private mean(values: number[]) { + let sum = 0; + values.forEach(v => (sum += v)); + return sum / values.length; + } + + private stdDeviation(values: number[]) { + const m = this.mean(values); + return Math.sqrt( + values.reduce((sq, n) => { + return sq + Math.pow(n - m, 2); + }, 0) / + (values.length - 1), + ); + } + + private zcolor(zcolorscale, col: any[], dimension) { + const scores = col.map(trial => trial[dimension]); + const z = this.zscore(scores.map(parseFloat)); + return d => zcolorscale(z(d[dimension])); + } + + // color by zscore + private zscore(col) { + const mean = this.mean(col); + const sigma = this.stdDeviation(col); + return d => (d - mean) / sigma; + } + + private buildDimensions(data) { + const range = + this.graph.height() - + this.graph.margin().top - + this.graph.margin().bottom; + + const dimensions = {}; + Object.keys(data[0]) + .filter(key => key !== 'trialName' && key !== 'Status' && key !== 'id') + .forEach(dimension => { + const min = d3.min(data, d => { + const value = +d[dimension]; + if (isNaN(value)) { + return; + } + + return value; + }); + const max = d3.max(data, d => { + const value = +d[dimension]; + if (isNaN(value)) { + return; + } + + return value; + }); + + if (!min && !max) { + dimensions[dimension] = { + title: dimension, + type: 'string', + }; + + return; + } + + const log = d3.scale + .linear() + .domain([ + min - Math.abs(safeDivision(min, 15)), + max + Math.abs(safeDivision(max, 15)), + ]) + .range([range, 1]); + + const minMaxLength = min.toString().length; + const average = safeDivision(min + max, 2); + + const tickValues = [ + min, + +safeDivision(min + average, 2) + .toString() + .slice(0, minMaxLength), + +average.toString().slice(0, minMaxLength), + +safeDivision(max + average, 2) + .toString() + .slice(0, minMaxLength), + max, + ].filter((value, index, array) => value !== array[index - 1]); + + dimensions[dimension] = { + title: lowerCase(dimension), + yscale: log, + innerTickSize: 5, + outerTickSize: 8, + type: 'number', + tickValues, + }; + }); + + return dimensions; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.module.ts new file mode 100644 index 00000000000..9b956abc638 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-graph/trials-graph.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { TrialsGraphComponent } from './trials-graph.component'; + +@NgModule({ + declarations: [TrialsGraphComponent], + imports: [CommonModule, MatProgressSpinnerModule], + exports: [TrialsGraphComponent], +}) +export class TrialsGraphModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.html new file mode 100644 index 00000000000..dfbca18632b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.html @@ -0,0 +1,31 @@ + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.scss new file mode 100644 index 00000000000..1a6cc1697ac --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.scss @@ -0,0 +1,21 @@ +.experiment-title { + font-weight: 400; + font-size: 20px; +} + +.modal { + display: flex; + flex-direction: column; + align-items: center; +} + +.title { + text-align: center; + margin-bottom: 16px; +} + +.loader { + display: flex; + flex: 1; + align-items: center; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.spec.ts new file mode 100644 index 00000000000..0039c089ba0 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TrialModalComponent } from './trial-modal.component'; + +describe('TrialModalComponent', () => { + let component: TrialModalComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TrialModalComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TrialModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.ts new file mode 100644 index 00000000000..fc35fa5c0f7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-modal/trial-modal.component.ts @@ -0,0 +1,104 @@ +import { Component, OnInit } from '@angular/core'; +import { curveLinear } from 'd3-shape'; +import { KWABackendService } from 'src/app/services/backend.service'; +import { transformStringResponses } from 'src/app/shared/utils'; + +interface ChartPoint { + name: string; + series: { + name: any; + value: number; + }[]; +} + +@Component({ + selector: 'app-trial-modal', + templateUrl: './trial-modal.component.html', + styleUrls: ['./trial-modal.component.scss'], +}) +export class TrialModalComponent implements OnInit { + trialName: string; + namespace: string; + dataLoaded: boolean; + + // chart's options + view = [700, 500]; + legend = true; + legendTitle = ''; + animations = true; + xAxis = true; + yAxis = true; + showYAxisLabel = true; + showXAxisLabel = true; + xAxisLabel = 'Datetime'; + yAxisLabel = 'Value'; + timeline = true; + chartData: ChartPoint[] = []; + curve = curveLinear; + yScaleMax = 0; + yScaleMin = 1; + + constructor(private backendService: KWABackendService) {} + + ngOnInit() { + this.backendService + .getTrial(this.trialName, this.namespace) + .subscribe(response => { + const { types, details } = transformStringResponses(response); + const nameIndex = types.findIndex(type => type === 'Metric name'); + const timeIndex = types.findIndex(type => type === 'Time'); + const valueIndex = types.findIndex(type => type === 'Value'); + + details.forEach(detail => { + const name = detail[nameIndex]; + const value = +detail[valueIndex]; + const time = new Date(detail[timeIndex]); + + // figure out the min-max values in y-axis + if (value > this.yScaleMax) { + this.yScaleMax = value; + } else { + this.yScaleMin = value; + } + + if (this.chartData.find(chart => chart.name === name)) { + // chart has already some points, append current one + const index = this.chartData.findIndex( + chart => chart.name === name, + ); + + this.chartData[index].series.push({ + //name: formattedDate, + name: time, + value, + }); + } else { + // first point of the chart + this.chartData.push({ + name, + series: [{ name: time, value }], + }); + } + }); + + this.yScaleMin = Math.floor(this.yScaleMin * 10) / 10; + this.yScaleMax = Math.ceil(this.yScaleMax * 10) / 10; + this.dataLoaded = true; + }); + } + + public xAxisFormat(time: Date) { + function zeroPad(n: number): string { + if (n < 10) { + return `0${n}`; + } + + return n.toString(); + } + + const hours = zeroPad(time.getHours()); + const minutes = zeroPad(time.getMinutes()); + const seconds = zeroPad(time.getSeconds()); + return `${hours}:${minutes}:${seconds}`; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.html new file mode 100644 index 00000000000..74890613f74 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.html @@ -0,0 +1,64 @@ + + + + + + + + + + + + > + +
+ {{ column === 'Kfp run' ? '' : column }} + + {{ element[i] }} + + + {{ element[i] }} + + +
+ +
+ + {{ element[i] }} +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.scss new file mode 100644 index 00000000000..3e21c31fa0f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.scss @@ -0,0 +1,65 @@ +:host { + display: block; + min-height: 550px; +} + +.cell { + width: 300px; +} + +.name { + cursor: pointer; + + &:hover { + color: #1e88e5; + text-decoration: underline; + } +} + +.green { + color: green; +} + +.red { + color: red; +} + +.icon { + cursor: pointer; + + &-disable { + cursor: default; + opacity: 0.5; + } +} + +td.mat-cell:not(:first-of-type) { + padding: 2px 28px 2px 0; +} + +td.mat-cell:first-of-type { + padding: 2px 28px 2px 24px; +} + +.mat-row:hover { + background-color: #f3f3f3; +} + +.svg-color { + color: #1e88e5; + display: flex; + justify-content: flex-end; + margin-right: 16px; +} + +::ng-deep :host svg { + fill: currentColor; +} + +.best-trail-row { + background-color: #ffffcd; + + &:hover { + background-color: #ffff98; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.spec.ts new file mode 100644 index 00000000000..4327740d765 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TrialsTableComponent } from './trials-table.component'; + +describe('TrialsTableComponent', () => { + let component: TrialsTableComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TrialsTableComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TrialsTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.ts new file mode 100644 index 00000000000..1dd1224e147 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.component.ts @@ -0,0 +1,78 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnChanges, + Output, + SimpleChanges, +} from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { TrialModalComponent } from './trial-modal/trial-modal.component'; + +@Component({ + selector: 'app-trials-table', + templateUrl: './trials-table.component.html', + styleUrls: ['./trials-table.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TrialsTableComponent implements OnChanges { + @Input() + displayedColumns = []; + + @Input() + data = []; + + @Input() + namespace: string; + + @Input() + bestTrialName: string; + + @Output() + mouseOnTrial = new EventEmitter(); + + @Output() + leaveMouseFromTrial = new EventEmitter(); + + bestTrialIndex: number; + + constructor(public dialog: MatDialog, private router: Router) {} + + ngOnChanges(changes: SimpleChanges): void { + if (changes.displayedColumns) { + this.displayedColumns = this.displayedColumns.slice( + 0, + this.displayedColumns.length, + ); + } + + if (this.data.length > 0 && this.bestTrialName) { + this.bestTrialIndex = this.data.findIndex( + trial => trial[0] === this.bestTrialName, + ); + } + } + + openTrialModal(name: string) { + const modalRef = this.dialog.open(TrialModalComponent, {}); + modalRef.componentInstance.trialName = name; + modalRef.componentInstance.namespace = this.namespace; + } + + handleMouseLeave = () => this.leaveMouseFromTrial.emit(); + + handleMouseOver = event => this.mouseOnTrial.emit(+event[event.length - 1]); + + goToKfpRun(kfpRun: string) { + // If the trial does not have a kfp run then do not redirect + if (!kfpRun) { + return; + } + + this.router.navigate([]).then(() => { + window.open(`/pipeline/#/runs/details/${kfpRun}`); + }); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.module.ts new file mode 100644 index 00000000000..dd16a85ab3a --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trials-table.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatTableModule } from '@angular/material/table'; +import { MatIconModule } from '@angular/material/icon'; +import { MatDialogModule } from '@angular/material/dialog'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; + +import { TrialsTableComponent } from './trials-table.component'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TrialModalComponent } from './trial-modal/trial-modal.component'; + +@NgModule({ + declarations: [TrialsTableComponent, TrialModalComponent], + imports: [ + CommonModule, + MatTableModule, + MatProgressSpinnerModule, + MatDialogModule, + MatIconModule, + NgxChartsModule, + MatTooltipModule, + ], + entryComponents: [TrialModalComponent], + exports: [TrialsTableComponent], +}) +export class TrialsTableModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.html new file mode 100644 index 00000000000..e519c802a48 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.html @@ -0,0 +1,8 @@ +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.scss new file mode 100644 index 00000000000..fd58b5f6d2c --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.scss @@ -0,0 +1,8 @@ +:host { + display: block; + overflow: auto; +} + +[ace-editor] { + width: 100%; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.spec.ts new file mode 100644 index 00000000000..3c52d5b44db --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentYamlComponent } from './experiment-yaml.component'; + +describe('ExperimentYamlComponent', () => { + let component: ExperimentYamlComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentYamlComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentYamlComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.ts new file mode 100644 index 00000000000..2389230f5a7 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.component.ts @@ -0,0 +1,17 @@ +import { Component, Input } from '@angular/core'; +import { ExperimentK8s } from 'src/app/models/experiment.k8s.model'; +import { dump } from 'js-yaml'; + +@Component({ + selector: 'app-experiment-yaml', + templateUrl: './experiment-yaml.component.html', + styleUrls: ['./experiment-yaml.component.scss'], +}) +export class ExperimentYamlComponent { + public yaml = ''; + + @Input() + set experimentJson(exp: ExperimentK8s) { + this.yaml = dump(exp); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.module.ts new file mode 100644 index 00000000000..261af14377f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/yaml/experiment-yaml.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ExperimentYamlComponent } from './experiment-yaml.component'; +import { AceEditorModule } from 'ng2-ace-editor'; + +@NgModule({ + declarations: [ExperimentYamlComponent], + imports: [CommonModule, AceEditorModule], + exports: [ExperimentYamlComponent], +}) +export class ExperimentYamlModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/config.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/config.ts new file mode 100644 index 00000000000..a188f6b9cec --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/config.ts @@ -0,0 +1,92 @@ +import { + PropertyValue, + StatusValue, + ActionListValue, + ActionIconValue, + TRUNCATE_TEXT_SIZE, + TableConfig, + DateTimeValue, + TemplateValue, + ChipsListValue, + ComponentValue, +} from 'kubeflow'; +import { + parseStatus, + parseSucceededTrials, + parseRunningTrials, + parseFailedTrials, + parseTotalTrials, +} from './utils'; +import { ExperimentOptimalTrialComponent } from './optimal-trial/experiment-optimal-trial.component'; + +export const experimentsTableConfig: TableConfig = { + title: 'Experiments', + newButtonText: 'NEW EXPERIMENT', + columns: [ + { + matHeaderCellDef: 'Status', + matColumnDef: 'status', + value: new StatusValue({ + valueFn: parseStatus, + }), + }, + { + matHeaderCellDef: 'Name', + matColumnDef: 'name', + value: new PropertyValue({ + field: 'name', + isLink: true, + }), + }, + { + matHeaderCellDef: 'Age', + matColumnDef: 'age', + value: new DateTimeValue({ + field: 'startTime', + }), + }, + { + matHeaderCellDef: 'Successful trials', + matColumnDef: 'successful', + textAlignment: 'right', + value: new PropertyValue({ + valueFn: parseSucceededTrials, + }), + }, + { + matHeaderCellDef: 'Running trials', + matColumnDef: 'running', + textAlignment: 'right', + value: new PropertyValue({ + valueFn: parseRunningTrials, + }), + }, + { + matHeaderCellDef: 'Failed trials', + matColumnDef: 'failed', + textAlignment: 'right', + value: new PropertyValue({ + valueFn: parseFailedTrials, + }), + }, + { + matHeaderCellDef: 'Optimal trial', + matColumnDef: 'optimal', + value: new ComponentValue({ + component: ExperimentOptimalTrialComponent, + }), + }, + { + matHeaderCellDef: '', + matColumnDef: 'actions', + value: new ActionListValue([ + new ActionIconValue({ + name: 'delete', + tooltip: 'Delete experiment', + color: '', + iconReady: 'material:delete', + }), + ]), + }, + ], +}; diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/delete-modal-config.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/delete-modal-config.ts new file mode 100644 index 00000000000..30dff53ce97 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/delete-modal-config.ts @@ -0,0 +1,18 @@ +import { DialogConfig } from 'kubeflow'; + +// --- Configs for the Confirm Dialogs --- +export function getDeleteDialogConfig( + name: string, + namespace: string, +): DialogConfig { + return { + title: `Delete experiment`, + message: `Are you sure you want to delete ${name} experiment from namespace ${namespace}?`, + accept: 'DELETE', + confirmColor: 'warn', + cancel: 'CANCEL', + error: '', + applying: 'DELETING', + width: '600px', + }; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.html new file mode 100644 index 00000000000..d8893a44e2b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.html @@ -0,0 +1,11 @@ + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.spec.ts new file mode 100644 index 00000000000..13cb487c034 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.spec.ts @@ -0,0 +1,23 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ExperimentsComponent } from './experiments.component'; + +describe('ExperimentsComponent', () => { + let component: ExperimentsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentsComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.ts new file mode 100644 index 00000000000..e9c523cb1ce --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.component.ts @@ -0,0 +1,142 @@ +import { + Component, + OnDestroy, + OnInit, + ViewChild, + TemplateRef, +} from '@angular/core'; +import { environment } from '@app/environment'; +import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { combineLatest, Subscription } from 'rxjs'; +import isEqual from 'lodash-es/isEqual'; +import { startWith } from 'rxjs/operators'; +import { + ConfirmDialogService, + DIALOG_RESP, + ExponentialBackoff, + NamespaceService, + TemplateValue, + ActionEvent, +} from 'kubeflow'; + +import { KWABackendService } from 'src/app/services/backend.service'; +import { Experiments, Experiment } from '../../models/experiment.model'; +import { StatusEnum } from '../../enumerations/status.enum'; +import { experimentsTableConfig } from './config'; +import { getDeleteDialogConfig } from './delete-modal-config'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-experiments', + templateUrl: './experiments.component.html', + styleUrls: ['./experiments.component.scss'], +}) +export class ExperimentsComponent implements OnInit, OnDestroy { + experiments: Experiments = []; + currNamespace: string; + config = experimentsTableConfig; + env = environment; + + private subs = new Subscription(); + private poller: ExponentialBackoff; + + constructor( + private backend: KWABackendService, + private confirmDialog: ConfirmDialogService, + private namespaceService: NamespaceService, + private router: Router, + ) {} + + ngOnInit() { + this.startExperimentsPolling(); + + // Reset the poller whenever the selected namespace changes + this.subs.add( + this.namespaceService.getSelectedNamespace().subscribe(nameSpace => { + this.currNamespace = nameSpace; + this.poller.reset(); + }), + ); + } + + ngOnDestroy(): void { + this.subs.unsubscribe(); + this.poller.stop(); + } + + trackByFn(index: number, experiment: Experiment) { + return `${experiment.name}/${experiment.namespace}`; + } + + reactToAction(a: ActionEvent) { + const exp = a.data as Experiment; + switch (a.action) { + case 'newResourceButton': // TODO: could also use enums here + this.router.navigate(['/new']); + break; + case 'name:link': + this.router.navigate([`/experiment/${exp.name}`]); + break; + case 'delete': + this.onDeleteExperiment(exp.name); + break; + } + } + + onDeleteExperiment(name: string) { + const deleteDialogConfig = getDeleteDialogConfig(name, this.currNamespace); + const ref = this.confirmDialog.open(name, deleteDialogConfig); + + const delSub = ref.componentInstance.applying$.subscribe(applying => { + if (!applying) { + return; + } + + // Close the open dialog only if the DELETE request succeeded + this.backend.deleteExperiment(name, this.currNamespace).subscribe({ + next: _ => { + this.poller.reset(); + ref.close(DIALOG_RESP.ACCEPT); + }, + error: err => { + const errorMsg = err; + deleteDialogConfig.error = errorMsg; + ref.componentInstance.applying$.next(false); + }, + }); + + // DELETE request has succeeded + ref.afterClosed().subscribe(res => { + delSub.unsubscribe(); + if (res !== DIALOG_RESP.ACCEPT) { + return; + } + + this.poller.reset(); + }); + }); + } + + private startExperimentsPolling() { + this.poller = new ExponentialBackoff({ interval: 1000, retries: 3 }); + + // Poll for new data and reset the poller if different data is found + this.subs.add( + this.poller.start().subscribe(() => { + this.backend.getExperiments().subscribe(experiments => { + // the backend should have proper namespace isolation + experiments = experiments.filter( + experiment => experiment.namespace === this.currNamespace, + ); + + if (isEqual(this.experiments, experiments)) { + return; + } + + this.experiments = experiments; + this.poller.reset(); + }); + }), + ); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.module.ts new file mode 100644 index 00000000000..81271db3d85 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/experiments.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatCardModule } from '@angular/material/card'; +import { + NamespaceSelectModule, + ResourceTableModule, + ConfirmDialogModule, +} from 'kubeflow'; + +import { ExperimentsComponent } from './experiments.component'; +import { ExperimentOptimalTrialModule } from './optimal-trial/experiment-optimal-trial.module'; + +@NgModule({ + declarations: [ExperimentsComponent], + imports: [ + CommonModule, + NamespaceSelectModule, + ResourceTableModule, + + ConfirmDialogModule, + ExperimentOptimalTrialModule, + MatCardModule, + ConfirmDialogModule, + MatCardModule, + ], + exports: [ExperimentsComponent], +}) +export class ExperimentsModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.html new file mode 100644 index 00000000000..8de64af035f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.html @@ -0,0 +1,37 @@ + + No succeeded trial yet + + + +
+
{{ metrics[0].name }}:
+ +
{{ metrics[0].value }}
+
+
+ + + +
Metrics
+ + {{ metric.value }} + + + + + +
Parameters
+ + {{ param.value }} + +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.scss new file mode 100644 index 00000000000..bca92ac603b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.scss @@ -0,0 +1,12 @@ +.metric-key { + font-weight: 500; + margin-right: 0.2rem; +} + +.popover-title { + color: rgba(0, 0, 0, 0.66); + + &:last-of-type { + margin-top: 0.8rem; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.spec.ts new file mode 100644 index 00000000000..f52ab575da0 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentOptimalTrialComponent } from './experiment-optimal-trial.component'; + +describe('ExperimentOptimalTrialComponent', () => { + let component: ExperimentOptimalTrialComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExperimentOptimalTrialComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentOptimalTrialComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.ts new file mode 100644 index 00000000000..fbd000e555f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { Experiment } from 'src/app/models/experiment.model'; +import { TableColumnComponent } from 'kubeflow/lib/resource-table/component-value/component-value.component'; +import { + KeyValuePair, + trackByParamFn, + parseOptimalMetric, + parseOptimalParameters, +} from './utils'; + +@Component({ + selector: 'app-experiment-optimal-trial', + templateUrl: './experiment-optimal-trial.component.html', + styleUrls: ['./experiment-optimal-trial.component.scss'], +}) +export class ExperimentOptimalTrialComponent implements TableColumnComponent { + public params: KeyValuePair[] = []; + public metrics: KeyValuePair[] = []; + public trackByParamFn = trackByParamFn; + + set element(experiment: Experiment) { + this.metrics = parseOptimalMetric(experiment); + this.params = parseOptimalParameters(experiment); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.module.ts new file mode 100644 index 00000000000..bdfa4cda9cd --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/experiment-optimal-trial.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ExperimentOptimalTrialComponent } from './experiment-optimal-trial.component'; +import { PopoverModule, DetailsListModule } from 'kubeflow'; +import { MatDividerModule } from '@angular/material/divider'; + +@NgModule({ + declarations: [ExperimentOptimalTrialComponent], + imports: [CommonModule, PopoverModule, DetailsListModule, MatDividerModule], + entryComponents: [ExperimentOptimalTrialComponent], +}) +export class ExperimentOptimalTrialModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/utils.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/utils.ts new file mode 100644 index 00000000000..e341e22c386 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/optimal-trial/utils.ts @@ -0,0 +1,58 @@ +import { Experiment } from 'src/app/models/experiment.model'; +import lowerCase from 'lodash-es/lowerCase'; +import { numberToExponential } from 'src/app/shared/utils'; + +export interface KeyValuePair { + name: string; + value: string; +} + +export function parseOptimalMetric(exp: Experiment): KeyValuePair[] { + if ( + !exp || + !exp.currentOptimalTrial || + !exp.currentOptimalTrial.observation.metrics + ) { + return []; + } + + return exp.currentOptimalTrial.observation.metrics.map(metric => { + const value = isNaN(+metric.latest) + ? metric.latest.toString() + : numberToExponential(Number(metric.latest), 6); + + const name = + lowerCase(metric.name) + .charAt(0) + .toUpperCase() + lowerCase(metric.name).slice(1); + + return { name, value }; + }); +} + +export function parseOptimalParameters(exp: Experiment): KeyValuePair[] { + if ( + !exp || + !exp.currentOptimalTrial || + !exp.currentOptimalTrial.parameterAssignments + ) { + return []; + } + + return exp.currentOptimalTrial.parameterAssignments.map(param => { + const value = isNaN(+param.value) + ? param.value.toString() + : numberToExponential(+param.value, 6); + + const name = + lowerCase(param.name) + .charAt(0) + .toUpperCase() + lowerCase(param.name).slice(1); + + return { name, value }; + }); +} + +export function trackByParamFn(index: number, row: KeyValuePair) { + return `${row.name}: ${row.value}`; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/utils.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/utils.ts new file mode 100644 index 00000000000..f293fcb9d9e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiments/utils.ts @@ -0,0 +1,45 @@ +import { Experiments, Experiment } from '../../models/experiment.model'; +import { Status, STATUS_TYPE, ChipsListValue, ChipDescriptor } from 'kubeflow'; +import { StatusEnum } from 'src/app/enumerations/status.enum'; + +export function parseStatus(exp: Experiment): Status { + const statusCol = { + phase: STATUS_TYPE.ERROR, + state: '', + message: exp.status, + }; + + if (exp.status === StatusEnum.SUCCEEDED) { + statusCol.phase = STATUS_TYPE.READY; + } + + if (exp.status === StatusEnum.FAILED) { + statusCol.phase = STATUS_TYPE.WARNING; + } + + if (exp.status === StatusEnum.CREATED) { + statusCol.phase = STATUS_TYPE.UNAVAILABLE; + } + + if (exp.status === StatusEnum.RUNNING) { + statusCol.phase = STATUS_TYPE.UNAVAILABLE; + } + + return statusCol; +} + +export function parseTotalTrials(exp: Experiment): number { + return exp.trials ? exp.trials : 0; +} + +export function parseSucceededTrials(exp: Experiment): number { + return exp.trialsSucceeded ? exp.trialsSucceeded : 0; +} + +export function parseRunningTrials(exp: Experiment): number { + return exp.trialsRunning ? exp.trialsRunning : 0; +} + +export function parseFailedTrials(exp: Experiment): number { + return exp.trialsFailed ? exp.trialsFailed : 0; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.spec.ts new file mode 100644 index 00000000000..2fe82175ac9 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { BackendService } from './backend.service'; + +describe('BackendService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: BackendService = TestBed.get(BackendService); + expect(service).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts b/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts new file mode 100644 index 00000000000..13b70460fdf --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { catchError, map } from 'rxjs/operators'; +import { Observable, throwError } from 'rxjs'; +import { + BackendService, + K8sObject, + SnackBarService, + SnackType, +} from 'kubeflow'; + +import { Experiments } from '../models/experiment.model'; +import { ExperimentK8s } from '../models/experiment.k8s.model'; +import { TrialTemplateResponse } from '../models/trial-templates.model'; + +@Injectable({ + providedIn: 'root', +}) +export class KWABackendService extends BackendService { + constructor(public http: HttpClient, public snack: SnackBarService) { + super(http, snack); + } + + private parseError(error) { + let msg = 'An error occured while talking to the backend'; + + if (error instanceof HttpErrorResponse) { + if (!error.status) { + msg = error.message; + } else { + msg = `[${error.status}] ${error.error}`; + } + } + + this.snack.open(msg, SnackType.Error, 8000); + + return throwError(msg); + } + + getExperiments(): Observable { + // If the route doesn't end in a "/"" then the backend will return a 301 to + // the url ending with "/". + const url = '/katib/fetch_experiments/'; + + return this.http.get(url).pipe( + catchError(error => this.parseError(error)), + map((resp: any) => { + return resp; + }), + ); + } + + getExperimentTrialsInfo(name: string, namespace: string): Observable { + const url = `/katib/fetch_hp_job_info/?experimentName=${name}&namespace=${namespace}`; + + return this.http.get(url).pipe(catchError(error => this.parseError(error))); + } + + getExperiment(name: string, namespace: string): Observable { + const url = `/katib/fetch_experiment/?experimentName=${name}&namespace=${namespace}`; + + return this.http + .get(url) + .pipe(catchError(error => this.parseError(error))) as Observable< + K8sObject + >; + } + + deleteExperiment(name: string, namespace: string): Observable { + const url = `/katib/delete_experiment/?experimentName=${name}&namespace=${namespace}`; + + return this.http + .delete(url) + .pipe(catchError(error => this.handleError(error))) as Observable< + ExperimentK8s + >; + } + + getTrial(name: string, namespace: string): Observable { + const url = `/katib/fetch_hp_job_trial_info/?trialName=${name}&namespace=${namespace}`; + return this.http.get(url).pipe(catchError(error => this.parseError(error))); + } + + getTrialTemplates(namespace: string): Observable { + const url = `/katib/fetch_trial_templates/`; + + return this.http + .get(url) + .pipe(catchError(error => this.parseError(error))); + } + + createExperiment(exp: ExperimentK8s): Observable { + const url = `/katib/create_experiment/`; + + return this.http + .post(url, { postData: exp }) + .pipe(catchError(error => this.parseError(error))); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/services/experiment-form.service.ts b/pkg/new-ui/v1beta1/frontend/src/app/services/experiment-form.service.ts new file mode 100644 index 00000000000..ff19fbc4041 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/services/experiment-form.service.ts @@ -0,0 +1,412 @@ +import { + FormBuilder, + Validators, + FormGroup, + FormArray, + FormControl, +} from '@angular/forms'; +import { Injectable } from '@angular/core'; +import { ObjectiveTypeEnum } from '../enumerations/objective-type.enum'; +import { AlgorithmsEnum } from '../enumerations/algorithms.enum'; +import { BehaviorSubject } from 'rxjs'; +import { createParameterGroup, createNasOperationGroup } from '../shared/utils'; +import { K8sObject, SnackBarService, SnackType } from 'kubeflow'; +import { dump, load } from 'js-yaml'; +import { + ObjectiveSpec, + AlgorithmSpec, + ParameterSpec, + FeasibleSpaceMinMax, + GraphConfig, + NasOperation, + AlgorithmSetting, +} from '../models/experiment.k8s.model'; +import { CollectorKind } from '../enumerations/metrics-collector'; + +@Injectable() +export class ExperimentFormService { + constructor(private builder: FormBuilder, private snack: SnackBarService) {} + + /* + * functions for creating the Form Controls + */ + createMetadataForm(namespace): FormGroup { + return this.builder.group({ + name: 'random-experiment', + namespace, + }); + } + + createTrialThresholdForm(): FormGroup { + return this.builder.group({ + parallelTrialCount: 3, + maxTrialCount: 12, + maxFailedTrialCount: 3, + }); + } + + createObjectiveForm(): FormGroup { + return this.builder.group({ + type: 'maximize', + goal: 0.99, + metricName: 'accuracy', + metricStrategy: 'max', + additionalMetricNames: this.builder.array(['train-accuracy']), + metricStrategies: this.builder.array([]), + setStrategies: this.builder.control(false), + }); + } + + createAlgorithmObjectiveForm(): FormGroup { + return this.builder.group({ + type: 'hp', + algorithm: AlgorithmsEnum.RANDOM, + algorithmSettings: this.builder.array([]), + }); + } + + createHyperParametersForm(): FormArray { + return this.builder.array([ + createParameterGroup({ + name: 'lr', + parameterType: 'double', + feasibleSpace: { + min: '0.01', + max: '0.03', + step: '0.01', + }, + }), + createParameterGroup({ + name: 'num-layers', + parameterType: 'int', + feasibleSpace: { + min: '1', + max: '64', + step: '1', + }, + }), + createParameterGroup({ + name: 'optimizer', + parameterType: 'categorical', + feasibleSpace: { list: ['sgd', 'adams', 'ftrl'] }, + }), + ]); + } + + createNasGraphForm(): FormGroup { + return this.builder.group({ + layers: 8, + inputSizes: this.builder.array([32, 32, 3]), + outputSizes: this.builder.array([10]), + }); + } + + createNasOperationsForm(): FormArray { + return this.builder.array([ + createNasOperationGroup({ + operationType: 'convolution', + parameters: [ + { + name: 'filter_size', + parameterType: 'categorical', + feasibleSpace: { list: ['3', '5', '7'] }, + }, + { + name: 'num_filter', + parameterType: 'categorical', + feasibleSpace: { list: ['32', '48', '64'] }, + }, + { + name: 'stride', + parameterType: 'categorical', + feasibleSpace: { list: ['1', '2'] }, + }, + ], + }), + + createNasOperationGroup({ + operationType: 'separable_convolution', + parameters: [ + { + name: 'filter_size', + parameterType: 'categorical', + feasibleSpace: { list: ['3', '5', '7'] }, + }, + { + name: 'num_filter', + parameterType: 'categorical', + feasibleSpace: { list: ['32', '48', '64'] }, + }, + { + name: 'stride', + parameterType: 'categorical', + feasibleSpace: { list: ['1', '2'] }, + }, + { + name: 'depth_multiplier', + parameterType: 'categorical', + feasibleSpace: { list: ['1', '2'] }, + }, + ], + }), + + createNasOperationGroup({ + operationType: 'depthwise_convolution', + parameters: [ + { + name: 'filter_size', + parameterType: 'categorical', + feasibleSpace: { list: ['3', '5', '7'] }, + }, + { + name: 'num_filter', + parameterType: 'categorical', + feasibleSpace: { list: ['32', '48', '64'] }, + }, + { + name: 'depth_multiplier', + parameterType: 'categorical', + feasibleSpace: { list: ['1', '2'] }, + }, + ], + }), + + createNasOperationGroup({ + operationType: 'reduction', + parameters: [ + { + name: 'reduction_type', + parameterType: 'categorical', + feasibleSpace: { list: ['max_pooling', 'avg_pooling'] }, + }, + { + name: 'pool_size', + parameterType: 'int', + feasibleSpace: { + min: '2', + max: '3', + step: '1', + }, + }, + ], + }), + ]); + } + + createMetricsForm(): FormGroup { + return this.builder.group({ + kind: CollectorKind.STDOUT, + metricsFile: '/var/log/katib/metrics.log', + tfDir: '/var/log/katib/tfevent/', + prometheus: this.builder.group({ + port: this.builder.control('8080', Validators.required), + path: this.builder.control('/metrics', Validators.required), + scheme: this.builder.control('HTTP', Validators.required), + host: this.builder.control(''), + httpHeaders: this.builder.array([]), + }), + }); + } + + createTrialTemplateForm(): FormGroup { + return this.builder.group({ + type: 'configmap', + podLabels: this.builder.array([]), + containerName: 'training-container', + successCond: 'status.conditions.#(type=="Complete")#|#(status=="True")#', + failureCond: 'status.conditions.#(type=="Failed")#|#(status=="True")#', + retain: 'false', + cmNamespace: '', + cmName: '', + cmTrialPath: '', + yaml: '', + }); + } + + /** + * helpers for parsing the form controls + */ + parseParam(parameter: ParameterSpec): ParameterSpec { + // we should omit the step in case it was not defined + const param = JSON.parse(JSON.stringify(parameter)); + + if ( + param.parameterType === 'discrete' || + param.parameterType === 'categorical' + ) { + return param; + } + + if ((param.feasibleSpace as FeasibleSpaceMinMax).step === '') { + delete (param.feasibleSpace as FeasibleSpaceMinMax).step; + } + + return param; + } + + /* + * YAML helpers for moving between FormControls and actual YAML strings + */ + metadataFromCtrl(group: FormGroup): any { + return { + name: group.get('name').value, + namespace: group.get('namespace').value, + }; + } + + objectiveFromCtrl(group: FormGroup): ObjectiveSpec { + const objMetric = group.get('metricName').value; + const objStrategy = group.get('type').value.substring(0, 3); + + const objective: any = { + type: group.get('type').value, + goal: group.get('goal').value, + objectiveMetricName: objMetric, + // metricStrategies: [{ name: objMetric, value: objStrategy }], + additionalMetricNames: [], + }; + + const additionalMetrics = group.get('additionalMetricNames').value || []; + additionalMetrics.forEach(metric => { + if (!metric) { + return; + } + + objective.additionalMetricNames.push(metric); + }); + + if (!group.get('setStrategies').value) { + return objective; + } + + objective.metricStrategies = [{ name: objMetric, value: objStrategy }]; + group.get('metricStrategies').value.forEach(metricStrategy => { + objective.metricStrategies.push({ + name: metricStrategy.metric, + value: metricStrategy.strategy, + }); + }); + + return objective; + } + + algorithmFromCtrl(group: FormGroup): AlgorithmSpec { + const settings: AlgorithmSetting[] = []; + group.get('algorithmSettings').value.forEach(setting => { + if (setting.value === null) { + return; + } + + settings.push({ name: setting.name, value: `${setting.value}` }); + }); + + return { + algorithmName: group.get('algorithm').value, + algorithmSettings: settings, + }; + } + + hyperParamsFromCtrl(paramsArray: FormArray): any { + const params = paramsArray.value as ParameterSpec[]; + return params.map(param => { + return this.parseParam(param); + }); + } + + nasOpsFromCtrl(operations: FormArray): NasOperation[] { + const ops: NasOperation[] = operations.value; + + return ops.map(op => { + op.parameters = op.parameters.map(param => this.parseParam(param)); + return op; + }); + } + + metricsCollectorFromCtrl(group: FormGroup): any { + const kind = group.get('kind').value; + const metrics: any = { + source: { + fileSystemPath: { + path: '', + kind: '', + }, + }, + collector: { kind }, + }; + + if (kind === 'StdOut' || kind === 'None') { + delete metrics.source; + return metrics; + } + + if (kind === 'File') { + metrics.source.fileSystemPath.path = group.get('metricsFile').value; + metrics.source.fileSystemPath.kind = 'File'; + return metrics; + } + + if (kind === 'TensorFlowEvent') { + metrics.source.fileSystemPath.path = group.get('tfDir').value; + metrics.source.fileSystemPath.kind = 'Directory'; + return metrics; + } + + if (kind === 'PrometheusMetric') { + delete metrics.source.fileSystemPath; + metrics.source.httpGet = group.get('prometheus').value; + + if (!metrics.source.httpGet.host) { + delete metrics.source.httpGet.host; + } + + const headers = metrics.source.httpGet.httpHeaders; + metrics.source.httpGet.httpHeaders = headers.map(header => { + return { name: header.key, value: header.value }; + }); + + return metrics; + } + + /* TODO(kimwnasptd): We need to handle the Custom case */ + if (kind === 'Custom') { + return metrics; + } + + return {}; + } + + trialTemplateFromCtrl(group: FormGroup): any { + const trialTemplate: any = {}; + const formValue = group.value; + + trialTemplate.primaryContainerName = formValue.containerName; + trialTemplate.successCondition = formValue.successCond; + trialTemplate.failureCondition = formValue.failureCond; + trialTemplate.retain = formValue.retain === 'true' ? true : false; + + if (formValue.podLabels && formValue.podLabels.length) { + trialTemplate.primaryPodLabels = {}; + formValue.podLabels.map( + label => (trialTemplate.primaryPodLabels[label.key] = label.value), + ); + } + + if (formValue.type === 'yaml') { + try { + trialTemplate.trialSpec = load(formValue.yaml); + } catch (e) { + this.snack.open(`${e.reason}`, SnackType.Warning, 4000); + } + + return trialTemplate; + } + + trialTemplate.configMap = { + configMapName: formValue.cmName, + configMapNamespace: formValue.cmNamespace, + templatePath: formValue.cmTrialPath, + }; + + return trialTemplate; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.html b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.html new file mode 100644 index 00000000000..8e38e83d52b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.html @@ -0,0 +1,22 @@ +

{{ header }}

+ + + +
+ + + {{ valueLabel }} + + + + +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.scss new file mode 100644 index 00000000000..3cd6805a203 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.scss @@ -0,0 +1,7 @@ +:host { + display: block; +} + +.add-btn { + margin-bottom: 12px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.spec.ts new file mode 100644 index 00000000000..e5ee05969b2 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListInputComponent } from './list-input.component'; + +describe('ListInputComponent', () => { + let component: ListInputComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ListInputComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ListInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.ts new file mode 100644 index 00000000000..19be4788a95 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { FormArray, FormControl, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-shared-list-input', + templateUrl: './list-input.component.html', + styleUrls: ['./list-input.component.scss'], +}) +export class ListInputComponent implements OnInit { + @Input() header: string; + @Input() valueLabel = 'Value'; + @Input() formArray: FormArray; + @Input() addValueText = 'Add value'; + @Input() requiredValue = true; + + constructor() {} + + ngOnInit() {} + + addCtrl() { + const validators = this.requiredValue ? Validators.required : []; + this.formArray.push(new FormControl('', validators)); + } + + removeCtrl(i: number) { + this.formArray.removeAt(i); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.module.ts new file mode 100644 index 00000000000..cf91d71c0bb --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-input/list-input.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ListInputComponent } from './list-input.component'; + +import { MatIconModule } from '@angular/material/icon'; + +import { FormModule } from 'kubeflow'; + +@NgModule({ + declarations: [ListInputComponent], + imports: [CommonModule, FormModule, MatIconModule], + exports: [ListInputComponent], +}) +export class ListInputModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.html b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.html new file mode 100644 index 00000000000..edd3fa36735 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.html @@ -0,0 +1,32 @@ +

{{ header }}

+ + + +
+
+ + {{ keyLabel }} + + + + + {{ valueLabel }} + + + + +
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.scss new file mode 100644 index 00000000000..7dec5543b8d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.scss @@ -0,0 +1,19 @@ +:host { + display: block; +} + +.key-value-entry { + display: flex; + + .mat-form-field:nth-child(2) { + margin-left: 12px; + } + + .delete-btn { + margin-top: 10px; + } +} + +.add-btn { + margin-bottom: 12px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.spec.ts new file mode 100644 index 00000000000..27d2e2e6e46 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListKeyValueComponent } from './list-key-value.component'; + +describe('ListKeyValueComponent', () => { + let component: ListKeyValueComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ListKeyValueComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ListKeyValueComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.ts new file mode 100644 index 00000000000..fdf90b05f9e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.component.ts @@ -0,0 +1,28 @@ +import { Component, Input } from '@angular/core'; +import { FormArray, FormControl, Validators, FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-list-key-value', + templateUrl: './list-key-value.component.html', + styleUrls: ['./list-key-value.component.scss'], +}) +export class ListKeyValueComponent { + @Input() header: string; + @Input() addButtonText = 'Add key-value'; + @Input() keyLabel = 'Key'; + @Input() valueLabel = 'Value'; + @Input() formArray: FormArray; + + addCtrl() { + this.formArray.push( + new FormGroup({ + key: new FormControl('k', Validators.required), + value: new FormControl('v', Validators.required), + }), + ); + } + + removeCtrl(i: number) { + this.formArray.removeAt(i); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.module.ts new file mode 100644 index 00000000000..9f49dd3b4d3 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/list-key-value/list-key-value.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ListKeyValueComponent } from './list-key-value.component'; + +import { MatIconModule } from '@angular/material/icon'; + +import { FormModule } from 'kubeflow'; + +@NgModule({ + declarations: [ListKeyValueComponent], + imports: [CommonModule, FormModule, MatIconModule], + exports: [ListKeyValueComponent], +}) +export class ListKeyValueModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.html b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.html new file mode 100644 index 00000000000..e674b8a2b86 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.html @@ -0,0 +1,66 @@ +

Add new parameter

+ +
+
+ Configure the new parameter that will be added to the list. + + + + Name + + + + + + Type + + Integer + Double + Discrete + Categorical + + + + + + +
+ + Min + + + + + Max + + + + + Step (optional) + + +
+
+ + + + + +
+
+ +
+ + +
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.scss new file mode 100644 index 00000000000..491b336568e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.scss @@ -0,0 +1,23 @@ +.margin-top { + margin-top: 12px; +} + +.value-type { + display: flex; + + * { + margin: 0 8px; + } +} + +.range-wrapper { + display: flex; + + > * { + width: 130px; + } + + > *:nth-child(2) { + margin: 0 12px; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.spec.ts new file mode 100644 index 00000000000..f62e1fdf9bf --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddModalComponent } from './add-modal.component'; + +describe('AddModalComponent', () => { + let component: AddModalComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AddModalComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.ts new file mode 100644 index 00000000000..9757412cb50 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/add-modal/add-modal.component.ts @@ -0,0 +1,69 @@ +import { Component, OnInit, OnDestroy, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { FormGroup, FormControl, Validators, FormArray } from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { createParameterGroup, createFeasibleSpaceGroup } from '../../utils'; +import { + FeasibleSpace, + FeasibleSpaceMinMax, + FeasibleSpaceList, +} from 'src/app/models/experiment.k8s.model'; + +@Component({ + selector: 'app-add-param-modal', + templateUrl: './add-modal.component.html', + styleUrls: ['./add-modal.component.scss'], +}) +export class AddParamModalComponent implements OnInit, OnDestroy { + originalFormGroup: FormGroup; + formGroup: FormGroup; + + subs = new Subscription(); + + get isList(): boolean { + const tp = this.formGroup.get('parameterType').value; + return tp === 'discrete' || tp === 'categorical'; + } + + constructor( + private dialog: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: FormGroup, + ) { + this.originalFormGroup = data; + this.formGroup = createParameterGroup(data.value); + } + + ngOnInit() { + this.subs.add( + this.formGroup.get('parameterType').valueChanges.subscribe(type => { + let fsMinMax: FeasibleSpaceMinMax = { min: '1', max: '64', step: '' }; + let fsList: FeasibleSpaceList = { list: [] }; + let fs: FeasibleSpace; + + fs = fsMinMax; + if (this.isList) { + fs = fsList; + } + + this.formGroup.removeControl('feasibleSpace'); + this.formGroup.addControl( + 'feasibleSpace', + createFeasibleSpaceGroup(type, fs), + ); + return; + }), + ); + } + + ngOnDestroy() { + this.subs.unsubscribe(); + } + + save() { + this.dialog.close(this.formGroup); + } + + cancel() { + this.dialog.close(); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.html b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.html new file mode 100644 index 00000000000..08d73cbac8d --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.html @@ -0,0 +1,74 @@ +
+
{{ name }}
+
{{ type }}
+ + +
+
+ {{ listStr }} +
+
+
+ + +
+ {{ min }} ≤ {{ name }} ≤ {{ max }}{{ stepSign }}{{ step }} +
+
+ + + + + + + + +
Feasible Space
+ + + + {{ min }} + + + + {{ max }} + + + + {{ step }} + +
+ + + + +
+ + {{ item }} + +
+
+
+
diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.scss new file mode 100644 index 00000000000..39f14ada6fd --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.scss @@ -0,0 +1,42 @@ +:host { + display: block; + border: #0000001f solid 1px; + border-radius: 8px; + margin-bottom: 8px; + width: 440px; + padding: 8px 8px 8px 16px; +} + +.params-row { + display: flex; + width: 100%; + align-items: center; +} + +.param-name { + width: 25%; +} + +.param-type { + width: 25%; +} + +.param-value { + width: 50%; + overflow: hidden; +} + +// popovers +.popover-title { + color: rgba(0, 0, 0, 0.66); + margin-bottom: 8px; +} + +.popover-list-wrapper { + display: grid; + grid-template-columns: repeat(5, 1fr); +} + +.popover-list-item { + margin: 0 3px; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.spec.ts new file mode 100644 index 00000000000..49b4e02bf21 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ParameterComponent } from './parameter.component'; + +describe('ParameterComponent', () => { + let component: ParameterComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ParameterComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ParameterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.ts new file mode 100644 index 00000000000..81b5dfe02cb --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/parameter/parameter.component.ts @@ -0,0 +1,89 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { AbstractControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { AddParamModalComponent } from '../add-modal/add-modal.component'; +import { + FeasibleSpaceMinMax, + ParameterType, +} from 'src/app/models/experiment.k8s.model'; + +@Component({ + selector: 'app-shared-parameter', + templateUrl: './parameter.component.html', + styleUrls: ['./parameter.component.scss'], +}) +export class ParameterComponent implements OnInit { + @Input() paramCtrl: AbstractControl; + @Output() delete = new EventEmitter(); + + constructor(private dialog: MatDialog) {} + + ngOnInit() {} + + get isListValue() { + if (!this.paramCtrl) { + return false; + } + + return this.type === 'discrete' || this.type === 'categorical'; + } + + get name(): string { + return this.paramCtrl.get('name').value; + } + + get type(): ParameterType { + return this.paramCtrl.get('parameterType').value; + } + + get min() { + return this.paramCtrl.get('feasibleSpace').value.min; + } + + get max() { + return this.paramCtrl.get('feasibleSpace').value.max; + } + + get step() { + return this.paramCtrl.get('feasibleSpace').value.step; + } + + get stepSign() { + if (this.step > 0) { + return ', +'; + } + + if (this.step < 0) { + return ', '; + } + + return ''; + } + + get listValue(): any[] { + return this.paramCtrl.get('feasibleSpace').value.list; + } + + get listStr() { + return this.listValue.join(', '); + } + + editParam() { + const sub = this.dialog + .open(AddParamModalComponent, { data: this.paramCtrl }) + .afterClosed() + .subscribe(group => { + sub.unsubscribe(); + + if (group) { + this.paramCtrl.get('name').setValue(group.get('name').value); + this.paramCtrl + .get('parameterType') + .setValue(group.get('parameterType').value); + this.paramCtrl + .get('feasibleSpace') + .setValue(group.get('feasibleSpace').value); + } + }); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.html b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.html new file mode 100644 index 00000000000..10ee8ca1be0 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.html @@ -0,0 +1,21 @@ + + +
+
Name
+
Type
+
Domain
+
+ + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.scss new file mode 100644 index 00000000000..d73914177fb --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.scss @@ -0,0 +1,30 @@ +:host { + display: block; +} + +.header-row { + padding-left: 16px; + margin-bottom: 8px; + display: flex; +} + +.header-name { + width: 90px; +} + +.header-type { + width: 90px; +} + +.header-value { + width: 55%; +} + +.new-param-btn { + margin-top: 12px; + margin-bottom: 12px; + + mat-icon { + margin-bottom: 2px; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.spec.ts new file mode 100644 index 00000000000..4a6f185787e --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ParamsListComponent } from './params-list.component'; + +describe('ParamsListComponent', () => { + let component: ParamsListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ParamsListComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ParamsListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.ts new file mode 100644 index 00000000000..f6db8ef2599 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { + FormArray, + FormBuilder, + FormGroup, + FormControl, + Validators, +} from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { AddParamModalComponent } from './add-modal/add-modal.component'; +import { createParameterGroup } from '../utils'; + +@Component({ + selector: 'app-shared-params-list', + templateUrl: './params-list.component.html', + styleUrls: ['./params-list.component.scss'], +}) +export class ParamsListComponent implements OnInit { + @Input() paramsArray: FormArray; + + constructor(private builder: FormBuilder, private dialog: MatDialog) {} + + ngOnInit() {} + + removeParam(i: number) { + this.paramsArray.removeAt(i); + } + + addParam() { + const newParamGroup = createParameterGroup({ + name: '', + parameterType: 'int', + feasibleSpace: { + min: '1', + max: '64', + step: '1', + }, + }); + + const sub = this.dialog + .open(AddParamModalComponent, { data: newParamGroup }) + .afterClosed() + .subscribe(group => { + sub.unsubscribe(); + + if (group) { + this.paramsArray.push(group); + } + }); + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.module.ts new file mode 100644 index 00000000000..d09332d9bb8 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/params-list/params-list.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ParamsListComponent } from './params-list.component'; +import { ParameterComponent } from './parameter/parameter.component'; +import { AddParamModalComponent } from './add-modal/add-modal.component'; + +import { MatIconModule } from '@angular/material/icon'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; + +import { FormModule, PopoverModule, DetailsListModule } from 'kubeflow'; +import { ListInputModule } from '../list-input/list-input.module'; + +@NgModule({ + declarations: [ + ParamsListComponent, + ParameterComponent, + AddParamModalComponent, + ], + entryComponents: [AddParamModalComponent], + imports: [ + CommonModule, + FormModule, + MatIconModule, + MatDividerModule, + MatRadioModule, + PopoverModule, + DetailsListModule, + MatSlideToggleModule, + ListInputModule, + ], + exports: [ParamsListComponent], +}) +export class ParamsListModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/shared/utils.ts b/pkg/new-ui/v1beta1/frontend/src/app/shared/utils.ts new file mode 100644 index 00000000000..fbe1dbeb055 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/shared/utils.ts @@ -0,0 +1,135 @@ +import lowerCase from 'lodash-es/lowerCase'; +import { FormControl, FormGroup, Validators, FormArray } from '@angular/forms'; +import { + ParameterSpec, + FeasibleSpaceMinMax, + FeasibleSpaceList, + NasOperation, + FeasibleSpace, + ParameterType, +} from '../models/experiment.k8s.model'; + +export function createNasOperationGroup(op: NasOperation): FormGroup { + const array = op.parameters.map(param => createParameterGroup(param)); + + return new FormGroup({ + operationType: new FormControl(op.operationType, Validators.required), + parameters: new FormArray(array), + }); +} + +export function createFeasibleSpaceGroup( + parameterType: ParameterType, + feasibleSpace: FeasibleSpace, +) { + let fs: FeasibleSpace; + + // min-max-step + if (parameterType === 'int' || parameterType === 'double') { + fs = feasibleSpace as FeasibleSpaceMinMax; + + return new FormGroup({ + min: new FormControl(fs.min, Validators.required), + max: new FormControl(fs.max, Validators.required), + step: new FormControl(fs.step, []), + }); + } + + // list values + fs = feasibleSpace as FeasibleSpaceList; + + const ctrls = fs.list.map(v => new FormControl(v, Validators.required)); + return new FormGroup({ + list: new FormArray(ctrls, Validators.required), + }); +} + +export function createParameterGroup(param: ParameterSpec): FormGroup { + return new FormGroup({ + name: new FormControl(param.name, Validators.required), + parameterType: new FormControl(param.parameterType, Validators.required), + feasibleSpace: createFeasibleSpaceGroup( + param.parameterType, + param.feasibleSpace, + ), + }); +} + +/* + * Arithmetics + **/ +export const numberToExponential = (num: number, digits: number): string => { + if (isNaN(Number(num))) { + return ''; + } + + if (num.toString().replace(/[.]/g, '').length <= digits) { + return num.toString(); + } + + if ( + num.toExponential().search(/e[+]1/) > -1 || + num.toExponential().search(/e[-]1/) > -1 + ) { + const slicedNumber = num.toString().slice(0, digits + 1); + + return slicedNumber.replace(/[.]*0+$/, ''); + } + + let exponentialNumber = num.toExponential(digits - 1); + + // If toExponential added e+0 in the end of the string remove it + exponentialNumber = exponentialNumber.replace(/[.]*0*e[+]0$/, ''); + + // If the number is e.g. 2.1000e-3, the zeros must to be removed + if (/[.]*0+e[+-][1-9]$/.test(exponentialNumber)) { + // Split the number and the exponent in order to remove from + // the number the zeros + const [numberToFix, exponentNumber] = exponentialNumber.split('e'); + const fixed = numberToFix.replace(/[0]*$/g, ''); + + // Build again the exponential number + exponentialNumber = `${fixed}e${exponentNumber}`; + + /*If the number was e.g. 2.000e-3 after the above replacement + it would be 2.e-3 so a zero between . and e has to be added*/ + return exponentialNumber.replace(/[.]e/g, '.0e'); + } + + return exponentialNumber; +}; + +export const transformStringResponses = ( + response: string, +): { types: string[]; details: string[][] } => { + response = response.replace(/\n$/, ''); + + // Separate each line + const lines = response.split('\n'); + let types = []; + let details = []; + + // The first line is the names of the types + types = lines[0].split(','); + // Transform them for consistency + types = types.map(column => lowerCase(column)); + + // Separate types from details of every item + lines.splice(0, 1); + + // Change the first letter of the column to upper case + types = types.map(column => column.charAt(0).toUpperCase() + column.slice(1)); + + // Transform the details of each item from string to an array + details = lines.map(detail => detail.split(',')); + + return { types, details }; +}; + +export const safeDivision = (divided: number, divider: number): number => + Math.round((divided * 10000.0) / divider) / 10000; + +export const safeMultiplication = ( + multiplicand: number, + multiplier: number, +): number => Math.round(multiplicand * 10000.0 * multiplier) / 10000; diff --git a/pkg/new-ui/v1beta1/frontend/src/assets/.gitkeep b/pkg/new-ui/v1beta1/frontend/src/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/new-ui/v1beta1/frontend/src/assets/css/d3.parcoords.css b/pkg/new-ui/v1beta1/frontend/src/assets/css/d3.parcoords.css new file mode 100644 index 00000000000..862d40dbfeb --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/assets/css/d3.parcoords.css @@ -0,0 +1,33 @@ +.parcoords > canvas { + font: 14px sans-serif; + position: absolute; +} +.parcoords > canvas { + pointer-events: none; +} +.parcoords text.label { + cursor: pointer; + margin-bottom: 8px; +} +.parcoords rect.background:hover { + fill: rgba(120, 120, 120, 0.2); +} +.parcoords canvas { + opacity: 1; + transition: opacity 0.3s; + -moz-transition: opacity 0.3s; + -webkit-transition: opacity 0.3s; + -o-transition: opacity 0.3s; +} +.parcoords canvas.faded { + opacity: 0.25; +} +.parcoords { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: white; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/assets/pipeline-centered.svg b/pkg/new-ui/v1beta1/frontend/src/assets/pipeline-centered.svg new file mode 100644 index 00000000000..4fc5fb2ae5f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/assets/pipeline-centered.svg @@ -0,0 +1,23 @@ + + + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/environments/environment.prod.ts b/pkg/new-ui/v1beta1/frontend/src/environments/environment.prod.ts new file mode 100644 index 00000000000..c9669790be1 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/pkg/new-ui/v1beta1/frontend/src/environments/environment.ts b/pkg/new-ui/v1beta1/frontend/src/environments/environment.ts new file mode 100644 index 00000000000..99c3763cad6 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/environments/environment.ts @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/pkg/new-ui/v1beta1/frontend/src/favicon.ico b/pkg/new-ui/v1beta1/frontend/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..997406ad22c29aae95893fb3d666c30258a09537 GIT binary patch literal 948 zcmV;l155mgP)CBYU7IjCFmI-B}4sMJt3^s9NVg!P0 z6hDQy(L`XWMkB@zOLgN$4KYz;j0zZxq9KKdpZE#5@k0crP^5f9KO};h)ZDQ%ybhht z%t9#h|nu0K(bJ ztIkhEr!*UyrZWQ1k2+YkGqDi8Z<|mIN&$kzpKl{cNP=OQzXHz>vn+c)F)zO|Bou>E z2|-d_=qY#Y+yOu1a}XI?cU}%04)zz%anD(XZC{#~WreV!a$7k2Ug`?&CUEc0EtrkZ zL49MB)h!_K{H(*l_93D5tO0;BUnvYlo+;yss%n^&qjt6fZOa+}+FDO(~2>G z2dx@=JZ?DHP^;b7*Y1as5^uphBsh*s*z&MBd?e@I>-9kU>63PjP&^#5YTOb&x^6Cf z?674rmSHB5Fk!{Gv7rv!?qX#ei_L(XtwVqLX3L}$MI|kJ*w(rhx~tc&L&xP#?cQow zX_|gx$wMr3pRZIIr_;;O|8fAjd;1`nOeu5K(pCu7>^3E&D2OBBq?sYa(%S?GwG&_0-s%_v$L@R!5H_fc)lOb9ZoOO#p`Nn`KU z3LTTBtjwo`7(HA6 z7gmO$yTR!5L>Bsg!X8616{JUngg_@&85%>W=mChTR;x4`P=?PJ~oPuy5 zU-L`C@_!34D21{fD~Y8NVnR3t;aqZI3fIhmgmx}$oc-dKDC6Ap$Gy>a!`A*x2L1v0 WcZ@i?LyX}70000 + + + + Frontend + + + + + + + + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/kubeflow.css b/pkg/new-ui/v1beta1/frontend/src/kubeflow.css new file mode 100644 index 00000000000..9cf66731c94 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/kubeflow.css @@ -0,0 +1,9 @@ +body { + --accent-color: #007dfc; + --accent-color-light: #69abff; + --accent-color-dark: #0052c8; + --kubeflow-color: #003c75; + --primary-background-color: #2196f3; + --sidebar-color: #f8fafb; + --border-color: #f4f4f6; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/main.ts b/pkg/new-ui/v1beta1/frontend/src/main.ts new file mode 100644 index 00000000000..fa4e0aef337 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/main.ts @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/pkg/new-ui/v1beta1/frontend/src/polyfills.ts b/pkg/new-ui/v1beta1/frontend/src/polyfills.ts new file mode 100644 index 00000000000..2f258e56c60 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/polyfills.ts @@ -0,0 +1,62 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/pkg/new-ui/v1beta1/frontend/src/styles.scss b/pkg/new-ui/v1beta1/frontend/src/styles.scss new file mode 100644 index 00000000000..fc0cd4d669b --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/styles.scss @@ -0,0 +1,15 @@ +/* You can add global styles to this file, and also import other style files */ +@import '~kubeflow/styles.scss'; + +body { + background-color: white; + margin: 0; +} + +.two-inputs { + display: flex; + + .mat-form-field:nth-child(2) { + margin-left: 12px; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/test.ts b/pkg/new-ui/v1beta1/frontend/src/test.ts new file mode 100644 index 00000000000..b6d614daa24 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/test.ts @@ -0,0 +1,20 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/pkg/new-ui/v1beta1/frontend/tsconfig.app.json b/pkg/new-ui/v1beta1/frontend/tsconfig.app.json new file mode 100644 index 00000000000..add669334a0 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.ts"], + "exclude": ["src/test.ts", "src/**/*.spec.ts"] +} diff --git a/pkg/new-ui/v1beta1/frontend/tsconfig.json b/pkg/new-ui/v1beta1/frontend/tsconfig.json new file mode 100644 index 00000000000..b8102c5f5fc --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "module": "esnext", + "moduleResolution": "node", + "importHelpers": true, + "target": "es2015", + "typeRoots": ["node_modules/@types"], + "paths": { + "@app/environment": ["src/environments/environment"] + }, + "lib": ["es2018", "dom"] + }, + "angularCompilerOptions": { + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true + } +} diff --git a/pkg/new-ui/v1beta1/frontend/tsconfig.spec.json b/pkg/new-ui/v1beta1/frontend/tsconfig.spec.json new file mode 100644 index 00000000000..6400fde7d54 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/pkg/new-ui/v1beta1/hp.go b/pkg/new-ui/v1beta1/hp.go new file mode 100644 index 00000000000..6d1ebfca7a1 --- /dev/null +++ b/pkg/new-ui/v1beta1/hp.go @@ -0,0 +1,223 @@ +package v1beta1 + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strconv" + "strings" + "time" + + commonv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/common/v1beta1" + api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1" +) + +const kfpRunIDAnnotation = "kubeflow-kale.org/kfp-run-uuid" + +func (k *KatibUIHandler) FetchHPJobInfo(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + experimentName := r.URL.Query()["experimentName"][0] + namespace := r.URL.Query()["namespace"][0] + + conn, c := k.connectManager() + defer conn.Close() + + resultText := "trialName,Status" + experiment, err := k.katibClient.GetExperiment(experimentName, namespace) + if err != nil { + log.Printf("GetExperiment from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Printf("Got Experiment") + metricsList := map[string]int{} + metricsName := experiment.Spec.Objective.ObjectiveMetricName + resultText += "," + metricsName + metricsList[metricsName] = 0 + for i, m := range experiment.Spec.Objective.AdditionalMetricNames { + resultText += "," + m + metricsList[m] = i + 1 + } + log.Printf("Got metrics names") + paramList := map[string]int{} + for i, p := range experiment.Spec.Parameters { + resultText += "," + p.Name + paramList[p.Name] = i + len(metricsList) + } + log.Printf("Got Parameters names") + + trialList, err := k.katibClient.GetTrialList(experimentName, namespace) + if err != nil { + log.Printf("GetTrialList from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Printf("Got Trial List") + + // append a column for the Pipeline UID associated with the Trial + if havePipelineUID(trialList.Items) { + resultText += ",KFP Run" + } + + foundPipelineUID := false + for _, t := range trialList.Items { + runUid, ok := t.GetAnnotations()[kfpRunIDAnnotation] + if !ok { + log.Printf("Trial %s has no pipeline run.", t.Name) + runUid = "" + } else { + foundPipelineUID = true + } + + var lastTrialCondition string + + // Take only the latest condition + if len(t.Status.Conditions) > 0 { + lastTrialCondition = string(t.Status.Conditions[len(t.Status.Conditions)-1].Type) + } + + trialResText := make([]string, len(metricsList)+len(paramList)) + + if t.IsSucceeded() || t.IsEarlyStopped() { + obsLogResp, err := c.GetObservationLog( + context.Background(), + &api_pb_v1beta1.GetObservationLogRequest{ + TrialName: t.Name, + StartTime: "", + EndTime: "", + }, + ) + if err != nil { + log.Printf("GetObservationLog from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + for _, m := range obsLogResp.ObservationLog.MetricLogs { + if trialResText[metricsList[m.Metric.Name]] == "" { + trialResText[metricsList[m.Metric.Name]] = m.Metric.Value + } else { + currentValue, _ := strconv.ParseFloat(m.Metric.Value, 64) + bestValue, _ := strconv.ParseFloat(trialResText[metricsList[m.Metric.Name]], 64) + if t.Spec.Objective.Type == commonv1beta1.ObjectiveTypeMinimize && currentValue < bestValue { + trialResText[metricsList[m.Metric.Name]] = m.Metric.Value + } else if t.Spec.Objective.Type == commonv1beta1.ObjectiveTypeMaximize && currentValue > bestValue { + trialResText[metricsList[m.Metric.Name]] = m.Metric.Value + } + } + } + } + for _, trialParam := range t.Spec.ParameterAssignments { + trialResText[paramList[trialParam.Name]] = trialParam.Value + } + resultText += "\n" + t.Name + "," + lastTrialCondition + "," + strings.Join(trialResText, ",") + if foundPipelineUID { + resultText += "," + runUid + } + } + log.Printf("Logs parsed, results:\n %v", resultText) + response, err := json.Marshal(resultText) + if err != nil { + log.Printf("Marshal result text for HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} + +// FetchHPJobTrialInfo returns all metrics for the HP Job Trial +func (k *KatibUIHandler) FetchHPJobTrialInfo(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + trialName := r.URL.Query()["trialName"][0] + namespace := r.URL.Query()["namespace"][0] + conn, c := k.connectManager() + defer conn.Close() + + trial, err := k.katibClient.GetTrial(trialName, namespace) + + if err != nil { + log.Printf("GetTrial from HP job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + objectiveType := trial.Spec.Objective.Type + + // resultArray - array of arrays, where [i][0] - metricName, [i][1] - metricTime, [i][2] - metricValue + var resultArray [][]string + resultArray = append(resultArray, strings.Split("metricName,time,value", ",")) + obsLogResp, err := c.GetObservationLog( + context.Background(), + &api_pb_v1beta1.GetObservationLogRequest{ + TrialName: trialName, + StartTime: "", + EndTime: "", + }, + ) + if err != nil { + log.Printf("GetObservationLog failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // prevMetricTimeValue is the dict, where key = metric name, + // value = array, where [0] - Last metric time, [1] - Best metric value for this time + prevMetricTimeValue := make(map[string][]string) + for _, m := range obsLogResp.ObservationLog.MetricLogs { + parsedCurrentTime, _ := time.Parse(time.RFC3339Nano, m.TimeStamp) + formatCurrentTime := parsedCurrentTime.Format("2006-01-02T15:04:05") + if _, found := prevMetricTimeValue[m.Metric.Name]; !found { + prevMetricTimeValue[m.Metric.Name] = []string{"", ""} + + } + + newMetricValue, err := strconv.ParseFloat(m.Metric.Value, 64) + if err != nil { + log.Printf("ParseFloat for new metric value: %v failed: %v", m.Metric.Value, err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + var prevMetricValue float64 + if prevMetricTimeValue[m.Metric.Name][1] != "" { + prevMetricValue, err = strconv.ParseFloat(prevMetricTimeValue[m.Metric.Name][1], 64) + if err != nil { + log.Printf("ParseFloat for prev metric value: %v failed: %v", prevMetricTimeValue[m.Metric.Name][1], err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + if formatCurrentTime == prevMetricTimeValue[m.Metric.Name][0] && + ((objectiveType == commonv1beta1.ObjectiveTypeMinimize && + newMetricValue < prevMetricValue) || + (objectiveType == commonv1beta1.ObjectiveTypeMaximize && + newMetricValue > prevMetricValue)) { + + prevMetricTimeValue[m.Metric.Name][1] = m.Metric.Value + for i := len(resultArray) - 1; i >= 0; i-- { + if resultArray[i][0] == m.Metric.Name { + resultArray[i][2] = m.Metric.Value + break + } + } + } else if formatCurrentTime != prevMetricTimeValue[m.Metric.Name][0] { + resultArray = append(resultArray, []string{m.Metric.Name, formatCurrentTime, m.Metric.Value}) + prevMetricTimeValue[m.Metric.Name][0] = formatCurrentTime + prevMetricTimeValue[m.Metric.Name][1] = m.Metric.Value + } + } + + var resultText string + for _, metric := range resultArray { + resultText += strings.Join(metric, ",") + "\n" + } + + response, err := json.Marshal(resultText) + if err != nil { + log.Printf("Marshal result text in Trial info failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} diff --git a/pkg/new-ui/v1beta1/nas.go b/pkg/new-ui/v1beta1/nas.go new file mode 100644 index 00000000000..84f6b2b64c4 --- /dev/null +++ b/pkg/new-ui/v1beta1/nas.go @@ -0,0 +1,89 @@ +package v1beta1 + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strconv" + + trialsv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1" + api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1" +) + +func (k *KatibUIHandler) FetchNASJobInfo(w http.ResponseWriter, r *http.Request) { + //enableCors(&w) + experimentName := r.URL.Query()["experimentName"][0] + namespace := r.URL.Query()["namespace"][0] + + responseRaw := make([]NNView, 0) + var architecture string + var decoder string + + conn, c := k.connectManager() + + defer conn.Close() + + trials, err := k.katibClient.GetTrialList(experimentName, namespace) + if err != nil { + log.Printf("GetTrialList from NAS job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Printf("Got Trial List") + + for i, t := range trials.Items { + succeeded := false + for _, condition := range t.Status.Conditions { + if condition.Type == trialsv1beta1.TrialSucceeded { + succeeded = true + } + } + if succeeded { + obsLogResp, err := c.GetObservationLog( + context.Background(), + &api_pb_v1beta1.GetObservationLogRequest{ + TrialName: t.Name, + StartTime: "", + EndTime: "", + }, + ) + if err != nil { + log.Printf("GetObservationLog from NAS job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + metricsName := make([]string, 0) + metricsValue := make([]string, 0) + for _, m := range obsLogResp.ObservationLog.MetricLogs { + metricsName = append(metricsName, m.Metric.Name) + metricsValue = append(metricsValue, m.Metric.Value) + + } + for _, trialParam := range t.Spec.ParameterAssignments { + if trialParam.Name == "architecture" { + architecture = trialParam.Value + } + if trialParam.Name == "nn_config" { + decoder = trialParam.Value + } + } + responseRaw = append(responseRaw, NNView{ + Name: "Generation " + strconv.Itoa(i), + TrialName: t.Name, + Architecture: generateNNImage(architecture, decoder), + MetricsName: metricsName, + MetricsValue: metricsValue, + }) + } + } + log.Printf("Logs parsed, result: %v", responseRaw) + + response, err := json.Marshal(responseRaw) + if err != nil { + log.Printf("Marshal result in NAS job failed: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Write(response) +} diff --git a/pkg/new-ui/v1beta1/types.go b/pkg/new-ui/v1beta1/types.go new file mode 100644 index 00000000000..c949bbda557 --- /dev/null +++ b/pkg/new-ui/v1beta1/types.go @@ -0,0 +1,86 @@ +package v1beta1 + +import ( + v1beta1experiment "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1beta1" + "github.com/kubeflow/katib/pkg/controller.v1beta1/consts" + "github.com/kubeflow/katib/pkg/util/v1beta1/katibclient" +) + +const maxMsgSize = 1<<31 - 1 + +var ( + // namespace = "default" + allowedHeaders = "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization, X-CSRF-Token" + + TrialTemplateLabel = map[string]string{ + consts.LabelTrialTemplateConfigMapName: consts.LabelTrialTemplateConfigMapValue} +) + +type Decoder struct { + Layers int `json:"num_layers"` + InputSize []int `json:"input_size"` + OutputSize []int `json:"output_size"` + Embedding map[int]*Block `json:"embedding"` +} + +type Block struct { + ID int `json:"opt_id"` + Type string `json:"opt_type"` + Param Option `json:"opt_params"` +} + +type Option struct { + FilterNumber string `json:"num_filter"` + FilterSize string `json:"filter_size"` + Stride string `json:"stride"` +} + +type ExperimentView struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + Type string `json:"type"` + Status string `json:"status"` + v1beta1experiment.ExperimentStatus +} + +type TrialTemplatesDataView struct { + ConfigMapNamespace string + ConfigMaps []ConfigMap +} + +type TrialTemplatesResponse struct { + Data []TrialTemplatesDataView +} + +type ConfigMap struct { + ConfigMapName string + Templates []Template +} + +type Template struct { + Path string + Yaml string +} + +type KatibUIHandler struct { + katibClient katibclient.Client + dbManagerAddr string +} + +type NNView struct { + Name string + TrialName string + Architecture string + MetricsName []string + MetricsValue []string +} + +type JobType string + +const ( + ExperimentTypeHP = "hp" + ExperimentTypeNAS = "nas" + ActionTypeAdd = "add" + ActionTypeEdit = "edit" + ActionTypeDelete = "delete" +) diff --git a/pkg/new-ui/v1beta1/util.go b/pkg/new-ui/v1beta1/util.go new file mode 100644 index 00000000000..01387629d2d --- /dev/null +++ b/pkg/new-ui/v1beta1/util.go @@ -0,0 +1,287 @@ +package v1beta1 + +import ( + "encoding/json" + "log" + "net/http" + "sort" + "strconv" + "strings" + + "github.com/kubeflow/katib/pkg/controller.v1beta1/consts" + + gographviz "github.com/awalterschulze/gographviz" + trialv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1" + apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (k *KatibUIHandler) getExperiments(namespace []string) ([]ExperimentView, error) { + experiments := []ExperimentView{} + + el, err := k.katibClient.GetExperimentList(namespace...) + if err != nil { + log.Printf("GetExperimentList failed: %v", err) + return nil, err + } + for _, experiment := range el.Items { + experimentLastCondition, err := experiment.GetLastConditionType() + if err != nil { + log.Printf("GetLastConditionType failed: %v", err) + return nil, err + } + tp := ExperimentTypeHP + if experiment.Spec.NasConfig != nil { + tp = ExperimentTypeNAS + } + + newExperiment := ExperimentView{ + experiment.Name, + experiment.Namespace, + tp, + string(experimentLastCondition), + experiment.Status, + } + + experiments = append(experiments, newExperiment) + } + + return experiments, nil +} + +func enableCors(w *http.ResponseWriter) { + (*w).Header().Set("Content-Type", "text/html; charset=utf-8") + (*w).Header().Set("Access-Control-Allow-Origin", "*") + (*w).Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE") + (*w).Header().Set("Access-Control-Allow-Headers", allowedHeaders) + (*w).Header().Set("Access-Control-Expose-Headers", "Access-Control-*") + (*w).Header().Set("Access-Control-Allow-Credentials", "true") +} + +func havePipelineUID(trials []trialv1beta1.Trial) bool { + for _, t := range trials { + _, ok := t.GetAnnotations()[kfpRunIDAnnotation] + if ok { + return true + } + } + + return false +} + +func (k *KatibUIHandler) getTrialTemplatesViewList() ([]TrialTemplatesDataView, error) { + trialTemplatesDataView := make([]TrialTemplatesDataView, 0) + + // Get all available namespaces + namespaces, err := k.getAvailableNamespaces() + if err != nil { + log.Printf("GetAvailableNamespaces failed: %v", err) + return nil, err + } + + // Get Trial Template ConfigMap for each namespace + for _, ns := range namespaces { + trialTemplatesConfigMapList, err := k.katibClient.GetTrialTemplates(ns) + if err != nil { + log.Printf("GetTrialTemplates failed: %v", err) + return nil, err + } + + if len(trialTemplatesConfigMapList.Items) != 0 { + newTrialTemplatesView := getTrialTemplatesView(trialTemplatesConfigMapList) + // ConfigMap with templates must exists in namespace + if len(newTrialTemplatesView.ConfigMaps) > 0 { + trialTemplatesDataView = append(trialTemplatesDataView, newTrialTemplatesView) + } + } + } + return trialTemplatesDataView, nil +} + +func (k *KatibUIHandler) getAvailableNamespaces() ([]string, error) { + var namespaces []string + + namespaceList, err := k.katibClient.GetNamespaceList() + if err != nil { + namespaces = append(namespaces, consts.DefaultKatibNamespace) + return namespaces, nil + } + for _, ns := range namespaceList.Items { + namespaces = append(namespaces, ns.ObjectMeta.Name) + } + + return namespaces, nil +} + +func getTrialTemplatesView(templatesConfigMapList *apiv1.ConfigMapList) TrialTemplatesDataView { + + trialTemplatesDataView := TrialTemplatesDataView{ + ConfigMapNamespace: templatesConfigMapList.Items[0].ObjectMeta.Namespace, + ConfigMaps: []ConfigMap{}, + } + for _, configMap := range templatesConfigMapList.Items { + newConfigMap := ConfigMap{ + ConfigMapName: configMap.ObjectMeta.Name, + Templates: []Template{}, + } + for key := range configMap.Data { + newTemplate := Template{ + Path: key, + Yaml: configMap.Data[key], + } + newConfigMap.Templates = append(newConfigMap.Templates, newTemplate) + } + + // Sort Trial Templates by Path + sort.SliceStable(newConfigMap.Templates, func(i, j int) bool { + return newConfigMap.Templates[i].Path <= newConfigMap.Templates[j].Path + }) + + // Templates with data must exists in ConfigMap + if len(newConfigMap.Templates) > 0 { + trialTemplatesDataView.ConfigMaps = append(trialTemplatesDataView.ConfigMaps, newConfigMap) + } + + } + return trialTemplatesDataView +} + +func (k *KatibUIHandler) updateTrialTemplates( + updatedConfigMapNamespace, + updatedConfigMapName, + configMapPath, + updatedConfigMapPath, + updatedTemplateYaml, + actionType string) ([]TrialTemplatesDataView, error) { + + templates, err := k.katibClient.GetConfigMap(updatedConfigMapName, updatedConfigMapNamespace) + if err != nil && !(errors.IsNotFound(err) && actionType == ActionTypeAdd) { + log.Printf("GetConfigMap failed: %v", err) + return nil, err + } + + if actionType == ActionTypeAdd { + if len(templates) == 0 { + templates = make(map[string]string) + templates[updatedConfigMapPath] = updatedTemplateYaml + } else { + templates[updatedConfigMapPath] = updatedTemplateYaml + } + } else if actionType == ActionTypeEdit { + delete(templates, configMapPath) + templates[updatedConfigMapPath] = updatedTemplateYaml + } else if actionType == ActionTypeDelete { + delete(templates, updatedConfigMapPath) + } + + templatesConfigMap := &apiv1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: updatedConfigMapName, + Namespace: updatedConfigMapNamespace, + Labels: TrialTemplateLabel, + }, + Data: templates, + } + + // If templates is empty delete Trial template configMap + if len(templates) == 0 { + err = k.katibClient.DeleteRuntimeObject(templatesConfigMap) + if err != nil { + log.Printf("DeleteRuntimeObject failed: %v", err) + return nil, err + } + // If len(templates) == 1 and adding template, we must create new ConfigMap + } else if len(templates) == 1 && actionType == ActionTypeAdd { + err = k.katibClient.CreateRuntimeObject(templatesConfigMap) + if err != nil { + log.Printf("CreateRuntimeObject failed: %v", err) + return nil, err + } + // Otherwise updating configMap + } else { + err = k.katibClient.UpdateRuntimeObject(templatesConfigMap) + if err != nil { + log.Printf("UpdateRuntimeObject failed: %v", err) + return nil, err + } + } + + newTemplates, err := k.getTrialTemplatesViewList() + if err != nil { + log.Printf("getTrialTemplatesViewList: %v", err) + return nil, err + } + + return newTemplates, nil + +} +func getNodeString(block *Block) string { + var nodeString string + switch block.Type { + case "convolution": + nodeString += block.Param.FilterSize + "x" + block.Param.FilterSize + nodeString += " conv\n" + nodeString += block.Param.FilterSize + " channels" + case "separable_convolution": + nodeString += block.Param.FilterSize + "x" + block.Param.FilterSize + nodeString += " sep_conv\n" + nodeString += block.Param.FilterSize + " channels" + case "depthwise_convolution": + nodeString += block.Param.FilterSize + "x" + block.Param.FilterSize + nodeString += " depth_conv\n" + case "reduction": + // TODO: Need to be fixed + nodeString += "3x3 max_pooling" + } + return strconv.Quote(nodeString) +} + +func generateNNImage(architecture string, decoder string) string { + + var architectureInt [][]int + + if err := json.Unmarshal([]byte(architecture), &architectureInt); err != nil { + panic(err) + } + /* + Always has num_layers, input_size, output_size and embeding + Embeding is a map: int to Parameter + Parameter has id, type, Option + + Beforehand substite all ' to " and wrap the string in ` + */ + + replacedDecoder := strings.Replace(decoder, `'`, `"`, -1) + var decoderParsed Decoder + + err := json.Unmarshal([]byte(replacedDecoder), &decoderParsed) + if err != nil { + panic(err) + } + + graphAst, _ := gographviz.ParseString(`digraph G {}`) + graph := gographviz.NewGraph() + if err := gographviz.Analyse(graphAst, graph); err != nil { + panic(err) + } + graph.AddNode("G", "0", map[string]string{"label": strconv.Quote("Input")}) + var i int + for i = 0; i < len(architectureInt); i++ { + graph.AddNode("G", strconv.Itoa(i+1), map[string]string{"label": getNodeString(decoderParsed.Embedding[architectureInt[i][0]])}) + graph.AddEdge(strconv.Itoa(i), strconv.Itoa(i+1), true, nil) + for j := 1; j < i+1; j++ { + if architectureInt[i][j] == 1 { + graph.AddEdge(strconv.Itoa(j-1), strconv.Itoa(i+1), true, nil) + } + } + } + graph.AddNode("G", strconv.Itoa(i+1), map[string]string{"label": strconv.Quote("GlobalAvgPool")}) + graph.AddEdge(strconv.Itoa(i), strconv.Itoa(i+1), true, nil) + graph.AddNode("G", strconv.Itoa(i+2), map[string]string{"label": strconv.Quote("FullConnect\nSoftmax")}) + graph.AddEdge(strconv.Itoa(i+1), strconv.Itoa(i+2), true, nil) + graph.AddNode("G", strconv.Itoa(i+3), map[string]string{"label": strconv.Quote("Output")}) + graph.AddEdge(strconv.Itoa(i+2), strconv.Itoa(i+3), true, nil) + s := graph.String() + return s +}