From 5e26c37ebc52493050df6da1b727dfcd6ddaa172 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 30 Aug 2021 16:53:09 +0200 Subject: [PATCH 01/35] Added the SwanProjects extension for jupyter lab 3 --- README.md | 3 +- SwanProjects/.bumpversion.cfg | 11 + SwanProjects/MANIFEST.in | 29 + SwanProjects/README.md | 35 + SwanProjects/SECURITY.md | 18 + SwanProjects/bin/swan_bash | 33 + SwanProjects/bin/swan_env | 83 + SwanProjects/bin/swan_kmspecs | 130 + SwanProjects/install.json | 5 + .../nb-config/swanprojects.json | 7 + .../server-config/swanprojects.json | 7 + SwanProjects/package.json | 100 + SwanProjects/pyproject.toml | 17 + SwanProjects/setup.py | 91 + SwanProjects/src/Components.tsx | 100 + SwanProjects/src/ProjectDialog.tsx | 218 + SwanProjects/src/ProjectWidget.tsx | 205 + SwanProjects/src/dialog.tsx | 48 + SwanProjects/src/icons.ts | 29 + SwanProjects/src/index.ts | 140 + SwanProjects/src/request.ts | 130 + SwanProjects/src/svg.d.ts | 6 + SwanProjects/src/theme-provider.tsx | 49 + SwanProjects/style/base.css | 108 + SwanProjects/style/cms.svg | 239 + SwanProjects/style/index.css | 1 + SwanProjects/style/index.js | 1 + .../style/kernels/python/logo-32x32.png | Bin 0 -> 1084 bytes .../style/kernels/python/logo-64x64.png | Bin 0 -> 2180 bytes SwanProjects/style/list-alt.svg | 1 + SwanProjects/style/project.svg | 1 + SwanProjects/style/sft.svg | 1190 +++ SwanProjects/swanprojects/__init__.py | 44 + SwanProjects/swanprojects/_version.py | 20 + SwanProjects/swanprojects/handlers.py | 289 + .../swanprojects/kernelmanager/__init__.py | 0 .../kernelmanager/kernelspecmanager.py | 131 + .../kernelmanager/resources/logo-32x32.png | Bin 0 -> 1084 bytes .../kernelmanager/resources/logo-64x64.png | Bin 0 -> 2180 bytes SwanProjects/swanprojects/stacks.json | 50 + SwanProjects/swanprojects/static/index.html | 10 + SwanProjects/swanprojects/utils.py | 107 + SwanProjects/tsconfig.json | 24 + SwanProjects/yarn.lock | 6362 +++++++++++++++++ 44 files changed, 10071 insertions(+), 1 deletion(-) create mode 100644 SwanProjects/.bumpversion.cfg create mode 100644 SwanProjects/MANIFEST.in create mode 100644 SwanProjects/README.md create mode 100644 SwanProjects/SECURITY.md create mode 100755 SwanProjects/bin/swan_bash create mode 100755 SwanProjects/bin/swan_env create mode 100755 SwanProjects/bin/swan_kmspecs create mode 100644 SwanProjects/install.json create mode 100644 SwanProjects/jupyter-config/nb-config/swanprojects.json create mode 100644 SwanProjects/jupyter-config/server-config/swanprojects.json create mode 100644 SwanProjects/package.json create mode 100644 SwanProjects/pyproject.toml create mode 100644 SwanProjects/setup.py create mode 100644 SwanProjects/src/Components.tsx create mode 100644 SwanProjects/src/ProjectDialog.tsx create mode 100644 SwanProjects/src/ProjectWidget.tsx create mode 100644 SwanProjects/src/dialog.tsx create mode 100644 SwanProjects/src/icons.ts create mode 100644 SwanProjects/src/index.ts create mode 100644 SwanProjects/src/request.ts create mode 100644 SwanProjects/src/svg.d.ts create mode 100644 SwanProjects/src/theme-provider.tsx create mode 100644 SwanProjects/style/base.css create mode 100644 SwanProjects/style/cms.svg create mode 100644 SwanProjects/style/index.css create mode 100644 SwanProjects/style/index.js create mode 100644 SwanProjects/style/kernels/python/logo-32x32.png create mode 100644 SwanProjects/style/kernels/python/logo-64x64.png create mode 100644 SwanProjects/style/list-alt.svg create mode 100644 SwanProjects/style/project.svg create mode 100644 SwanProjects/style/sft.svg create mode 100644 SwanProjects/swanprojects/__init__.py create mode 100644 SwanProjects/swanprojects/_version.py create mode 100644 SwanProjects/swanprojects/handlers.py create mode 100644 SwanProjects/swanprojects/kernelmanager/__init__.py create mode 100644 SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py create mode 100644 SwanProjects/swanprojects/kernelmanager/resources/logo-32x32.png create mode 100644 SwanProjects/swanprojects/kernelmanager/resources/logo-64x64.png create mode 100644 SwanProjects/swanprojects/stacks.json create mode 100644 SwanProjects/swanprojects/static/index.html create mode 100644 SwanProjects/swanprojects/utils.py create mode 100644 SwanProjects/tsconfig.json create mode 100644 SwanProjects/yarn.lock diff --git a/README.md b/README.md index a6fdcb22..847e8c5b 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,5 @@ Repository that stores all the Jupyter extensions for SWAN. * [SwanNotebookViewer](SwanNotebookViewer) - Read-only mode for opening notebooks (used from the Sharing interface) inside Jupyter Notebooks * [SwanNotifications](SwanNotifications) - Extension to display notifications to users * [SwanOauthRenew](SwanOauthRenew) - Extension that fetches the latest oAuth tokens from JupyterHub and writes to the file observed by EOS -* [SwanShare](SwanShare) - Jupyter Notebooks/CERNBox sharing integration used by SwanContents \ No newline at end of file +* [SwanShare](SwanShare) - Jupyter Notebooks/CERNBox sharing integration used by SwanContents +* [SwanProjects](SwanProjects) - Jupyter Lab extension with backend and dialogs to Create/Edit projects, also have a customized KerneSpecManager to handle kernels in multiple environments. \ No newline at end of file diff --git a/SwanProjects/.bumpversion.cfg b/SwanProjects/.bumpversion.cfg new file mode 100644 index 00000000..7f6a9647 --- /dev/null +++ b/SwanProjects/.bumpversion.cfg @@ -0,0 +1,11 @@ +[bumpversion] +current_version = 0.1.0 +commit = True +tag = True +tag_name = SwanProjects/v{new_version} +message = SwanProjects v{new_version} + +[bumpversion:file:swanprojects/_version.py] + +[bumpversion:file:package.json] + diff --git a/SwanProjects/MANIFEST.in b/SwanProjects/MANIFEST.in new file mode 100644 index 00000000..e474447c --- /dev/null +++ b/SwanProjects/MANIFEST.in @@ -0,0 +1,29 @@ +include LICENSE +include README.md +include SECURITY.md +include pyproject.toml +recursive-include jupyter-config *.json + +include swanprojects/kernelmanager/resources/* +include swanprojects/stacks.json +include swanprojects/static/index.html +include package.json +include install.json +include ts*.json +include yarn.lock + +graft swanprojects/labextension + +# Javascript files +graft src +graft style +prune **/node_modules +prune lib +prune binder + +# Patterns to exclude from any directory +global-exclude *~ +global-exclude *.pyc +global-exclude *.pyo +global-exclude .git +global-exclude .ipynb_checkpoints diff --git a/SwanProjects/README.md b/SwanProjects/README.md new file mode 100644 index 00000000..3bcf960c --- /dev/null +++ b/SwanProjects/README.md @@ -0,0 +1,35 @@ +# SwanProjects + +Server and Lab extension that provides: +* In the backend, the endpoints to: + * Create and edit projects + * Get project information + * Get software stack information + * Customized Kernel Spec Manager to handle kernel metadata. +* In the Lab extension: + * React dialogs with a set of components that allows to create and edit projects + * LabIcons required for the dialogs + +## Requirements + +JupyterLab~=3.0 and SwanContents + +## Install + +Install the package and the lab extension: + +```bash +pip install swanprojects +``` + +To replace the default Jupyter Contents Manager and Kernel Spec Manager in the JupyterLab Notebook configuration (i.e in `jupyter_notebook_config.py`), set the following: + +```python +c.NotebookApp.default_url = 'lab' +c.NotebookApp.contents_manager_class = 'swancontents.filemanager.swanfilemanager.SwanFileManager' +c.NotebookApp.kernel_spec_manager_class = 'swanprojects.kernelmanager.kernelspecmanager.SwanKernelSpecManager' +c.KernelSpecManager.ensure_native_kernel = False + +c.SwanProjects.stacks_path=path_to_stacks.json +c.SwanKSMConfig.kernel_resources=path_to_native_kernel_resources +``` diff --git a/SwanProjects/SECURITY.md b/SwanProjects/SECURITY.md new file mode 100644 index 00000000..232a333e --- /dev/null +++ b/SwanProjects/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.1.0 | :white_check_mark: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash new file mode 100755 index 00000000..0aca7e27 --- /dev/null +++ b/SwanProjects/bin/swan_bash @@ -0,0 +1,33 @@ +#!/bin/bash -i +#Author Omar.Zapata@cern.ch 2021 + +clear +if ! [ -x "$(command -v jq)" ]; then + echo 'Error: jq is not installed.' >&2 + sleep 60 + exit 1 +fi +if [[ $# -gt 0 ]] ; then + PROJECT=$1 + PROJECT_PATH="$HOME/SWAN_projects/$PROJECT" + PROJECT_FILE="$PROJECT_PATH/.swanproject" + + if [ -d "$PROJECT_PATH" ] + then + STACK=`jq '.stack' $PROJECT_FILE` + RELEASE=`jq '.release' $PROJECT_FILE` + PLATFORM=`jq '.platform' $PROJECT_FILE` + USER_SCRIPT="$PROJECT_PATH/.userscript" + env -i HOME=$HOME OAUTH2_FILE=$OAUTH2_FILE \ + OAUTH2_TOKEN=$OAUTH2_TOKEN \ + OAUTH_INSPECTION_ENDPOINT=$OAUTH_INSPECTION_ENDPOINT \ + PROJECT=$PROJECT \ + PROJECT_PATH=$PROJECT_PATH PS1="$PS1" \ + PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:$HOME/.local/bin/" \ + bash -c "swan_env $PROJECT $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " + else + echo "Error: project $PROJECT_PATH doesn't exists" >&2 + sleep 60 + exit 1 + fi +fi \ No newline at end of file diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env new file mode 100755 index 00000000..5be7a923 --- /dev/null +++ b/SwanProjects/bin/swan_env @@ -0,0 +1,83 @@ +#!/bin/bash +#DEFINITIONS: +# STACK: LCG/CMSSW +# RELEASE: version of the stack ex (LCG_99) +# PLATFORM: usually os and compiler version +# USER_SCRIPT: customized bash script provided by the user +# CWD: working directory path +PARAMETERS=($@) #WARNING: empty options disappear + +#Checking if jq is installed +if ! [ -x "$(command -v jq)" ]; then + echo 'Error: jq is not installed.' >&2 + sleep 60 + exit 1 +fi + +#Checking if we have more that 2 parameters +if [[ $# -lt 2 ]] ; then + echo 'Error: project name and commands required.' >&2 + echo 'Example: swan_env myproject python --version' >&2 + sleep 60 + exit 1 +fi + +PROJECT=$1 +PROJECT_PATH="$HOME/SWAN_projects/$PROJECT" +PROJECT_FILE="$PROJECT_PATH/.swanproject" +STACK=`jq -r '.stack' $PROJECT_FILE` +RELEASE=`jq -r '.release' $PROJECT_FILE` +PLATFORM=`jq -r '.platform' $PROJECT_FILE` +USER_SCRIPT="$PROJECT_PATH/.userscript" + +CWD="$2" + +i=0 +if [ "$CWD" != "" ]; then + i=$((i+1)) +fi + +for j in $(seq 0 1 $((i)));do + unset PARAMETERS[$j] +done + +COMMAND=${PARAMETERS[@]} + +if [[ $STACK == "LCG" ]]; then + #example: swan_env SFT LCG_96 x86_64-centos7-gcc8-opt "" $PWD which python + CVMFS_PATH="/cvmfs/sft.cern.ch/lcg/views/$RELEASE/$PLATFORM/setup.sh" + if [ "$SWAN_ENV_SILENCE" != "1" ]; then + echo "Loading $RELEASE with plafortm $PLATFORM " + fi + source $CVMFS_PATH +fi + +if [[ $STACK == "CMSSW" ]]; then + #example: swan_env CMS CMSSW_10_6_19 slc7_amd64_gcc820 ipython kernelspec list + source /cvmfs/cms.cern.ch/cmsset_default.sh + if [ "$SWAN_ENV_SILENCE" != "1" ]; then + echo "Loading $RELEASE with plafortm $PLATFORM " + fi + CMS_BASEDIR=/cvmfs/cms.cern.ch + CMSSW=$RELEASE + SCRAM=$PLATFORM + + export PATH=${CMS_BASEDIR}/common:$PATH + #by default I will load the environment in the cvmfs path in read only + cd /cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW + eval `scramv1 runtime -sh` + #requires to prepend the lib and bin paths + export LD_LIBRARY_PATH=/cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/lib/:$LD_LIBRARY_PATH + export PATH=/cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/bin/:$PATH + +fi +export SWAN_STACK="$RELEASE($PLATFORM)" +export SWAN_PROJECT_NAME=$PROJECT +export SWAN_PROJECT_PATH=$PROJECT_PATH + +if [ "$USER_SCRIPT" != "" ] && [ -f "$USER_SCRIPT" ]; then + . ${USER_SCRIPT} +fi +cd $CWD +$COMMAND + diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs new file mode 100755 index 00000000..67d8f4ab --- /dev/null +++ b/SwanProjects/bin/swan_kmspecs @@ -0,0 +1,130 @@ +#!/usr/bin/env python +import argparse +import json +import os +import pprint +import subprocess +import sys +from distutils.spawn import find_executable +from shutil import rmtree + +def _checkipykernel(project_path,python_interpreter,python_version): + python_code='import ipykernel' + command = [python_interpreter,"-c",python_code] + proc = subprocess.Popen(command, stdout = subprocess.PIPE) + proc.wait() + data = proc.stdout.read().decode("utf-8") + print(data) + proc.communicate() + if proc.returncode !=0 : + print("Error ipykernel not found for {} in project {}".format(python_version,project_path)) + return proc.returncode + +def check_native_kernel(project_path): + project_file = project_path+os.path.sep+".swanproject" + f = open(project_file,"r+") + project_data = f.read() + if project_data.strip() == "": + project_data = {} + else: + project_data = json.loads(project_data) + print(project_data) + f.seek(0) + python2 = find_executable("python2") + if python2 is not None: + print("python2 found = "+python2) + project_data["python2"]={"found":True,"path":python2} + rcode = _checkipykernel(project_path,python2,"python2") + if rcode == 0: + project_data["python2"]["ipykernel"] = True + else: + project_data["python2"]["ipykernel"] = False + else: + project_data["python2"]["found"] = False + + python3 = find_executable("python3") + if python3 is not None: + print("python3 found = "+python3) + project_data["python3"]={"found":True,"path":python3} + rcode = _checkipykernel(project_path,python3,"python3") + if rcode == 0: + project_data["python3"]["ipykernel"] = True + else: + project_data["python3"]["ipykernel"] = False + else: + project_data["python3"]["found"] = False + + f.seek(0) + f.truncate() + json.dump(project_data,f,indent=4) + f.close() + +def save_kernel_paths(project_path): + from jupyter_core.paths import jupyter_path + kernels_blacklist_paths = [os.environ["HOME"]+os.sep+'.local/share/jupyter/kernels','/usr/local/share/jupyter/kernels','/usr/share/jupyter/kernels'] + tmp_paths = jupyter_path('kernels') + paths = [] + for path in tmp_paths: + found=False + for bl_path in kernels_blacklist_paths: + if bl_path in path: + found=True + if not found: + paths.append(path) + project_file = project_path+os.path.sep+".swanproject" + + with open(project_file,"r+") as f: + project_data = f.read() + if project_data == "": + project_data = {} + else: + project_data = json.loads(project_data) + f.seek(0) + f.truncate() + if "kernel_dirs" in list(project_data.keys()): + project_data["kernel_dirs"] = project_data["kernel_dirs"] + paths + else: + project_data["kernel_dirs"] = paths + json.dump(project_data,f,indent=4) + +def generate_ksminfo(project_path): + check_native_kernel(project_path) + save_kernel_paths(project_path) + +def swan_kmspecs(project_name): + command = ["env","-i","HOME=%s"%os.environ["HOME"]] + #checking if we are on EOS to add the env variables + #we required this to read/write in a isolate environment with EOS + if "OAUTH2_FILE" in os.environ: + command.append("OAUTH2_FILE=%s"%os.environ["OAUTH2_FILE"]) + if "OAUTH2_TOKEN" in os.environ: + command.append("OAUTH2_TOKEN=%s"%os.environ["OAUTH2_TOKEN"]) + if "OAUTH_INSPECTION_ENDPOINT" in os.environ: + command.append("OAUTH_INSPECTION_ENDPOINT=%s"%os.environ["OAUTH_INSPECTION_ENDPOINT"]) + + #special case when the package was not installed like root, useful for development + command.append("PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:{}/.local/bin/".format(os.environ["HOME"])) + command += ["/bin/bash","swan_env",project_name, ".","python",__file__,"--generate_ksminfo","--project_name",project_name] + print(" ".join(command)) + proc = subprocess.Popen(command, stdout = subprocess.PIPE) + proc.wait() + data = proc.stdout.read().decode("utf-8") + proc.communicate() + if proc.returncode !=0 : + print("Error creating navite kernel for project {}".format(project_name)) + + return proc.returncode + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Swan Enviroment Kernel Manager CMD options.') + parser.add_argument('--project_name', type=str,required=True, help='Project name') + parser.add_argument('--generate_ksminfo', action='store_true',default=None, help='Generates Kernel Spec Manager info') + args = parser.parse_args() + project_name = args.project_name + + project_path = os.environ["HOME"]+"/SWAN_projects/"+project_name + if args.generate_ksminfo: + generate_ksminfo(project_path) + else: + rcode = swan_kmspecs(project_name) + sys.exit(rcode) diff --git a/SwanProjects/install.json b/SwanProjects/install.json new file mode 100644 index 00000000..ac227bdf --- /dev/null +++ b/SwanProjects/install.json @@ -0,0 +1,5 @@ +{ + "packageManager": "python", + "packageName": "swanprojects", + "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package swanprojects" +} diff --git a/SwanProjects/jupyter-config/nb-config/swanprojects.json b/SwanProjects/jupyter-config/nb-config/swanprojects.json new file mode 100644 index 00000000..863446e6 --- /dev/null +++ b/SwanProjects/jupyter-config/nb-config/swanprojects.json @@ -0,0 +1,7 @@ +{ + "NotebookApp": { + "nbserver_extensions": { + "swanprojects": true + } + } +} diff --git a/SwanProjects/jupyter-config/server-config/swanprojects.json b/SwanProjects/jupyter-config/server-config/swanprojects.json new file mode 100644 index 00000000..16bb2254 --- /dev/null +++ b/SwanProjects/jupyter-config/server-config/swanprojects.json @@ -0,0 +1,7 @@ +{ + "ServerApp": { + "jpserver_extensions": { + "swanprojects": true + } + } +} diff --git a/SwanProjects/package.json b/SwanProjects/package.json new file mode 100644 index 00000000..348d5b29 --- /dev/null +++ b/SwanProjects/package.json @@ -0,0 +1,100 @@ +{ + "name": "@swan/swanprojects", + "version": "0.1.0", + "description": "SWAN JupyterLab extension for projects", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/swan-cern/jupyter-extensions", + "bugs": { + "url": "https://github.com/swan-cern/jupyter-extensions/issues" + }, + "license": "AGPL-3.0", + "author": "SWAN Admins", + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "style/index.js" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "repository": { + "type": "git", + "url": "https://github.com/swan-cern/jupyter-extensions.git" + }, + "scripts": { + "build": "jlpm run build:lib && jlpm run build:labextension:dev", + "build:all": "jlpm run build:lib && jlpm run build:labextension", + "build:labextension": "jupyter labextension build .", + "build:labextension:dev": "jupyter labextension build --development True .", + "build:lib": "tsc", + "build:prod": "jlpm run clean && jlpm run build:lib && jlpm run build:labextension", + "clean": "jlpm run clean:lib", + "clean:all": "jlpm run clean:lib && jlpm run clean:labextension", + "clean:labextension": "rimraf swanprojects/labextension", + "clean:lib": "rimraf lib tsconfig.tsbuildinfo", + "eslint": "eslint . --ext .ts,.tsx --fix", + "eslint:check": "eslint . --ext .ts,.tsx", + "flake8": "flake8 . --count --ignore=C901 --exit-zero --max-complexity=10 --max-line-length=127 --statistics", + "install:extension": "jlpm run build", + "link": "jupyter labextension link . --no-build", + "prepare": "jlpm run clean && jlpm run build:prod", + "watch": "run-p watch:src watch:labextension", + "watch:labextension": "jupyter labextension watch .", + "watch:src": "tsc -w" + }, + "dependencies": { + "@jupyterlab/application": "^3.0.11", + "@jupyterlab/apputils": "^3.0.10", + "@jupyterlab/codeeditor": "^3.0.9", + "@jupyterlab/codemirror": "^3.0.9", + "@jupyterlab/filebrowser": "^3.0.11", + "@jupyterlab/launcher": "^3.0.10", + "@jupyterlab/mainmenu": "^3.0.10", + "@jupyterlab/services": "^6.0.10", + "@jupyterlab/translation": "^3.0.10", + "@jupyterlab/ui-components": "^3.0.8", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.11.2", + "react-select": "^4.3.0", + "react-tooltip": "^4.2.15" + }, + "devDependencies": { + "@jupyterlab/builder": "^3.0.0", + "@types/react-select": "^4.0.13", + "@typescript-eslint/eslint-plugin": "^4.8.1", + "@typescript-eslint/parser": "^4.8.1", + "eslint": "^7.14.0", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-jsdoc": "^22.0.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-react": "^7.18.3", + "mkdirp": "^1.0.3", + "npm-run-all": "^4.1.5", + "prettier": "^2.1.1", + "rimraf": "^3.0.2", + "typescript": "~4.1.3" + }, + "sideEffects": [ + "style/*.css", + "style/index.js" + ], + "jupyterlab": { + "discovery": { + "server": { + "managers": [ + "pip" + ], + "base": { + "name": "swanprojects" + } + } + }, + "extension": true, + "outputDir": "swanprojects/labextension" + }, + "styleModule": "style/index.js" +} diff --git a/SwanProjects/pyproject.toml b/SwanProjects/pyproject.toml new file mode 100644 index 00000000..6245f7c7 --- /dev/null +++ b/SwanProjects/pyproject.toml @@ -0,0 +1,17 @@ +[build-system] +requires = ["jupyter_packaging~=0.10,<2", "jupyterlab~=3.0"] +build-backend = "jupyter_packaging.build_api" + +[tool.jupyter-packaging.options] +skip-if-exists = ["swanprojects/labextension/static/style.js"] +ensured-targets = ["swanprojects/labextension/static/style.js", "swanprojects/labextension/package.json"] + +[tool.jupyter-packaging.builder] +factory = "jupyter_packaging.npm_builder" + +[tool.jupyter-packaging.build-args] +build_cmd = "build:prod" +npm = ["jlpm"] + +[tool.check-manifest] +ignore = ["swanprojects/labextension/**", "yarn.lock", ".*", "package-lock.json"] diff --git a/SwanProjects/setup.py b/SwanProjects/setup.py new file mode 100644 index 00000000..4fd78d7c --- /dev/null +++ b/SwanProjects/setup.py @@ -0,0 +1,91 @@ +""" +swanprojects setup +""" +import site +import sys +import json +from pathlib import Path + +import setuptools + +HERE = Path(__file__).parent.resolve() + +site.ENABLE_USER_SITE = "--user" in sys.argv[1:] + +# The name of the project +name = "swanprojects" + +lab_path = (HERE / name.replace("-", "_") / "labextension") + +# Representative files that should exist after a successful build +ensured_targets = [ + str(lab_path / "package.json"), + str(lab_path / "static/style.js") +] + +labext_name = "@swan/swanprojects" + +data_files_spec = [ + ("share/jupyter/labextensions/%s" % + labext_name, str(lab_path.relative_to(HERE)), "**"), + ("share/jupyter/labextensions/%s" % labext_name, str('.'), "install.json"), + ("etc/jupyter/jupyter_server_config.d", "jupyter-config/server-config", "swanprojects.json"), + # For backward compatibility with notebook server + ("etc/jupyter/jupyter_notebook_config.d", + "jupyter-config/nb-config", "swanprojects.json") +] + +long_description = (HERE / "README.md").read_text() + +# Get the package info from package.json +pkg_json = json.loads((HERE / "package.json").read_bytes()) + +setup_args = dict( + name=name, + version=pkg_json["version"], + url=pkg_json["homepage"], + author=pkg_json["author"], + description=pkg_json["description"], + license=pkg_json["license"], + long_description=long_description, + long_description_content_type="text/markdown", + scripts=['bin/swan_env', 'bin/swan_bash', 'bin/swan_kmspecs'], + packages=setuptools.find_packages(), + install_requires=[ + "jupyter_server>=1.6,<2" + ], + zip_safe=False, + include_package_data=True, + python_requires=">=3.6", + platforms="Linux, Mac OS X, Windows", + keywords=["Jupyter", "JupyterLab", "JupyterLab3"], + classifiers=[ + "License :: OSI Approved :: GNU Affero General Public License v3", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Framework :: Jupyter", + ], +) + +try: + from jupyter_packaging import ( + wrap_installers, + npm_builder, + get_data_files + ) + post_develop = npm_builder( + build_cmd="install:extension", source_dir="src", build_dir=lab_path + ) + setup_args['cmdclass'] = wrap_installers( + post_develop=post_develop, ensured_targets=ensured_targets) + setup_args['data_files'] = get_data_files(data_files_spec) +except ImportError as e: + print(e) + pass + +if __name__ == "__main__": + setuptools.setup(**setup_args) diff --git a/SwanProjects/src/Components.tsx b/SwanProjects/src/Components.tsx new file mode 100644 index 00000000..e2b9ce63 --- /dev/null +++ b/SwanProjects/src/Components.tsx @@ -0,0 +1,100 @@ +// Copyright (c) SWAN Development Team. +// Author: Omar.Zapata@cern.ch 2021 +import { classes, LabIcon } from '@jupyterlab/ui-components'; +import * as React from 'react'; +import ReactTooltip from 'react-tooltip'; + +/** + * @param id Id for the html element + * @param message Message to display in the tooltip + * @returns React Element + */ +export function HelpTooltip(props: { + id: string; + message: string; +}): React.ReactElement { + return ( +
+
+ + ? + + `${dataTip}`} + /> +
+
+ ); +} + +/** + * A pure tsx component for a launcher card. + * + * @param label - Text for the Card + * @param icon - Icon for the Card + * @param isSelected - Helps to know the selected stack. + * @param updateCallback - Callback to update stacks on other components + * @returns a vdom `VirtualElement` for the launcher card. + */ +export function Card(props: { + label: string; + icon: LabIcon; + isSelected?: boolean; + updateCallback: (stack: string) => void; +}): React.ReactElement { + const title = props.label; + // Build the onclick handler. + const onclick = (): void => { + // If an item has already been launched, + // don't try to launch another. + props.updateCallback(props.label); + }; + + const onkeypress = (event: React.KeyboardEvent): void => { + if (event.key === 'Enter') { + onclick(); + } + }; + return ( +
+
+ { + + } +
+
+

{props.label}

+
+
+ ); +} diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx new file mode 100644 index 00000000..04e420c3 --- /dev/null +++ b/SwanProjects/src/ProjectDialog.tsx @@ -0,0 +1,218 @@ +// Copyright (c) SWAN Team. +// Author: Omar Zapata CERN 2021 + +import { showErrorMessage } from '@jupyterlab/apputils'; +import { showDialog } from './dialog'; +import { + contentRequest, + createProjectRequest, + editProjectRequest +} from './request'; +import { Spinner } from '@jupyterlab/apputils'; +import { CommandRegistry } from '@lumino/commands'; +/** + * Namespace for project dialogs + */ +export namespace ProjectDialog { + export interface ISWANOptions { + name?: string; + stack?: string; + release?: string; + platform?: string; + user_script?: string; + corrupted?: boolean; + stacks_options?: { [stack: string]: { [release: string]: Array } }; + } + + /** + * variables from backend response + */ + export interface ISWANReqResponse { + status: boolean; + project_dir: string; + msg: string; + } + + /** + * Create and show a modal dialog to create or modify projects. + * + * @param options - The dialog setup options. + * @param create - true for a new project, false to modify. + * @param commands - CommandRegistry object + * @param theme - colors in the interface 'light' | 'dark'. + * @returns A promise that resolves with whether the dialog was accepted + */ + // eslint-disable-next-line no-inner-declarations + export async function OpenModal( + options: ISWANOptions, + create: boolean, + commands: CommandRegistry, + theme: 'light' | 'dark' + ): Promise { + const _spinner = new Spinner(); + const old_options = Object.assign({}, options); + + function startSpinner(): void { + /** + * Function to start the spiner in the SwanLauncer, embed in the html tag with id jp-main-dock-panel. + */ + const node = document.getElementById('jp-main-dock-panel'); + node?.appendChild(_spinner.node); + node?.focus(); + _spinner.activate(); + _spinner.show(); + _spinner.node.focus(); + } + + /** + * hides the spiner from the component + */ + function stopSpinner(): void { + _spinner.hide(); + } + + let valid = false; + let dialogResult: { + changesSaved: boolean; + newOptions?: ISWANOptions; + } | null = null; + do { + dialogResult = await showDialog({ + ...options, + theme + }); + if (dialogResult?.changesSaved && dialogResult?.newOptions) { + options = dialogResult.newOptions; + if (options.name?.trim() !== '') { + //check is project already exists + if (create) { + const content = await contentRequest( + 'SWAN_projects/' + options.name + ).catch((): void => { + //not message here, it is not needed, + //I am checking if the directory doesn't exist in order + //to make valid the creation of the project folder. + }); + if (content === undefined) { + valid = true; + } else { + await showErrorMessage( + 'Invalid project name', + 'Project already exists.' + ); + valid = false; + } + } else { + //this is a special case for editing because I need to check that the new name of the project doesn't exists. + if (old_options.name !== options.name) { + const content = await contentRequest( + 'SWAN_projects/' + options.name + ).catch(() => { + //not message here, it is not needed, + //I am checking if the directory doesn't exist in order + //to make valid the edition of the name of the project folder. + }); + if (content === undefined) { + valid = true; //folder doesn't exists, then I can to raname the project. + } else { + await showErrorMessage( + 'Invalid project name', + 'Project already exists.' + ); + valid = false; + continue; + } + } + + if (options.corrupted) { + valid = true; + break; + } + + //verifying that options where changed, othewise I will not send the request + if (JSON.stringify(old_options) !== JSON.stringify(options)) { + valid = true; + } else { + valid = false; + } + } + } + + if (options.name?.trim() === '') { + await showErrorMessage( + 'Invalid project name', + 'Select a valid (non-empty) project name.' + ); + valid = false; + } + } else { + valid = true; + } + } while (!valid); + if (dialogResult.changesSaved) { + startSpinner(); + if (create) { + await createProjectRequest(options) + .then((res: ISWANReqResponse) => { + if (res.status) { + commands.execute('filebrowser:go-to-path', { + path: res.project_dir, + showBrowser: false + }); + } else { + stopSpinner(); + showErrorMessage('Error creating project', res.msg); + } + return res; + }) + .catch((msg: any): void => { + stopSpinner(); + showErrorMessage('Error creating project', msg); + }); + } else { + await commands + .execute('filebrowser:go-to-path', { + path: '/SWAN_projects', + showBrowser: false + }) + .then(async () => { + await editProjectRequest(old_options, options) + .then(async (res: ISWANReqResponse) => { + if (res.status) { + await commands + .execute('filebrowser:go-to-path', { + path: res.project_dir, + showBrowser: false + }) + .catch((msg: any) => { + stopSpinner(); + console.log( + 'Error moving from edited project ' + old_options.name + ); + console.log(msg); + }); + } else { + stopSpinner(); + showErrorMessage('Error editing project', res.msg); + } + return res; + }) + .catch((msg: any) => { + stopSpinner(); + console.log('Error editing project: ' + old_options.name); + console.log(msg); + }); + }) + .catch((msg: any) => { + stopSpinner(); + console.log( + 'Error moving to /SWAN_projects to edit the project: ' + + old_options.name + ); + console.log(msg); + }); + } + stopSpinner(); + } + } +} diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx new file mode 100644 index 00000000..5a4bcc4d --- /dev/null +++ b/SwanProjects/src/ProjectWidget.tsx @@ -0,0 +1,205 @@ +// Copyright (c) SWAN Development Team. +// Author: Omar.Zapata@cern.ch 2021 + +import * as React from 'react'; + +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import MenuItem from '@material-ui/core/MenuItem'; + +import { Card, HelpTooltip } from './Components'; +export interface IStackOptions { + visible: boolean; +} +import { swanProjectIcon, sftIcon, cmsIcon } from './icons'; + +import { ProjectDialog } from './ProjectDialog'; + +export const ProjectWidget: React.FunctionComponent<{ + options: ProjectDialog.ISWANOptions; + onSubmit: (selectedOptions: ProjectDialog.ISWANOptions) => void; + onCancel: () => void; +}> = props => { + const options = props.options; + const [projectName, setProjectName] = React.useState(options.name || ''); + + const availableStacks = Object.keys(options.stacks_options); + const defaultStack = availableStacks.includes(options.stack) + ? options.stack + : availableStacks[0]; + const [stack, setStack] = React.useState(defaultStack); + + const availableReleases = Object.keys(options.stacks_options[stack]); + const defaultRelease = availableReleases.includes(options.release) + ? options.release + : availableReleases[0]; + const [release, setRelease] = React.useState(defaultRelease); + + const availablePlatforms = options.stacks_options[stack][release]; + const defaultPlatform = availablePlatforms.includes(options.platform) + ? options.platform + : availablePlatforms[0]; + const [platform, setPlatform] = React.useState(defaultPlatform); + + const [userScript, setUserScript] = React.useState(options.user_script || ''); + + const onClickSubmit = () => { + props.onSubmit({ + name: projectName, + stack, + release, + platform, + user_script: userScript, + stacks_options: options.stacks_options // TODO remove this + }); + }; + + const onClickCancel = () => { + props.onCancel(); + }; + + const onChangeProjectName = (event: React.ChangeEvent) => { + setProjectName(event.target.value); + }; + + const onChangeStack = (newStack: string) => { + setStack(newStack); + const newRelease = Object.keys(options.stacks_options[newStack])[0]; + setRelease(newRelease); + const newPlatform = options.stacks_options[newStack][newRelease][0]; + setPlatform(newPlatform); + }; + + const onChangeRelease = (event: React.ChangeEvent<{ value: unknown }>) => { + const newRelease = event.target.value as string; + setRelease(newRelease); + const newPlatform = options.stacks_options[stack][newRelease][0]; + setPlatform(newPlatform); + }; + + const onChangePlatform = (event: React.ChangeEvent<{ value: unknown }>) => { + setPlatform(event.target.value as string); + }; + + const onChangeUserScript = ( + event: React.ChangeEvent + ) => { + setUserScript(event.target.value as string); + }; + + return ( +
+
+ + +
+
+ onChangeStack('LCG')} + isSelected={stack === 'LCG'} + /> + onChangeStack('CMSSW')} + isSelected={stack === 'CMSSW'} + /> +
+
+
+
+ Release + + + +
+ + {availableReleases.map((release, index) => { + return ( + + {release} + + ); + })} + +
+
+
+ Platform + + + +
+ + {availablePlatforms.map((platform, index) => { + return ( + + {platform} + + ); + })} + +
+
+
+
+
User environment
+
+ +
+
+ +
+
+ + +
+
+ ); +}; diff --git a/SwanProjects/src/dialog.tsx b/SwanProjects/src/dialog.tsx new file mode 100644 index 00000000..e56899a1 --- /dev/null +++ b/SwanProjects/src/dialog.tsx @@ -0,0 +1,48 @@ +// Copyright (c) SWAN Development Team. +// Author: Omar.Zapata@cern.ch 2021 + +import React from 'react'; +import { ReactWidget } from '@jupyterlab/apputils'; +import { Widget } from '@lumino/widgets'; + +import Dialog from '@material-ui/core/Dialog'; +import DialogContent from '@material-ui/core/DialogContent'; + +import { ThemeProvider } from './theme-provider'; +import { ProjectWidget } from './ProjectWidget'; +import { ProjectDialog } from './ProjectDialog'; + +export async function showDialog( + options: ProjectDialog.ISWANOptions & { theme: 'light' | 'dark' } +): Promise<{ + changesSaved: boolean; + newOptions?: ProjectDialog.ISWANOptions; +}> { + return new Promise(resolve => { + const widget = ReactWidget.create( + + + + { + Widget.detach(widget); + resolve({ + changesSaved: true, + newOptions + }); + }} + onCancel={() => { + Widget.detach(widget); + resolve({ + changesSaved: false + }); + }} + /> + + + + ); + Widget.attach(widget, document.body); + }); +} diff --git a/SwanProjects/src/icons.ts b/SwanProjects/src/icons.ts new file mode 100644 index 00000000..7d8b88f9 --- /dev/null +++ b/SwanProjects/src/icons.ts @@ -0,0 +1,29 @@ +// Copyright (c) SWAN Development Team. +// Author: Omar.Zapata@cern.ch 2021 + +import { LabIcon } from '@jupyterlab/ui-components'; + +import swanProjectIconStr from '../style/project.svg'; +import swanReadmeIconStr from '../style/list-alt.svg'; +import cmsIconStr from '../style/cms.svg'; +import sftIconStr from '../style/sft.svg'; + +export const swanProjectIcon = new LabIcon({ + name: 'jupyterlab_swan:project', + svgstr: swanProjectIconStr +}); + +export const swanReadmeIcon = new LabIcon({ + name: 'jupyterlab_swan:reame', + svgstr: swanReadmeIconStr +}); + +export const cmsIcon = new LabIcon({ + name: 'jupyterlab_swan:cms', + svgstr: cmsIconStr +}); + +export const sftIcon = new LabIcon({ + name: 'jupyterlab_swan:sft', + svgstr: sftIconStr +}); diff --git a/SwanProjects/src/index.ts b/SwanProjects/src/index.ts new file mode 100644 index 00000000..1b48cbbf --- /dev/null +++ b/SwanProjects/src/index.ts @@ -0,0 +1,140 @@ +// Copyright (c) SWAN Development Team. +// Author: Omar.Zapata@cern.ch 2021 + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils'; +import { swanProjectIcon } from './icons'; + +const PALETTE_CATEGORY = 'Project'; + +import { ProjectDialog } from './ProjectDialog'; + +/** + * The command IDs used by the server extension plugin. + */ +namespace CommandIDs { + export const projectDialog = 'swan:create-project-dialog'; + export const projectDialogEdit = 'swan:edit-project-dialog'; +} + +import { ILauncher } from '@jupyterlab/launcher'; +import { kernelsInfoRequest } from './request'; + +import { IMainMenu } from '@jupyterlab/mainmenu'; + +/** + * Initialization data for the server-extension-example extension. + */ +const extension: JupyterFrontEndPlugin = { + id: 'swanprojects', + autoStart: true, + optional: [], + requires: [ICommandPalette, ILauncher, IThemeManager, IMainMenu], + activate: async ( + app: JupyterFrontEnd, + palette: ICommandPalette, + launcher: ILauncher, + themeManager: IThemeManager, + mainMenu: IMainMenu + ) => { + console.log('JupyterLab extension swanprojects is activated!'); + + let theme: 'light' | 'dark' = 'light'; + if (themeManager) { + if (themeManager.theme && themeManager.isLight(themeManager.theme)) { + theme = 'light'; + } else { + theme = 'dark'; + } + themeManager.themeChanged.connect((_, args) => { + if (themeManager.isLight(args.newValue)) { + theme = 'light'; + } else { + theme = 'dark'; + } + }); + } + + const { commands } = app; + + commands.addCommand(CommandIDs.projectDialog, { + icon: swanProjectIcon, + label: 'New Project', + caption: 'New Project', + execute: async args => { + const stacks = await kernelsInfoRequest(); + ProjectDialog.OpenModal( + { + name: '', + stack: '', + release: '', + platform: '', + user_script: '', + stacks_options: stacks['stacks'] + }, + true, + commands, + theme + ); + } + }); + + commands.addCommand(CommandIDs.projectDialogEdit, { + icon: swanProjectIcon, + label: 'Edit', + caption: 'Edit', + execute: async args => { + const stacks = await kernelsInfoRequest(); + ProjectDialog.OpenModal( + { + name: args.name as string, + stack: args.stack as string, + release: args.release as string, + platform: args.platform as string, + user_script: args.user_script as string, + stacks_options: stacks['stacks'], + corrupted: args.corrupted as boolean + }, + false, + commands, + theme + ); + } + }); + + // Add the command to the launcher + if (launcher) { + launcher.add({ + command: CommandIDs.projectDialog, + category: PALETTE_CATEGORY, + rank: 1, + kernelIconUrl: '' + }); + } + + // // Add the command to the palette + if (palette) { + palette.addItem({ + command: CommandIDs.projectDialog, + args: { isPalette: true }, + category: PALETTE_CATEGORY + }); + } + const command = CommandIDs.projectDialog; + app.contextMenu.addItem({ + command: command, + rank: 0, + selector: '.jp-DirListing-content' + }); + + if (mainMenu) { + mainMenu.fileMenu.newMenu.addGroup([{ command: command }], 0); + } + } +}; + +export default extension; diff --git a/SwanProjects/src/request.ts b/SwanProjects/src/request.ts new file mode 100644 index 00000000..468cb771 --- /dev/null +++ b/SwanProjects/src/request.ts @@ -0,0 +1,130 @@ +// Copyright (c) SWAN Development Team. +// Author: Omar.Zapata@cern.ch 2021 + +import { URLExt } from '@jupyterlab/coreutils'; + +import { ServerConnection } from '@jupyterlab/services'; +import { ProjectDialog } from './ProjectDialog'; + +/** + * Call the API extension, base function to implement the other requests + * + * @param endPoint API REST end point for the extension + * @param init Initial values for the request + * @returns The response body interpreted as JSON + */ +export async function request( + endPoint = '', + init: RequestInit = {} +): Promise { + // Make request to Jupyter API + const settings = ServerConnection.makeSettings(); + const requestUrl = URLExt.join(settings.baseUrl, '', endPoint); + + let response: Response; + try { + response = await ServerConnection.makeRequest(requestUrl, init, settings); + } catch (error) { + throw new ServerConnection.NetworkError(error); + } + + const data = await response.json(); + + if (!response.ok) { + throw new ServerConnection.ResponseError(response, data.message); + } + + return data; +} + +/** + * Request to get contents from a path + * + * @param cwd path get information from jupyter api + * @returns json object with the information of the path or json object with the information of the error. + */ +export function contentRequest(cwd: string): any { + try { + return request('api/contents/' + cwd, { + method: 'GET' + }); + } catch (reason) { + const msg = `Error on GET 'api/contents'+ ${cwd}.\n${reason}`; + return { status: 'error', reason: reason, param: cwd, msg: msg }; + } +} + +/** + * Request to create a project + * + * @param options parameters to send to the backend, such as name, stack, release etc.. + * @returns json object with the keys 'project_dir' and 'msg' or json object with the information of the error. + */ +export function createProjectRequest(options: ProjectDialog.ISWANOptions): any { + const dataToSend = { + name: options.name, + stack: options.stack, + release: options.release, + platform: options.platform, + user_script: options.user_script + }; + try { + return request('swan/project/create', { + body: JSON.stringify(dataToSend), + method: 'POST' + }); + } catch (reason) { + const msg = `Error on POST /swan/project/create ${options}.\n${reason}`; + return { status: 'error', reason: reason, param: options, msg: msg }; + } +} + +/** + * Request to edit project + * + * @param old_name previous name of the project + * @param options new project parameters to send to the backend, such as name, stack, release etc.. + * @returns json object with the keys 'project_dir' and 'msg' or json object with the information of the error. + */ +export function editProjectRequest( + old_options: ProjectDialog.ISWANOptions, + options: ProjectDialog.ISWANOptions +): any { + const dataToSend = { + old_name: old_options.name, + old_stack: old_options.stack, + old_release: old_options.release, + old_platform: old_options.platform, + old_userscript: old_options.user_script, + name: options.name, + stack: options.stack, + release: options.release, + platform: options.platform, + user_script: options.user_script, + corrupted: options.corrupted + }; + try { + return request('swan/project/edit', { + body: JSON.stringify(dataToSend), + method: 'POST' + }); + } catch (reason) { + const msg = `Error on POST swan/project/edit ${options}.\n${reason}`; + return { status: 'error', reason: reason, param: options, msg: msg }; + } +} + +/** + * Request to get the information for the software stacks + * + * @returns json with the software stack names, releases, platform, etc.. + */ +export function kernelsInfoRequest(): any { + try { + return request('swan/stacks/info', { + method: 'GET' + }); + } catch (reason) { + console.error(`Error on GET 'swan/stacks/info'.\n${reason}`); + } +} diff --git a/SwanProjects/src/svg.d.ts b/SwanProjects/src/svg.d.ts new file mode 100644 index 00000000..81101e35 --- /dev/null +++ b/SwanProjects/src/svg.d.ts @@ -0,0 +1,6 @@ +// svg.d.ts + +declare module '*.svg' { + const value: string; + export default value; +} diff --git a/SwanProjects/src/theme-provider.tsx b/SwanProjects/src/theme-provider.tsx new file mode 100644 index 00000000..10f1468f --- /dev/null +++ b/SwanProjects/src/theme-provider.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { + createTheme, + ThemeProvider as MaterialUIThemeProvider +} from '@material-ui/core/styles'; + +const createLightorDarkTheme = (color: 'light' | 'dark') => { + return createTheme({ + shadows: Array(25).fill('none') as any, + shape: { + borderRadius: 2 + }, + typography: { + fontSize: 12, + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"' + ].join(',') + }, + palette: { + type: color || 'light', + primary: { + main: '#0153a1' + } + } + }); +}; + +const lightTheme = createLightorDarkTheme('light'); +const darkTheme = createLightorDarkTheme('dark'); + +export const ThemeProvider: React.FunctionComponent<{ + theme: 'light' | 'dark'; +}> = props => { + const currentTheme = props.theme === 'light' ? lightTheme : darkTheme; + return ( + + {props.children} + + ); +}; diff --git a/SwanProjects/style/base.css b/SwanProjects/style/base.css new file mode 100644 index 00000000..f49a25ae --- /dev/null +++ b/SwanProjects/style/base.css @@ -0,0 +1,108 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) 2021, SWAN Team. +| +| Distributed under the terms of the AGPL License. +|----------------------------------------------------------------------------*/ + +.sw-Dialog-content { + display: flex; + flex-direction: column; + margin-left: auto; + margin-right: auto; + padding: 24px; + padding-bottom: 20px; + word-wrap: break-word; + resize: both; +} +.sw-Dialog-content > * + * { + margin-top: 20px; +} + +.sw-Dialog-project-icon * { + display: flex; + width: 39px; +} + +.sw-Dialog-project-name { + display: flex; + align-items: center; +} + +.sw-Dialog-project-name-input { + flex-grow: 1; + margin-left: 16px !important; +} + +.sw-Dialog-userscript { + display: flex; + flex-direction: column; + flex: 1 1 auto; +} +.sw-Dialog-userscript textarea { + font-family: monospace; +} + +.sw-Dialog-userscript-title { + display: flex; + padding-top: 5px; + padding-bottom: 5px; +} +.sw-Dialog-userscript-tooltip, +.sw-Dialog-release-tooltip { + padding-left: 5px; +} + +.sw-Dialog-body > .jp-select-wrapper { + width: 100%; +} + +/* required to disable "new" notebook and console from the main menu */ +/* with the concept of project, notebook/console can be only created in the launcher, when kernel manager */ +/* have kernels available, otherwise the user will have a kernel error, becuase of kernels are not available*/ +li[data-command='notebook:create-new'] { + display: none; +} + +li[data-command='console:create'] { + display: none; +} +li[data-command='terminal:create-new'] { + display: none; +} + +.sw-Dialog-select-stack { + display: flex; +} +.sw-Dialog-select-stack .jp-LauncherCard:first-child { + margin-left: 0; +} + +.sw-Dialog-stack-options { + display: flex; + align-items: flex-end; +} +.sw-Dialog-stack-options > * { + flex: 1 1 0; +} +.sw-Dialog-stack-options > * + * { + margin-left: 10px; +} +.sw-Dialog-stack-option-label { + display: flex; + margin-bottom: 4px; +} + +.sw-Dialog-button-area { + display: flex; + padding: 8px 0px; + justify-content: flex-end; +} +.sw-Dialog-button-area > * + * { + margin-left: 8px !important; +} + +.sw.jp-LauncherCard-icon > div { + height: 40px; + width: 40px; + margin: 4px 0 8px 0; +} diff --git a/SwanProjects/style/cms.svg b/SwanProjects/style/cms.svg new file mode 100644 index 00000000..1e40f126 --- /dev/null +++ b/SwanProjects/style/cms.svg @@ -0,0 +1,239 @@ + + + + diff --git a/SwanProjects/style/index.css b/SwanProjects/style/index.css new file mode 100644 index 00000000..8a7ea29e --- /dev/null +++ b/SwanProjects/style/index.css @@ -0,0 +1 @@ +@import url('base.css'); diff --git a/SwanProjects/style/index.js b/SwanProjects/style/index.js new file mode 100644 index 00000000..a028a764 --- /dev/null +++ b/SwanProjects/style/index.js @@ -0,0 +1 @@ +import './base.css'; diff --git a/SwanProjects/style/kernels/python/logo-32x32.png b/SwanProjects/style/kernels/python/logo-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..be81330765764699553aa4fbaf0e9fc27c20c6d2 GIT binary patch literal 1084 zcmV-C1jGA@P)enw2jbMszQuf3kC$K7$S;4l;TgSRfzha5>pgWAEY9PR!IdB zTSZXtp`b02h)|SJ3#AW|AKF?KgNSQ|Sg=ZCgHaT%F`4#g>iG8;N__GBLh26(2qOGO9};SPeUDLyV^m!K($s69;fB|`Ui z{nqhFk+};I5Vb+1*IC+gaNEtF()dX{`(!1eUb?=>+~p#JOj-qUi2^^^uzi1p(thMz&#&LJq>Cf)~tBhxq*;Npy$=mheX>2t4(OR zWk&s74VR$m@6rlD?Nud*cEGO2$>|mV&tzP1%j+W-N_;a>$_%)&Yn?|hX(50fV5s); zkLsKLb20?nJo-eIQ&vLU?~T?v{=JUtFa!EFC;;*i2@lY(#8Ur2b{` z!nc_6C42;g?mDnyRp9)U84ZxUv=Ja10XDYX;KZ|EPJ`h_&;S{#m9Q!a*xC#MiI?P; zx4sNs;+Uif!Da~pAQU}S)ww^M;qb(^FD`~`s1D2+foklsECF&ZZKas%kF~bU-M9bY zuhs+V2CzISGy`A&Lkq;MkgWkjD)R)1WqC_*Tx45LdH=lV+}XPaAFS+wus(ZG#IuZp zEE@YdBSMkKnX~3J?j7u_^kl&mQ+7t_i^t4YG6X0cS+J89bl~_Igc~wh(?=P_08}Iv z0NHqkz|x<~Z;3paR=+czhC^#TYlWDdd@Rc|#cCUooxt4edl>=;-neznjL)SlXtdOh z=2NAO%Gxj%BLM->i|(q=eePLs=%wD>*F6312}yTRxn%!IzZtmkN`YjQBMNkckc4h;pSXO%%?N2y_ccz zS`INlItXC6DR;umS}Mn43NzsR7MS0Sf|rrv1n7UvdO9UC3&XB+{A~zNMyyXY@lF_q zps;z-9S*u(m1{=;T?YYxd%vmwj5N7<3lv^}?EK6DlWbFPZoBI|w5zEE06;(VF2nD? z_QUyZi0eRG2jDb-NyvSR5{_bd`5o6W`WOCh1>4`s79R;zVm_k)0000kjcw83I)rwURf9H)0d)l3>^8*`$3&wplXaSnv^ouL zxig617>J8x{$<2zvZ44vm&sPJz*Z;|)^sj29S|e(QD`@&rR&E%&(A;Zx#ym9?>Xnb z=k|6x#=dRS_rB-ex99mi&+qvXHKxY@^N`8h{N|r@TsA(& zsCpk!BK%oN(i-QUbD69cd?H!sn{mG-Lrs4l70Gd-TRSnnlw<)m#)CQ1364@U( zb1huc+%2C?f zYjwl_PTT;XJ$4oVU=Be51c+U`UEX_ls%aSHu0jnXMCH=*+Sd}C2irp2UqB=Z0E)N85&+GM z>q^`|nwHj#MQ}!_hFxHI0P?d05b<<^{$@L)xRXP$*7NMe_Al`SAe_UPXbALJOH3_5 zcM?1d0-}ThP+N;&R(k{$P!RUyBLuGx7u*NjI0EqWx*LBO^)ny+&f^)CC}~0x8ViOeXmOp`hB@Wk%DqXy3C1Q0?$fKnaUFPm1OP-ZjVK`deF} zSeAF2mylo&RQ`&~-?2v|r4t6AY0JJPRN1JijUXW&kBk6^2Cvr^I{u5UuqP$>16T2K z9R$k@xromL3Y>lI8J_*t?K0<)3neE)OPIZA`y$|W32O|S;>(;-_BoaG7O_=2G z6D)9yzzx@Wf#9y!>3jH(JLX0Lz*6}#sWZF@h^aPF)_fq;^c^8JPiTh*0JRcGe<2b8 zN_@jF0rBt^lR=9@fPBV9TT3%D0)}bdo{O3TaO38^?3k0H{bUT-qpE!%+$xpS2LPf1an-UJ2DJ9KqouI6R;TMiW;X0gzCw zHO|Y+R^XVXy4>IM=$idVj4jUz?GhXz)&RZ6C=nuAOFRF5GYcGpaQ8++^bVf8D~Ysh zasY5*fBszU=;2(eHKTx{cJgCCqK3OyNG?6L{qEzi@F-xtJB056lt^D=Mgd{1M;|3o zptQ9-Tf6}9DG0x>)iWA;*7d!}f34XL)z1YaJw+(tZvmBs7Qne4&B4c^71J}j0Cl!mHAtQyc|{3a zzhEhE=-#}lmuK6SVomEdD6U096Gc<`?9IYNt09igBXq$&uNwIPk|#@Za%kz^ysDSy z+SWt37r+OM+U|uhJI|3tadcq`kq(&o0OEv1c4+!|*N<=iE&E$ngIs6G>;UsEYRUoH z*N{CGAkP{BAQ=ioDsa;2iU)Z9+n0m7&G0!|IACWkdlBI1w@S4<6a_#XeAP z1@TTJt)oc(Zd&9NrG)FXraO%+ph_!V8AqA`#S;PpD4=AwE!!e+(HZRH`J4Q`%$PKn zL#RLx{&wZdvT~>OrXG{ynQ!)hTxeLDW{is=avgT_Q@X{_ryQSRf-z;cCzzZ%57>p+XNOwhgQWFSDdeo<;8g((CJEj(Z4)c6IEc3%k9{YIG zk+*m8hahOo-7ycwG7kU%o^1X(sCP!|<+23tKd4KhH8=|#dkr8hdCPys`Kq?qW`a42rV{8owiaTo2X%UpUcJedmjJmB_0Mh> zDfdCyN&K%dp1k=ojE<}Z_*K9@aFMV5@X-t5FOkM$vasuX>}!EgFkb%DENHq8U>%?f zGQUv=A_?Fk1g}BS5Ab;i4xv&G$^7TeU}{W_sWCMsdHfgT%>1XE)oy \ No newline at end of file diff --git a/SwanProjects/style/project.svg b/SwanProjects/style/project.svg new file mode 100644 index 00000000..23e34465 --- /dev/null +++ b/SwanProjects/style/project.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/SwanProjects/style/sft.svg b/SwanProjects/style/sft.svg new file mode 100644 index 00000000..c2721aa2 --- /dev/null +++ b/SwanProjects/style/sft.svg @@ -0,0 +1,1190 @@ + + + + diff --git a/SwanProjects/swanprojects/__init__.py b/SwanProjects/swanprojects/__init__.py new file mode 100644 index 00000000..9c45b2ac --- /dev/null +++ b/SwanProjects/swanprojects/__init__.py @@ -0,0 +1,44 @@ + +from .handlers import setup_handlers +import json +from pathlib import Path + +from ._version import __version__ + +HERE = Path(__file__).parent.resolve() + +with (HERE / "labextension" / "package.json").open() as fid: + data = json.load(fid) + + +def _jupyter_labextension_paths(): + return [{ + "src": "labextension", + "dest": data["name"] + }] + + +def _jupyter_server_extension_points(): + return [{ + "module": "swanprojects" + }] + + +def _load_jupyter_server_extension(server_app): + """Registers the API handler to receive HTTP requests from the frontend extension. + + Parameters + ---------- + server_app: jupyterlab.labapp.LabApp + JupyterLab application instance + """ + print(f"JupyterLab swanprojects {__version__} is activated!") + url_path = "swan" + setup_handlers(server_app.web_app, url_path) + server_app.log.info( + f"Registered swanprojects extension at URL path /{url_path}" + ) + + +# For backward compatibility with notebook server - useful for Binder/JupyterHub +load_jupyter_server_extension = _load_jupyter_server_extension diff --git a/SwanProjects/swanprojects/_version.py b/SwanProjects/swanprojects/_version.py new file mode 100644 index 00000000..d0bfd88d --- /dev/null +++ b/SwanProjects/swanprojects/_version.py @@ -0,0 +1,20 @@ +import json +from pathlib import Path + +__all__ = ["__version__"] + + +def _fetchVersion(): + HERE = Path(__file__).parent.resolve() + + for settings in HERE.rglob("package.json"): + try: + with settings.open() as f: + return json.load(f)["version"] + except FileNotFoundError: + pass + + raise FileNotFoundError(f"Could not find package.json under dir {HERE!s}") + + +__version__ = _fetchVersion() diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py new file mode 100644 index 00000000..0b79f384 --- /dev/null +++ b/SwanProjects/swanprojects/handlers.py @@ -0,0 +1,289 @@ +# Copyright (c) SWAN Development Team. +# Author: Omar.Zapata@cern.ch 2021 +import json +import os +import shutil +import subprocess + +import tornado +from notebook.base.handlers import APIHandler +from notebook.utils import url_path_join +from tornado.web import StaticFileHandler +from traitlets import Unicode +from traitlets.config import Configurable + +from .utils import (get_project_info, get_project_path, get_project_readme, + get_user_script_content, get_env_isolated) + +class SwanProjects(Configurable): + stacks_path = Unicode( + os.path.dirname(os.path.abspath(__file__)) + '/stacks.json', + config=True, + help="The path to the JSON containing stack configuration") + +class ProjectInfoHandler(APIHandler): + @tornado.web.authenticated + def post(self): + """ + Post request for the SwanLauncher/SwanFileBrowser, + this endpoint returns project information such as stack, release, platform etc.. + if the path is not inside the project return and empty project data. + """ + input_data = self.get_json_body() + self.log.info(f"ProjectInfoHandler = {input_data}") + path = input_data["path"] + + project = get_project_path(path) + project_data = {} + if project is not None: + project_data = get_project_info(project) + + project_data["name"] = project.split(os.path.sep)[-1] + readme = get_project_readme(project) + if readme is not None: + project_data["readme"] = readme + project_data["user_script"] = get_user_script_content(project) + payload = {"project_data": project_data} + self.finish(json.dumps(payload)) + +class StacksInfoHandler(APIHandler): + + swan_projects_config = None + + def initialize(self): + self.swan_projects_config = SwanProjects(config=self.config) + + @tornado.web.authenticated + def get(self): + """ + This endpoint is required for the project dialog, it's returning the information saved on stacks.json + """ + with open(self.swan_projects_config.stacks_path) as f: + stacks = json.loads(f.read()) + self.finish(json.dumps({"stacks": stacks})) + +class KernelSpecManagerPathHandler(APIHandler): + @tornado.web.authenticated + def post(self): + """ + This endpoint is required for the project kernel spec manager, it's it is setting the path to + check if we are inside a project to change the kernel spec manager path + """ + input_data = self.get_json_body() + path = input_data["path"] + + self.log.info(f"KernelSpecManagerPathHandler = {input_data}") + + project = get_project_path(path) + if self.kernel_spec_manager.set_path(path): + data = {"status": True, "is_project": project is not None, + "msg": f"SWAN kernel spec manager set to path: {path}"} + self.finish(json.dumps(data)) + else: + data = {"status": False, "is_project": project is not None, + "msg": f"Error setting SWAN kernel spec manager to path: {path}"} + self.finish(json.dumps(data)) + +class CreateProjectHandler(APIHandler): + @tornado.web.authenticated + def post(self): + """ + Endpoint to create a project, receive project information such as name, stack, platform, release, user_script. + The project is created at $HOME/SWAN_projects/project_name and a hidden json ".swanproject" file with the information + project is set inside the project folder. + """ + input_data = self.get_json_body() + self.log.info(f"creating project {input_data}") + + name = input_data["name"] + stack = input_data["stack"] # CMSSW/LCG + platform = input_data["platform"] # SCRAM/x86_64..centos7..gccX + release = input_data["release"] # CMSSW_X_Y_Z/LCG_XYZ + user_script = input_data["user_script"] + + project_dir = os.environ["HOME"] + "/SWAN_projects/" + name + try: + os.makedirs(project_dir) + except Exception as msg: + data = {"status": False, "project_dir": f"SWAN_projects/{name}", + "msg": f"Error creating folder for project {name}, traceback: {msg}"} + self.finish(json.dumps(data)) + return + swan_project_file = project_dir + os.path.sep + '.swanproject' + swan_project_content = {'stack': stack, 'release': release, + 'platform': platform} + try: + with open(swan_project_file, 'w+') as f: + f.write(json.dumps(swan_project_content, + indent=4, sort_keys=True)) + f.close() + except Exception as msg: + data = {"status": False, "project_dir": f"SWAN_projects/{name}", + "msg": f"Error creating .swanproject file for project {name}, traceback: {msg}"} + self.finish(json.dumps(data)) + return + + try: + swan_user_script_file = project_dir + os.path.sep + '.userscript' + with open(swan_user_script_file, 'w') as f: + f.write(user_script) + f.close() + except Exception as msg: + data = {"status": False, "project_dir": f"SWAN_projects/{name}", + "msg": f"Error creating .userscript file for project {name}, traceback: {msg}"} + self.finish(json.dumps(data)) + return + + command = get_env_isolated() + command += ["/bin/bash", "-c", "swan_kmspecs --project_name %s" % name] + self.log.info(f"running {command} ") + proc = subprocess.Popen(command, stdout=subprocess.PIPE) + proc.wait() + output = proc.stdout.read().decode("utf-8") + self.log.info(f"swan_kmspecs output: {output}") + proc.communicate() + self.log.info(f"swan_kmspecs return code: {proc.returncode}") + if proc.returncode != 0: + data = {"status": False, "project_dir": f"SWAN_projects/{name}", + "msg": f"Error collecting the information from cvmfs for project {name}, traceback: {output}"} + self.finish(json.dumps(data)) + return + + data = {"status": True, "project_dir": f"SWAN_projects/{name}", + "msg": f"created project {name}"} + self.finish(json.dumps(data)) + +class EditProjectHandler(APIHandler): + + @tornado.web.authenticated + def post(self): + """ + This endpoint allows to edit project information, such as name, stack, platform etc.. + The project can be renamed from $HOME/SWAN_projects/old_name to $HOME/SWAN_projects/name + and metadata in the .swanproject is updated. + + This endpoint allows also edit corrupted projects, if something is wrong in .swanproject + the edit project dialog will send the information again to this endpoint to fix the project information. + """ + input_data = self.get_json_body() + print(f"EditProjectHandler = {input_data}") + + corrupted = False + if "corrupted" in input_data.keys(): + corrupted = input_data["corrupted"] + + old_name = "" + if "old_name" in input_data.keys(): + old_name = input_data["old_name"] + + old_stack = "" + if "old_stack" in input_data.keys(): + old_stack = input_data["old_stack"] + + old_platform = "" + if "old_platform" in input_data.keys(): + old_platform = input_data["old_platform"] + + old_release = "" + if "old_release" in input_data.keys(): + old_release = input_data["old_release"] + + old_userscript = "" + if "old_userscript" in input_data.keys(): + old_userscript = input_data["old_userscript"] + + name = input_data["name"] + stack = input_data["stack"] + platform = input_data["platform"] + release = input_data["release"] + user_script = input_data["user_script"] + + project_dir = os.environ["HOME"] + "/SWAN_projects/" + name + if old_name != name: + try: + old_project_dir = os.environ["HOME"] + \ + "/SWAN_projects/" + old_name + os.rename(old_project_dir, project_dir) + except Exception as msg: + data = {"status": False, "project_dir": f"SWAN_projects/{old_name}", + "msg": f"Error editing project folder {old_name}, traceback: {msg}"} + # this will stop the execution here, it's the same for the next exceptions. + self.finish(json.dumps(data)) + return + + if old_userscript != user_script: + userscript_file = project_dir + os.path.sep + '.userscript' + try: + with open(userscript_file, 'w') as f: + f.write(user_script) + f.close() + except Exception as msg: + data = {"status": False, "project_dir": f"SWAN_projects/{name}", + "msg": f"Error editing .userscript for project {name}, traceback: {msg}"} + self.finish(json.dumps(data)) + return + + if stack != old_stack or platform != old_platform or release != old_release or corrupted: + swan_project_file = project_dir + os.path.sep + '.swanproject' + swan_project_content = {'stack': stack, 'release': release, + 'platform': platform} + kernel_dir = project_dir + "/.local/share/jupyter/kernels" + + # removing old native kernels for python only(this is generated by us) + if os.path.exists(kernel_dir + "/python2"): + shutil.rmtree(kernel_dir + "/python2") + + if os.path.exists(kernel_dir + "/python3"): + shutil.rmtree(kernel_dir + "/python3") + + with open(swan_project_file, 'w+') as f: + f.write(json.dumps(swan_project_content, + indent=4, sort_keys=True)) + f.close() + command = get_env_isolated() + command += ["swan_kmspecs", "--project_name", name] + self.log.info(f"running {command} ") + proc = subprocess.Popen(command, stdout=subprocess.PIPE) + proc.wait() + output = proc.stdout.read().decode("utf-8") + self.log.info(f"result {output} ") + proc.communicate() + self.log.info(f"swan_kmspecs return code: {proc.returncode}") + if proc.returncode != 0: + data = {"status": False, "project_dir": f"SWAN_projects/{name}", + "msg": f"Error editing stack, platform or relase for project {name}, traceback: {output}"} + self.finish(json.dumps(data)) + return + data = {"status": True, "project_dir": f"SWAN_projects/{name}", + "msg": f"edited project {name}"} + self.finish(json.dumps(data)) + +# URL to handler mappings +def setup_handlers(web_app, url_path): + host_pattern = ".*$" + base_url = web_app.settings["base_url"] + + # Prepend the base_url so that it works in a jupyterhub setting + create_pattern = url_path_join(base_url, url_path, "project/create") + edit_pattern = url_path_join(base_url, url_path, "project/edit") + project_pattern = url_path_join(base_url, url_path, "project/info") + stack_pattern = url_path_join(base_url, url_path, "stacks/info") + ksm_path_pattern = url_path_join(base_url, url_path, "kernelspec/set") + + handlers = [(create_pattern, CreateProjectHandler)] + handlers.append((edit_pattern, EditProjectHandler)) + handlers.append((project_pattern, ProjectInfoHandler)) + handlers.append((stack_pattern, StacksInfoHandler)) + handlers.append((ksm_path_pattern, KernelSpecManagerPathHandler)) + + web_app.add_handlers(host_pattern, handlers) + + # Prepend the base_url so that it works in a jupyterhub setting + doc_url = url_path_join(base_url, url_path, "static") + doc_dir = os.getenv( + "SWAN_JLAB_SERVER_STATIC_DIR", + os.path.join(os.path.dirname(__file__), "static"), + ) + handlers = [("{}/(.*)".format(doc_url), + StaticFileHandler, {"path": doc_dir})] + web_app.add_handlers(".*$", handlers) diff --git a/SwanProjects/swanprojects/kernelmanager/__init__.py b/SwanProjects/swanprojects/kernelmanager/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py new file mode 100644 index 00000000..12b4390a --- /dev/null +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -0,0 +1,131 @@ +# Copyright (c) SWAN Development Team. +# Author: Omar.Zapata@cern.ch 2021 + +import json +import os +import shutil + +from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel +from swanprojects.utils import (get_project_info, get_project_name, + get_project_path, get_env_isolated) +from traitlets import Unicode +from traitlets.config import Configurable + + +class SwanKSMConfig(Configurable): + kernel_resources = Unicode( + os.path.dirname(os.path.abspath(__file__)) + '/resources', + config=True, + help="The path to the folder containg the resources to add to the kernel" + ) + + +class SwanKernelSpecManager(KernelSpecManager): + path = Unicode("", config=True, allow_none=True, + help="SWAN Project path") + + def __init__(self, **kwargs): + super(SwanKernelSpecManager, self).__init__(**kwargs) + self.log.info("JupyterLab swankernelspecmanager is activated!") + self.project = None + self.kernel_dirs = [] + self.ksmconfig = SwanKSMConfig(config=self.config) + + def save_native_spec(self, kernel_dir, python_path, display_name): + """ + This function creates a default kernel with the info from the stack. + It's necessary for CMSSW stacks and those that doesn't have a Python kernel in json file. + """ + self.log.info( + f"copying resources from {self.ksmconfig.kernel_resources} to {kernel_dir}") + shutil.copytree(self.ksmconfig.kernel_resources, kernel_dir) + spec = {"argv": [python_path, + "-m", + "ipykernel_launcher", + "-f", + "{connection_file}" + ], + "display_name": display_name, + "language": "python" + } + kernel_file = kernel_dir + "/kernel.json" + f = open(kernel_file, "w+") + json.dump(spec, f, indent=4) + f.close() + + def set_path(self, path): + self.path = path + self.project = get_project_path(path) + if self.project is None: + self.kernel_dirs = [] + return True + else: + self.project_info = get_project_info(self.project) + self.project_name = get_project_name(self.project) + if "kernel_dirs" in self.project_info: + self.kernel_dirs = self.project_info["kernel_dirs"] + local_kernels = self.project + "/.local/share/jupyter/kernels/" + for version in ["2", "3"]: + python = "python" + version + if self.project_info[python]["found"] and self.project_info[python]["ipykernel"]: + kerne_dir = local_kernels + python + if not os.path.exists(kerne_dir): + self.save_native_spec( + kerne_dir, self.project_info[python]["path"], "Python " + version) + self.kernel_dirs.append(local_kernels) + self.log.debug(f"KERNEL DIRS = {self.kernel_dirs}") + self.log.debug(f"specs:\n {self.get_all_specs()}") + return True + else: + self.log.debug( + f"Error setting kernel paths, project {self.project_name} corrupted.") + self.kernel_dirs = [] + return False + + def wrap_kernel_specs(self, project_name, kspec): + + argv = get_env_isolated() + argv += ["/bin/bash", "-c", "swan_env {} {} ".format( + project_name, ".") + "'" + " ".join(kspec.argv) + "'" + ] + + kspec.argv = argv + return kspec + + def find_kernel_specs(self, skip_base=True): + """ Returns a dict mapping kernel names to resource directories. + The update process also adds the resource dir for the SWAN + environments. + """ + kspecs = super(SwanKernelSpecManager, self).find_kernel_specs() + return kspecs + + def get_kernel_spec(self, kernel_name): + """ Returns a :class:`KernelSpec` instance for the given kernel_name. + Also, SWAN kernelspecs are generated on the fly + according to the detected environments. + """ + kspec = super(SwanKernelSpecManager, self).get_kernel_spec(kernel_name) + if self.project is None: + return kspec + else: + kspec = self.wrap_kernel_specs(self.project_name, kspec) + self.log.debug(f"ON get_kernel_spec = {kspec.argv}") + + return kspec + + def get_all_specs(self): + """ Returns a dict mapping kernel names to dictionaries with two + entries: "resource_dir" and "spec". This was added to fill out + the full public interface to KernelManagerSpec. + """ + res = {} + for name, resource_dir in self.find_kernel_specs().items(): + try: + spec = self.get_kernel_spec(name) + res[name] = {'resource_dir': resource_dir, + 'spec': spec.to_dict()} + except NoSuchKernel: + self.log.warning( + "Error loading kernelspec %r", name, exc_info=True) + return res diff --git a/SwanProjects/swanprojects/kernelmanager/resources/logo-32x32.png b/SwanProjects/swanprojects/kernelmanager/resources/logo-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..be81330765764699553aa4fbaf0e9fc27c20c6d2 GIT binary patch literal 1084 zcmV-C1jGA@P)enw2jbMszQuf3kC$K7$S;4l;TgSRfzha5>pgWAEY9PR!IdB zTSZXtp`b02h)|SJ3#AW|AKF?KgNSQ|Sg=ZCgHaT%F`4#g>iG8;N__GBLh26(2qOGO9};SPeUDLyV^m!K($s69;fB|`Ui z{nqhFk+};I5Vb+1*IC+gaNEtF()dX{`(!1eUb?=>+~p#JOj-qUi2^^^uzi1p(thMz&#&LJq>Cf)~tBhxq*;Npy$=mheX>2t4(OR zWk&s74VR$m@6rlD?Nud*cEGO2$>|mV&tzP1%j+W-N_;a>$_%)&Yn?|hX(50fV5s); zkLsKLb20?nJo-eIQ&vLU?~T?v{=JUtFa!EFC;;*i2@lY(#8Ur2b{` z!nc_6C42;g?mDnyRp9)U84ZxUv=Ja10XDYX;KZ|EPJ`h_&;S{#m9Q!a*xC#MiI?P; zx4sNs;+Uif!Da~pAQU}S)ww^M;qb(^FD`~`s1D2+foklsECF&ZZKas%kF~bU-M9bY zuhs+V2CzISGy`A&Lkq;MkgWkjD)R)1WqC_*Tx45LdH=lV+}XPaAFS+wus(ZG#IuZp zEE@YdBSMkKnX~3J?j7u_^kl&mQ+7t_i^t4YG6X0cS+J89bl~_Igc~wh(?=P_08}Iv z0NHqkz|x<~Z;3paR=+czhC^#TYlWDdd@Rc|#cCUooxt4edl>=;-neznjL)SlXtdOh z=2NAO%Gxj%BLM->i|(q=eePLs=%wD>*F6312}yTRxn%!IzZtmkN`YjQBMNkckc4h;pSXO%%?N2y_ccz zS`INlItXC6DR;umS}Mn43NzsR7MS0Sf|rrv1n7UvdO9UC3&XB+{A~zNMyyXY@lF_q zps;z-9S*u(m1{=;T?YYxd%vmwj5N7<3lv^}?EK6DlWbFPZoBI|w5zEE06;(VF2nD? z_QUyZi0eRG2jDb-NyvSR5{_bd`5o6W`WOCh1>4`s79R;zVm_k)0000kjcw83I)rwURf9H)0d)l3>^8*`$3&wplXaSnv^ouL zxig617>J8x{$<2zvZ44vm&sPJz*Z;|)^sj29S|e(QD`@&rR&E%&(A;Zx#ym9?>Xnb z=k|6x#=dRS_rB-ex99mi&+qvXHKxY@^N`8h{N|r@TsA(& zsCpk!BK%oN(i-QUbD69cd?H!sn{mG-Lrs4l70Gd-TRSnnlw<)m#)CQ1364@U( zb1huc+%2C?f zYjwl_PTT;XJ$4oVU=Be51c+U`UEX_ls%aSHu0jnXMCH=*+Sd}C2irp2UqB=Z0E)N85&+GM z>q^`|nwHj#MQ}!_hFxHI0P?d05b<<^{$@L)xRXP$*7NMe_Al`SAe_UPXbALJOH3_5 zcM?1d0-}ThP+N;&R(k{$P!RUyBLuGx7u*NjI0EqWx*LBO^)ny+&f^)CC}~0x8ViOeXmOp`hB@Wk%DqXy3C1Q0?$fKnaUFPm1OP-ZjVK`deF} zSeAF2mylo&RQ`&~-?2v|r4t6AY0JJPRN1JijUXW&kBk6^2Cvr^I{u5UuqP$>16T2K z9R$k@xromL3Y>lI8J_*t?K0<)3neE)OPIZA`y$|W32O|S;>(;-_BoaG7O_=2G z6D)9yzzx@Wf#9y!>3jH(JLX0Lz*6}#sWZF@h^aPF)_fq;^c^8JPiTh*0JRcGe<2b8 zN_@jF0rBt^lR=9@fPBV9TT3%D0)}bdo{O3TaO38^?3k0H{bUT-qpE!%+$xpS2LPf1an-UJ2DJ9KqouI6R;TMiW;X0gzCw zHO|Y+R^XVXy4>IM=$idVj4jUz?GhXz)&RZ6C=nuAOFRF5GYcGpaQ8++^bVf8D~Ysh zasY5*fBszU=;2(eHKTx{cJgCCqK3OyNG?6L{qEzi@F-xtJB056lt^D=Mgd{1M;|3o zptQ9-Tf6}9DG0x>)iWA;*7d!}f34XL)z1YaJw+(tZvmBs7Qne4&B4c^71J}j0Cl!mHAtQyc|{3a zzhEhE=-#}lmuK6SVomEdD6U096Gc<`?9IYNt09igBXq$&uNwIPk|#@Za%kz^ysDSy z+SWt37r+OM+U|uhJI|3tadcq`kq(&o0OEv1c4+!|*N<=iE&E$ngIs6G>;UsEYRUoH z*N{CGAkP{BAQ=ioDsa;2iU)Z9+n0m7&G0!|IACWkdlBI1w@S4<6a_#XeAP z1@TTJt)oc(Zd&9NrG)FXraO%+ph_!V8AqA`#S;PpD4=AwE!!e+(HZRH`J4Q`%$PKn zL#RLx{&wZdvT~>OrXG{ynQ!)hTxeLDW{is=avgT_Q@X{_ryQSRf-z;cCzzZ%57>p+XNOwhgQWFSDdeo<;8g((CJEj(Z4)c6IEc3%k9{YIG zk+*m8hahOo-7ycwG7kU%o^1X(sCP!|<+23tKd4KhH8=|#dkr8hdCPys`Kq?qW`a42rV{8owiaTo2X%UpUcJedmjJmB_0Mh> zDfdCyN&K%dp1k=ojE<}Z_*K9@aFMV5@X-t5FOkM$vasuX>}!EgFkb%DENHq8U>%?f zGQUv=A_?Fk1g}BS5Ab;i4xv&G$^7TeU}{W_sWCMsdHfgT%>1XE)oy + + + + SWAN + + + This content is served from the jupyter_swan server extension. + + diff --git a/SwanProjects/swanprojects/utils.py b/SwanProjects/swanprojects/utils.py new file mode 100644 index 00000000..8590ca81 --- /dev/null +++ b/SwanProjects/swanprojects/utils.py @@ -0,0 +1,107 @@ +import json +import os + + +def has_project_file(path): + """ + Method to check if .swanproject exists + path: path to check + """ + return os.path.exists(path + os.path.sep + ".swanproject") + + +def get_project_info(path): + if has_project_file(path): + swanfile = path + os.path.sep + ".swanproject" + try: + with open(swanfile) as json_file: + data = json.load(json_file) + return data + except Exception as e: + print(e) + return {} + else: + return {} + + +def get_project_path(cwd): + if cwd.startswith('/'): + cwd = cwd[1:] + + paths = cwd.split(os.path.sep) + cwd_current = cwd + for i in range(len(paths)): + if has_project_file(cwd_current): + return cwd_current + cwd_current = cwd_current[:-(len(paths[len(paths) - i - 1]) + 1)] + return None + + +def get_project_readme(project_path): + readme_path = project_path + os.path.sep + "README.md" + if os.path.exists(readme_path): + f = open(readme_path, "r") + text = f.read() + f.close() + return text + else: + return None + + +def get_user_script_content(project_path): + user_script_path = project_path + os.path.sep + ".userscript" + if os.path.exists(user_script_path): + f = open(user_script_path, "r") + text = f.read() + f.close() + return text + else: + return "" + + +def get_project_name(project_path): + path = get_project_path(project_path) + name = None + if path is not None: + name = path.split('/')[-1] + return name + + +def check_project_info(project_info): + """ + Allows to check if the .swanproject file content is corrupted. + """ + project_keys = ["stack", "platform", "release", + "user_script", "python3", "python2", "kernel_dirs"] + not_found = [] + status = True + for key in project_keys: + if key not in project_info.keys(): + status = False + not_found.append(key) + return {"status": status, "not_found": not_found} + + +def get_env_isolated(): + """ + Command line required with environmental variables to isolate the environment. + """ + command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] + # checking if we are on EOS to add the env variables + # we required this to read/write in a isolate environment with EOS + if "OAUTH2_FILE" in os.environ: + command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) + if "OAUTH2_TOKEN" in os.environ: + command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) + if "OAUTH_INSPECTION_ENDPOINT" in os.environ: + command.append("OAUTH_INSPECTION_ENDPOINT=%s" % + os.environ["OAUTH_INSPECTION_ENDPOINT"]) + + # special case when the package was not installed like root, useful for development + command.append( + "PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:{}/.local/bin/".format(os.environ["HOME"])) + command.append( + "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib" + ) + + return command diff --git a/SwanProjects/tsconfig.json b/SwanProjects/tsconfig.json new file mode 100644 index 00000000..81139f54 --- /dev/null +++ b/SwanProjects/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "composite": true, + "declaration": true, + "esModuleInterop": true, + "incremental": true, + "jsx": "react", + "module": "esnext", + "moduleResolution": "node", + "noEmitOnError": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "preserveWatchOutput": true, + "resolveJsonModule": true, + "outDir": "lib", + "rootDir": "src", + "strict": true, + "strictNullChecks": false, + "target": "es2017", + "types": [] + }, + "include": ["src/*"] +} diff --git a/SwanProjects/yarn.lock b/SwanProjects/yarn.lock new file mode 100644 index 00000000..3c813621 --- /dev/null +++ b/SwanProjects/yarn.lock @@ -0,0 +1,6362 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.14.5": + version "7.14.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" + integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== + +"@babel/highlight@^7.10.4": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.13.10", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7": + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" + integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== + dependencies: + regenerator-runtime "^0.13.4" + +"@blueprintjs/colors@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/colors/-/colors-1.0.0.tgz#3b3501eae2294098fe5bdf8f37d0c471e95cd0e6" + integrity sha512-eJh111ucz8HYxLBON6ADkAGQQBACqdbX6Zws/GpuiTkeCFJ3IAjZdBpk7IM7/Y5XuGuSS1ujwjnLDOEtyywtKw== + +"@blueprintjs/core@^3.36.0", "@blueprintjs/core@^3.48.0": + version "3.48.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-3.48.0.tgz#e2d27c05943203d4979eae95f8a0c10b24d2b6cc" + integrity sha512-tuAL3dZrNaTq36RRy6O86wjmkiLt8LwHkleZ1zUcn/DC3cXsM3dSsRpV3f662bcEiAXMPeGemSC3tqv6uZCeLg== + dependencies: + "@blueprintjs/colors" "^1.0.0" + "@blueprintjs/icons" "^3.28.0" + "@types/dom4" "^2.0.1" + classnames "^2.2" + dom4 "^2.1.5" + normalize.css "^8.0.1" + popper.js "^1.16.1" + react-lifecycles-compat "^3.0.4" + react-popper "^1.3.7" + react-transition-group "^2.9.0" + resize-observer-polyfill "^1.5.1" + tslib "~1.13.0" + +"@blueprintjs/icons@^3.28.0": + version "3.28.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.28.0.tgz#d06528df4aadc0ea685b0b3be18fd3b19dece9e5" + integrity sha512-gDvvU2ljV4NXsY5ofKcs1ChXAgmqNp/DIMu2uJIJmXhSXfP6JDd4qbnbGMsP3FmLTaqQP3E9oBZqAG/FRB8VmQ== + dependencies: + classnames "^2.2" + tslib "~1.13.0" + +"@blueprintjs/select@^3.15.0": + version "3.17.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/select/-/select-3.17.0.tgz#b4e49ed9dd8a2cebbf27226c3e2f25c4071fdb29" + integrity sha512-38jvSt1zGOJuw6Vj3BrDn1ojZCI+U5UV8xEupGPTEVyszE7RwFoF8l1iDTLbiPwLV5DSOxTN0B6mxeSPX45OQw== + dependencies: + "@blueprintjs/core" "^3.48.0" + classnames "^2.2" + tslib "~1.13.0" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d" + integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g== + +"@emotion/cache@^11.4.0": + version "11.4.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.4.0.tgz#293fc9d9a7a38b9aad8e9337e5014366c3b09ac0" + integrity sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g== + dependencies: + "@emotion/memoize" "^0.7.4" + "@emotion/sheet" "^1.0.0" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + stylis "^4.0.3" + +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@emotion/memoize@^0.7.4": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" + integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== + +"@emotion/react@^11.1.1": + version "11.4.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.4.1.tgz#a1b0b767b5bad57515ffb0cad9349614d27f4d57" + integrity sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg== + dependencies: + "@babel/runtime" "^7.13.10" + "@emotion/cache" "^11.4.0" + "@emotion/serialize" "^1.0.2" + "@emotion/sheet" "^1.0.2" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.0.0", "@emotion/serialize@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965" + integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A== + dependencies: + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.4" + "@emotion/unitless" "^0.7.5" + "@emotion/utils" "^1.0.0" + csstype "^3.0.2" + +"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.2.tgz#1d9ffde531714ba28e62dac6a996a8b1089719d0" + integrity sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw== + +"@emotion/unitless@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@emotion/utils@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" + integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== + +"@emotion/weak-memoize@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" + integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== + +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@fortawesome/fontawesome-free@^5.12.0": + version "5.15.4" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5" + integrity sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg== + +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" + integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + +"@hypnosphi/create-react-context@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6" + integrity sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A== + dependencies: + gud "^1.0.0" + warning "^4.0.3" + +"@jupyterlab/application@^3.0.11": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/application/-/application-3.1.8.tgz#81ddb1f24bf7bd92b19893cefbf7e988bde5281c" + integrity sha512-lc9WVkf8KqV0ugk04goqpbrNNwXtKIqjcTQ91FxH44lExKTmxm8VqODV7Ej00k5WKNtm10JP98CR1KlZZJXo0g== + dependencies: + "@fortawesome/fontawesome-free" "^5.12.0" + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/docregistry" "^3.1.8" + "@jupyterlab/rendermime" "^3.1.8" + "@jupyterlab/rendermime-interfaces" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/statedb" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/application" "^1.16.0" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + +"@jupyterlab/apputils@^3.0.10", "@jupyterlab/apputils@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/apputils/-/apputils-3.1.8.tgz#ad9a5805978e52857a80eeceb9367d6641cffd26" + integrity sha512-oG1XUJ5VbvmhrzPFJx+PQyM6DnltG8yzJCzYmMv1HZFlO01l3EjkzgEql7LMZyDZ58GYhJejaCi1sYt3D42/uw== + dependencies: + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/settingregistry" "^3.1.8" + "@jupyterlab/statedb" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.19.0" + "@types/react" "^17.0.0" + react "^17.0.1" + react-dom "^17.0.1" + sanitize-html "~2.3.3" + url "^0.11.0" + +"@jupyterlab/builder@^3.0.0": + version "3.1.9" + resolved "https://registry.yarnpkg.com/@jupyterlab/builder/-/builder-3.1.9.tgz#c144866529b6d1ad5a895fe79c893468ad7a4daa" + integrity sha512-dDSv8Bb/3XNEcDbj4R8zm8cyksnCb1dak5BpWblmEXy383osWVTLh/KDQ7nwaWtrOCefxaig8wi267T/mHO9Ug== + dependencies: + "@jupyterlab/buildutils" "^3.1.9" + "@lumino/algorithm" "^1.3.3" + "@lumino/application" "^1.16.0" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.19.0" + ajv "^6.12.3" + commander "~6.0.0" + css-loader "^5.0.1" + duplicate-package-checker-webpack-plugin "^3.0.0" + file-loader "~6.0.0" + fs-extra "^9.0.1" + glob "~7.1.6" + license-webpack-plugin "^2.3.14" + mini-css-extract-plugin "~1.3.2" + path-browserify "^1.0.0" + process "^0.11.10" + raw-loader "~4.0.0" + style-loader "~2.0.0" + supports-color "^7.2.0" + svg-url-loader "~6.0.0" + terser-webpack-plugin "^4.1.0" + to-string-loader "^1.1.6" + url-loader "~4.1.0" + webpack "^5.41.1" + webpack-cli "^4.1.0" + webpack-merge "^5.1.2" + worker-loader "^3.0.2" + +"@jupyterlab/buildutils@^3.1.9": + version "3.1.9" + resolved "https://registry.yarnpkg.com/@jupyterlab/buildutils/-/buildutils-3.1.9.tgz#457fe7490cb147f3c1de015c966bb3e02c2f8364" + integrity sha512-PPosDz7rUF6inz6FyIkrDQx/uIkyFS6Ni+1D0o7FTz6p5emyB5uKuoLXksvSeCPrt4R1G/NYa+up8xw8E95n9Q== + dependencies: + "@lumino/coreutils" "^1.5.3" + "@yarnpkg/lockfile" "^1.1.0" + child_process "~1.0.2" + commander "~6.0.0" + crypto "~1.0.1" + dependency-graph "^0.9.0" + fs-extra "^9.0.1" + glob "~7.1.6" + inquirer "^7.0.0" + minimatch "~3.0.4" + os "~0.1.1" + package-json "^6.5.0" + prettier "~2.1.1" + process "^0.11.10" + semver "^7.3.2" + sort-package-json "~1.44.0" + typescript "~4.1.3" + verdaccio "^5.1.1" + +"@jupyterlab/codeeditor@^3.0.9", "@jupyterlab/codeeditor@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/codeeditor/-/codeeditor-3.1.8.tgz#92ddc0f1da830fa1d530695c63c268656d25d244" + integrity sha512-UskhfNtvk7niHFDi7teqrufQsmJ72ZcHra5Nzu8axFlb37gsK+Gf8Wgn2Tddnd17Ay32+U+Hu3bJF4GVfMJp9A== + dependencies: + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/nbformat" "^3.1.8" + "@jupyterlab/observables" "^4.1.8" + "@jupyterlab/shared-models" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + +"@jupyterlab/codemirror@^3.0.9", "@jupyterlab/codemirror@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/codemirror/-/codemirror-3.1.8.tgz#1a3c56690aab10ea10e44248a8f98416d4a7b7b0" + integrity sha512-tO8dRJrXt7F9/uByVeTdfemeBep2Rno1RAa8BNxq/iipBKt0IMIf3ts5LzECAX0omYWcAbcvDo1YjwfShSw0wQ== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/codeeditor" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/nbformat" "^3.1.8" + "@jupyterlab/observables" "^4.1.8" + "@jupyterlab/shared-models" "^3.1.8" + "@jupyterlab/statusbar" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + codemirror "~5.61.0" + react "^17.0.1" + y-codemirror "^2.1.1" + +"@jupyterlab/coreutils@^5.1.8": + version "5.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/coreutils/-/coreutils-5.1.8.tgz#a11140f40950e5ea68865f4090ff0b08647ed447" + integrity sha512-HxQuLfpKtkFmN4Y2meVnTr4FIKBQ4mHux5PJkdluRAsqOeycPKCS+f/dd7M5PVLJJMjw/Y+0o5qGNeatwbfF5A== + dependencies: + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + minimist "~1.2.0" + moment "^2.24.0" + path-browserify "^1.0.0" + url-parse "~1.5.1" + +"@jupyterlab/docmanager@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/docmanager/-/docmanager-3.1.8.tgz#328ee8a28b190aed64f4d704d8d0bb7c2753520a" + integrity sha512-Aalj1r3OqPdydkcYIK7zNJCLfn4IqWCR3OTrN97BsS83BjbPV4IuK0fk4w2OwhcRVPK/KpZ36Bnpo1S/MQ2tMA== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/docprovider" "^3.1.8" + "@jupyterlab/docregistry" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/statusbar" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + react "^17.0.1" + +"@jupyterlab/docprovider@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/docprovider/-/docprovider-3.1.8.tgz#56f270717d60499b7e5383e240a732dfc14da4ea" + integrity sha512-XfIxhSwOKt8VSg8SQmWKv4UICYhR6u6kcM4ObFeeX2jyNZemUb+X8KDn2JDIhYRnHVkI9py+x0GFncZ3jO0SvA== + dependencies: + "@jupyterlab/shared-models" "^3.1.8" + "@lumino/coreutils" "^1.5.3" + lib0 "^0.2.42" + y-websocket "^1.3.15" + yjs "^13.5.6" + +"@jupyterlab/docregistry@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/docregistry/-/docregistry-3.1.8.tgz#20d83d9ddba162f25bdde23e644d785f065546a4" + integrity sha512-c0rrMz6TSFuTH/ZILxAV/DUy0PZ85ZqmVBfnWl3RksTrwlee/6ki8FZRiJRB6ehgm+49FsG2cHGaXT8kCE6tkg== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/codeeditor" "^3.1.8" + "@jupyterlab/codemirror" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/docprovider" "^3.1.8" + "@jupyterlab/observables" "^4.1.8" + "@jupyterlab/rendermime" "^3.1.8" + "@jupyterlab/rendermime-interfaces" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/shared-models" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + yjs "^13.5.6" + +"@jupyterlab/filebrowser@^3.0.11": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/filebrowser/-/filebrowser-3.1.8.tgz#65a2cdd6996da7a00528e0899b11f71274301a35" + integrity sha512-SMtpHZrE4opUPJE9dr9WdVjxBbv4NyVh4AagKPxKATnpLQtd/NgHUpM9uW08zWK4I/OPLbTENiPOjRn6rpFw8g== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/docmanager" "^3.1.8" + "@jupyterlab/docregistry" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/statedb" "^3.1.8" + "@jupyterlab/statusbar" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.19.0" + react "^17.0.1" + +"@jupyterlab/launcher@^3.0.10": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/launcher/-/launcher-3.1.8.tgz#7c3e6d476616971b12afd2cf9eba0139ae46da5e" + integrity sha512-GgTY8HXba9MKRG9UBv+5yUE55oVXFdlxMyM9MhEKj8Hflpr32WVMXsut2Js07SrQ8CFg0iINby8nfjT/RYYI8Q== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/widgets" "^1.19.0" + react "^17.0.1" + +"@jupyterlab/mainmenu@^3.0.10": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/mainmenu/-/mainmenu-3.1.8.tgz#4bfe77e41f4fc77bc6927f4779000f33dadc7fb0" + integrity sha512-Vc8XaE+vYntSXf2fIG5L6wwB5ShYf/79StV1hP2LidbxUOhDjy9fzr2x2gdijrQ8kMEf09ssOv5QpiPsdhZO4g== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/widgets" "^1.19.0" + +"@jupyterlab/nbformat@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-3.1.8.tgz#958c5e060babe07c9688995d76cd0e3b956d26b9" + integrity sha512-FFVerxKdbFQ/lxP3Xd3drYkQ82Le8HdBF6zxHZND7k+I2TMyE8HV+834Nn+HR4C7TintP9ap9P3wgg/H8MTf4g== + dependencies: + "@lumino/coreutils" "^1.5.3" + +"@jupyterlab/observables@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/observables/-/observables-4.1.8.tgz#7cd57065c11d8d35045b9582c676d4ef6a855a91" + integrity sha512-4FQ4rf0+DFNn/TD/3KMJm+hCHLGiTwj337hpipYVxrIBxYqXwG9jzBDRJC1Dqz+dB2dihnuGHxDnOaUinaM+ow== + dependencies: + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + +"@jupyterlab/rendermime-interfaces@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-3.1.8.tgz#db0c0e3f8ef169d2c35ccbc3153bf11f47303727" + integrity sha512-MRs283+0ZGnpQIaKPV5VHnpqaCxAFnZBhcx5pi3jd8KPQpzFJs+hf4kgWOOqVkPayehp5e5MKKEB26RB4b+3Bw== + dependencies: + "@jupyterlab/translation" "^3.1.8" + "@lumino/coreutils" "^1.5.3" + "@lumino/widgets" "^1.19.0" + +"@jupyterlab/rendermime@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime/-/rendermime-3.1.8.tgz#398a75a4d179c4dcc9ac0e27f5c78822473e083a" + integrity sha512-NTKL1Xm81iukZf2UcdseFWsdkx08WTKdYk8LHIKI3hzoKdfAZNb7YQHzCAlBglXETR6mOtE4XNXBFtvu2QfoHQ== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/codemirror" "^3.1.8" + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/nbformat" "^3.1.8" + "@jupyterlab/observables" "^4.1.8" + "@jupyterlab/rendermime-interfaces" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/translation" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + lodash.escape "^4.0.1" + marked "^2.0.0" + +"@jupyterlab/services@^6.0.10", "@jupyterlab/services@^6.1.8": + version "6.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/services/-/services-6.1.8.tgz#ff189dd2f4282dac6f099e6020ce47897440d78b" + integrity sha512-uofOdL0g0mwrgoGbFLRArx4EqZEMAHE4arqZ/Hb6/FkIUa15ikS8w9mJ5aiaolmmiSytUP3618hc09R2QUP2nw== + dependencies: + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/nbformat" "^3.1.8" + "@jupyterlab/observables" "^4.1.8" + "@jupyterlab/settingregistry" "^3.1.8" + "@jupyterlab/statedb" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/signaling" "^1.4.3" + node-fetch "^2.6.0" + ws "^7.4.6" + +"@jupyterlab/settingregistry@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/settingregistry/-/settingregistry-3.1.8.tgz#013e64f63a3582c323b9266b63ea5f6565a6d7d2" + integrity sha512-1ti+5js1rpK04xaFWF5Hgo1buvN7VzcsblC8azb2fVKAfEmrvUjIZlw/r0LSluUA14CMdwR0NwqE5MH6dCBdVA== + dependencies: + "@jupyterlab/statedb" "^3.1.8" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + ajv "^6.12.3" + json5 "^2.1.1" + +"@jupyterlab/shared-models@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/shared-models/-/shared-models-3.1.8.tgz#5f7be922907953124bb75dd6611da7e4435af2a4" + integrity sha512-l/6wuKwYBYem6fdthNvLMLt2WIQ1xkl90kwajU4O2XQsLMXLkPY42DFFI8ahmghSYsdwciuFzg1cf/IRZvmdaQ== + dependencies: + "@jupyterlab/nbformat" "^3.1.8" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + y-protocols "^1.0.5" + yjs "^13.5.6" + +"@jupyterlab/statedb@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/statedb/-/statedb-3.1.8.tgz#d9bfaa3e4be51a242b8a6bf16c48a400ed3760b8" + integrity sha512-RLx7atICQuBxaWU1snquXG8zPa0KjWGNVzo3F7tPKcrhDBrZdRJFvvhFCUweY1GNfO1fGuI00xrSbvaftuSqcQ== + dependencies: + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + +"@jupyterlab/statusbar@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/statusbar/-/statusbar-3.1.8.tgz#e196372bc8f5892fc8229b84d45a68b3cf830377" + integrity sha512-O9im0v/ohGgf6uhssEKPsBZE57FqQvUhOEcEBa85ptRqRkxF8NZraNRx04atGaJLMNO3TdIhJLcB022OGXq7hg== + dependencies: + "@jupyterlab/apputils" "^3.1.8" + "@jupyterlab/codeeditor" "^3.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/translation" "^3.1.8" + "@jupyterlab/ui-components" "^3.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.19.0" + csstype "~3.0.3" + react "^17.0.1" + typestyle "^2.0.4" + +"@jupyterlab/translation@^3.0.10", "@jupyterlab/translation@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/translation/-/translation-3.1.8.tgz#83526f82c8b22f4e98c2cd0246085f42aae41f08" + integrity sha512-oQ7vr/tmCl5uxkghDj8UwXR0ubkHf9gvOb6OvG112C3lq/VwC1boYeniYalX7AjcZghP2DPXvRjMjwrQtk/60Q== + dependencies: + "@jupyterlab/coreutils" "^5.1.8" + "@jupyterlab/services" "^6.1.8" + "@jupyterlab/statedb" "^3.1.8" + "@lumino/coreutils" "^1.5.3" + +"@jupyterlab/ui-components@^3.0.8", "@jupyterlab/ui-components@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@jupyterlab/ui-components/-/ui-components-3.1.8.tgz#158e611a2136a93ba0797134a1aa26fc7ef066b0" + integrity sha512-d64RtvXj8gZX2K/XW2dSbtHxUE+LmE9eDdyBgKa6Hh15VgOVEsht/mcc+kf5QvNUJZCarMPC4PshmX56HuOI5Q== + dependencies: + "@blueprintjs/core" "^3.36.0" + "@blueprintjs/select" "^3.15.0" + "@jupyterlab/coreutils" "^5.1.8" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.19.0" + react "^17.0.1" + react-dom "^17.0.1" + typestyle "^2.0.4" + +"@lumino/algorithm@^1.3.3", "@lumino/algorithm@^1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@lumino/algorithm/-/algorithm-1.6.2.tgz#c68968d04857f7fbe9f4856ab3078aee7694868e" + integrity sha512-4QlhUduCKjoHqWjYRayDZvd5kgrj2EWm4ELFC1IIcQ8SuIUPCmyAIsIVx5l76lI3hUkmHvVl411s9qWLQuiX+A== + +"@lumino/application@^1.16.0": + version "1.23.2" + resolved "https://registry.yarnpkg.com/@lumino/application/-/application-1.23.2.tgz#01ffb2900af9c9dc54d6b732c398668849bae93c" + integrity sha512-7LusRW6jXbqZCCd2HEn7AmKoE/SZYRkmrJXbEuSZks7oCnV+LE/JBKrMtRcMCHQzk7auiqFVb8JS8T/Tb/U0DA== + dependencies: + "@lumino/commands" "^1.15.2" + "@lumino/coreutils" "^1.8.2" + "@lumino/widgets" "^1.26.2" + +"@lumino/collections@^1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@lumino/collections/-/collections-1.6.2.tgz#9cbfd54e81aefbc1693d8dbc2b3b45a2c011b8cd" + integrity sha512-keStZ5Vv4iua6EZAY1RouDmGe7JJq46NQSbw96Q1AvLqFPwLl4ll3cyPBlnIgMsJqeHUP/UZ7ef7Akf744pLBQ== + dependencies: + "@lumino/algorithm" "^1.6.2" + +"@lumino/commands@^1.12.0", "@lumino/commands@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@lumino/commands/-/commands-1.15.2.tgz#9543f80667fac50765466f4470c3915c580360de" + integrity sha512-H4FUJxF4lM3hPkGtM8mgmqEOwOUZtOkcqacH6Emcw7dyeniic1q1czsQYxuXixz1hrBO/2t51qZGwo2ehq/RnA== + dependencies: + "@lumino/algorithm" "^1.6.2" + "@lumino/coreutils" "^1.8.2" + "@lumino/disposable" "^1.7.2" + "@lumino/domutils" "^1.5.2" + "@lumino/keyboard" "^1.5.2" + "@lumino/signaling" "^1.7.2" + "@lumino/virtualdom" "^1.11.2" + +"@lumino/coreutils@^1.5.3", "@lumino/coreutils@^1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@lumino/coreutils/-/coreutils-1.8.2.tgz#e303a3b12a319f0f304b835269ca85319346d219" + integrity sha512-IPlQEU9yJ/ysaEYiTqb25xKnKgAANiRy5huC2fPWuJtNfhjl8fyWsj8knTJC6XmI09NzhP8ZAyMAZjTFPYnyaw== + +"@lumino/disposable@^1.4.3", "@lumino/disposable@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@lumino/disposable/-/disposable-1.7.2.tgz#86c30bee7335bc624881195501de58a074195b34" + integrity sha512-3+1sVpO0X4Ymh/VcHgy/0aM6VrWecSPDDtJnyEzrKJNvVj582FnD3Ftej5hx5yc4DGjOJ4U7Ue7zkIKc+vOxTw== + dependencies: + "@lumino/algorithm" "^1.6.2" + "@lumino/signaling" "^1.7.2" + +"@lumino/domutils@^1.2.3", "@lumino/domutils@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@lumino/domutils/-/domutils-1.5.2.tgz#a0130967b95aa4a81cd7abc21db98e2f3416ce51" + integrity sha512-2Hd3Bp6BObNwOaejeJUOAQcwjqSwORuDCmyJyqKu/5MnX7aQqPAMFtnNvVufcKnxV8lBej3fhV3zZYfChYlFqA== + +"@lumino/dragdrop@^1.10.2", "@lumino/dragdrop@^1.7.1": + version "1.10.2" + resolved "https://registry.yarnpkg.com/@lumino/dragdrop/-/dragdrop-1.10.2.tgz#09f539873c8a35e0394aa4b4aca3db2825d57cf1" + integrity sha512-QWichLgP5FW1UBF5wMADXxgXy/gz3ZGgpZKdaTyrkEN/Zwpz7FquH0f/JE1XXKkmlmHi/x8YTfHX50ImHXTwsQ== + dependencies: + "@lumino/coreutils" "^1.8.2" + "@lumino/disposable" "^1.7.2" + +"@lumino/keyboard@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@lumino/keyboard/-/keyboard-1.5.2.tgz#1d937cf071b16a6dbed3a2a5876e0892a504f9df" + integrity sha512-/tme1dGnLkqWuTTBkvOaywDCgt2dyYwIcVo3eCTHpJIG4pPactGlMyd7PypgT3NXBlNxEvZnQSGzYCOXP1/2IQ== + +"@lumino/messaging@^1.4.3", "@lumino/messaging@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@lumino/messaging/-/messaging-1.7.2.tgz#568f38d4fa5ef3cecef24cde3f85e582b038f5ed" + integrity sha512-nkhNiL4ZZEmf5M+zxPJgV7M+LJ3wUHMX1hnFsqcvrBkLBPkqZVowDzEcr0oN4UOUuHaMJehjdiN9SG0T5chosQ== + dependencies: + "@lumino/algorithm" "^1.6.2" + "@lumino/collections" "^1.6.2" + +"@lumino/polling@^1.3.3": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@lumino/polling/-/polling-1.6.2.tgz#8bc7019538e2b03de2d553bf0eb67c7abeeaad61" + integrity sha512-jO26SptvND+au6cueV/YVBuq7a8Tu23x7BCm7bWwlJajZq5VXLm0g7ktOgcz7TLsL31Lvc/jN/HPAxd2YAS0Vw== + dependencies: + "@lumino/coreutils" "^1.8.2" + "@lumino/disposable" "^1.7.2" + "@lumino/signaling" "^1.7.2" + +"@lumino/properties@^1.2.3", "@lumino/properties@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@lumino/properties/-/properties-1.5.2.tgz#aa3211552ddb2e9566f90cd75f074be53879ad6e" + integrity sha512-rrlbO3mi6wXe5OJxnqhRv8oQdGdjqPwtP3a6M+AdHXG71yV1R5DQjE6ez1z9nLJP5wYFlITcxqOU+xq7FDLFGQ== + +"@lumino/signaling@^1.4.3", "@lumino/signaling@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@lumino/signaling/-/signaling-1.7.2.tgz#76eec02b6edf333b804b232fdd6cc5aff669076a" + integrity sha512-HnEDrvLPE3HiLzZuXMU2uULp5f15QsPl6pwOpl8q9D/dhKVytPH3j04J+pl4SO4NlbtTktIWqVvSDWl2WbMWZg== + dependencies: + "@lumino/algorithm" "^1.6.2" + +"@lumino/virtualdom@^1.11.2", "@lumino/virtualdom@^1.8.0": + version "1.11.2" + resolved "https://registry.yarnpkg.com/@lumino/virtualdom/-/virtualdom-1.11.2.tgz#131be2b9f2fcb443722cc07779be8af3cf675abc" + integrity sha512-lTvQObA5FBglIuD85aZo7bF1VtkrxB5jEQ8PxlnsVSH/lgE6SWA9syfCIByMxhFSne4y5yD7SbICUViJF4ht+w== + dependencies: + "@lumino/algorithm" "^1.6.2" + +"@lumino/widgets@^1.19.0", "@lumino/widgets@^1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@lumino/widgets/-/widgets-1.26.2.tgz#f8833af7112558d270e1931b05a55c6341ffae7f" + integrity sha512-/UfEGznReDUXyKw0qlaDgNRMO2sNJ/vJ3kajznGMep+FrMVjIHEtMXtUbzlRasyg6CN88zS5G6wD1DdZF3ZtSg== + dependencies: + "@lumino/algorithm" "^1.6.2" + "@lumino/commands" "^1.15.2" + "@lumino/coreutils" "^1.8.2" + "@lumino/disposable" "^1.7.2" + "@lumino/domutils" "^1.5.2" + "@lumino/dragdrop" "^1.10.2" + "@lumino/keyboard" "^1.5.2" + "@lumino/messaging" "^1.7.2" + "@lumino/properties" "^1.5.2" + "@lumino/signaling" "^1.7.2" + "@lumino/virtualdom" "^1.11.2" + +"@material-ui/core@^4.12.2": + version "4.12.3" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.12.3.tgz#80d665caf0f1f034e52355c5450c0e38b099d3ca" + integrity sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/styles" "^4.11.4" + "@material-ui/system" "^4.12.1" + "@material-ui/types" "5.1.0" + "@material-ui/utils" "^4.11.2" + "@types/react-transition-group" "^4.2.0" + clsx "^1.0.4" + hoist-non-react-statics "^3.3.2" + popper.js "1.16.1-lts" + prop-types "^15.7.2" + react-is "^16.8.0 || ^17.0.0" + react-transition-group "^4.4.0" + +"@material-ui/icons@^4.11.2": + version "4.11.2" + resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5" + integrity sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ== + dependencies: + "@babel/runtime" "^7.4.4" + +"@material-ui/styles@^4.11.4": + version "4.11.4" + resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d" + integrity sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew== + dependencies: + "@babel/runtime" "^7.4.4" + "@emotion/hash" "^0.8.0" + "@material-ui/types" "5.1.0" + "@material-ui/utils" "^4.11.2" + clsx "^1.0.4" + csstype "^2.5.2" + hoist-non-react-statics "^3.3.2" + jss "^10.5.1" + jss-plugin-camel-case "^10.5.1" + jss-plugin-default-unit "^10.5.1" + jss-plugin-global "^10.5.1" + jss-plugin-nested "^10.5.1" + jss-plugin-props-sort "^10.5.1" + jss-plugin-rule-value-function "^10.5.1" + jss-plugin-vendor-prefixer "^10.5.1" + prop-types "^15.7.2" + +"@material-ui/system@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.12.1.tgz#2dd96c243f8c0a331b2bb6d46efd7771a399707c" + integrity sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.11.2" + csstype "^2.5.2" + prop-types "^15.7.2" + +"@material-ui/types@5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2" + integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== + +"@material-ui/utils@^4.11.2": + version "4.11.2" + resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.11.2.tgz#f1aefa7e7dff2ebcb97d31de51aecab1bb57540a" + integrity sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA== + dependencies: + "@babel/runtime" "^7.4.4" + prop-types "^15.7.2" + react-is "^16.8.0 || ^17.0.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@types/dom4@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.2.tgz#6495303f049689ce936ed328a3e5ede9c51408ee" + integrity sha512-Rt4IC1T7xkCWa0OG1oSsPa0iqnxlDeQqKXZAHrQGLb7wFGncWm85MaxKUjAGejOrUynOgWlFi4c6S6IyJwoK4g== + +"@types/eslint-scope@^3.7.0": + version "3.7.1" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e" + integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" + integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.50": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + +"@types/glob@^7.1.1": + version "7.1.4" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672" + integrity sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/minimatch@*": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + +"@types/node@*": + version "16.7.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.1.tgz#c6b9198178da504dfca1fd0be9b2e1002f1586f0" + integrity sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A== + +"@types/prop-types@*": + version "15.7.4" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" + integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== + +"@types/react-dom@*": + version "17.0.9" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add" + integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg== + dependencies: + "@types/react" "*" + +"@types/react-select@^4.0.13": + version "4.0.17" + resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-4.0.17.tgz#2e5ab4042c09c988bfc2711550329b0c3c9f8513" + integrity sha512-ZK5wcBhJaqC8ntQl0CJvK2KXNNsk1k5flM7jO+vNPPlceRzdJQazA6zTtQUyNr6exp5yrAiwiudtYxgGlgGHLg== + dependencies: + "@emotion/serialize" "^1.0.0" + "@types/react" "*" + "@types/react-dom" "*" + "@types/react-transition-group" "*" + +"@types/react-transition-group@*", "@types/react-transition-group@^4.2.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.2.tgz#38890fd9db68bf1f2252b99a942998dc7877c5b3" + integrity sha512-KibDWL6nshuOJ0fu8ll7QnV/LVTo3PzQ9aCPnRUYPfX7eZohHwLIdNHj7pftanREzHNP4/nJa8oeM73uSiavMQ== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^17.0.0": + version "17.0.19" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.19.tgz#8f2a85e8180a43b57966b237d26a29481dacc991" + integrity sha512-sX1HisdB1/ZESixMTGnMxH9TDe8Sk709734fEQZzCV/4lSu9kJCPbo2PbTRoZM+53Pp0P10hYVyReUueGwUi4A== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/source-list-map@*": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== + +"@types/webpack-sources@^0.1.5": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.9.tgz#da69b06eb34f6432e6658acb5a6893c55d983920" + integrity sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.6.1" + +"@typescript-eslint/eslint-plugin@^4.8.1": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.3.tgz#95cb8029a8bd8bd9c7f4ab95074a7cb2115adefa" + integrity sha512-tBgfA3K/3TsZY46ROGvoRxQr1wBkclbVqRQep97MjVHJzcRBURRY3sNFqLk0/Xr//BY5hM9H2p/kp+6qim85SA== + dependencies: + "@typescript-eslint/experimental-utils" "4.29.3" + "@typescript-eslint/scope-manager" "4.29.3" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@4.29.3": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.3.tgz#52e437a689ccdef73e83c5106b34240a706f15e1" + integrity sha512-ffIvbytTVWz+3keg+Sy94FG1QeOvmV9dP2YSdLFHw/ieLXWCa3U1TYu8IRCOpMv2/SPS8XqhM1+ou1YHsdzKrg== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.29.3" + "@typescript-eslint/types" "4.29.3" + "@typescript-eslint/typescript-estree" "4.29.3" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^4.8.1": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.29.3.tgz#2ac25535f34c0e98f50c0e6b28c679c2357d45f2" + integrity sha512-jrHOV5g2u8ROghmspKoW7pN8T/qUzk0+DITun0MELptvngtMrwUJ1tv5zMI04CYVEUsSrN4jV7AKSv+I0y0EfQ== + dependencies: + "@typescript-eslint/scope-manager" "4.29.3" + "@typescript-eslint/types" "4.29.3" + "@typescript-eslint/typescript-estree" "4.29.3" + debug "^4.3.1" + +"@typescript-eslint/scope-manager@4.29.3": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.3.tgz#497dec66f3a22e459f6e306cf14021e40ec86e19" + integrity sha512-x+w8BLXO7iWPkG5mEy9bA1iFRnk36p/goVlYobVWHyDw69YmaH9q6eA+Fgl7kYHmFvWlebUTUfhtIg4zbbl8PA== + dependencies: + "@typescript-eslint/types" "4.29.3" + "@typescript-eslint/visitor-keys" "4.29.3" + +"@typescript-eslint/types@4.29.3": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.3.tgz#d7980c49aef643d0af8954c9f14f656b7fd16017" + integrity sha512-s1eV1lKNgoIYLAl1JUba8NhULmf+jOmmeFO1G5MN/RBCyyzg4TIOfIOICVNC06lor+Xmy4FypIIhFiJXOknhIg== + +"@typescript-eslint/typescript-estree@4.29.3": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.3.tgz#1bafad610015c4ded35c85a70b6222faad598b40" + integrity sha512-45oQJA0bxna4O5TMwz55/TpgjX1YrAPOI/rb6kPgmdnemRZx/dB0rsx+Ku8jpDvqTxcE1C/qEbVHbS3h0hflag== + dependencies: + "@typescript-eslint/types" "4.29.3" + "@typescript-eslint/visitor-keys" "4.29.3" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@4.29.3": + version "4.29.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.3.tgz#c691760a00bd86bf8320d2a90a93d86d322f1abf" + integrity sha512-MGGfJvXT4asUTeVs0Q2m+sY63UsfnA+C/FDgBKV3itLBmM9H0u+URcneePtkd0at1YELmZK6HSolCqM4Fzs6yA== + dependencies: + "@typescript-eslint/types" "4.29.3" + eslint-visitor-keys "^2.0.0" + +"@verdaccio/commons-api@10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@verdaccio/commons-api/-/commons-api-10.0.0.tgz#2d7de8722f94181f1a71891fe91198a7c14e6dea" + integrity sha512-UC8wrRI9FvqjfDeB1RijF7aVI0JJhCOI8RkEDibCT/JD8zVngphrNmgSWcjo8Es3lRiu7NugWXDSuggCCeCfUg== + dependencies: + http-errors "1.8.0" + http-status-codes "1.4.0" + +"@verdaccio/file-locking@10.0.0", "@verdaccio/file-locking@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-10.0.0.tgz#3d476a6ba28207c795d49828438e7335166c1cfc" + integrity sha512-2tQUbJF3tQ3CY9grAlpovaF/zu8G56CBYMaeHwMBHo9rAmsJI9i7LfliHGS6Jygbs8vd0cOCPT7vl2CL9T8upw== + dependencies: + lockfile "1.0.4" + +"@verdaccio/local-storage@10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@verdaccio/local-storage/-/local-storage-10.0.6.tgz#be485a8107ad84206cf80702d325ca47b7f22f68" + integrity sha512-YEImOMUL56lziS/N3o1YzoOcVGZXpyZclGSonw7XQ1lKQEvEhU06V2+tIdjPgtqIOuH9ZKdPeBsBuN7ILa2qzQ== + dependencies: + "@verdaccio/commons-api" "10.0.0" + "@verdaccio/file-locking" "10.0.0" + "@verdaccio/streams" "10.0.0" + async "3.2.0" + debug "4.3.1" + lodash "4.17.21" + lowdb "1.0.0" + mkdirp "1.0.4" + +"@verdaccio/readme@10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@verdaccio/readme/-/readme-10.0.0.tgz#f9627c32b309ace311318b98b2c42226823f6cd7" + integrity sha512-OD3dMnRC8SvhgytEzczMBleN+K/3lMqyWw/epeXvolCpCd7mW/Dl5zSR25GiHh/2h3eTKP/HMs4km8gS1MMLgA== + dependencies: + dompurify "^2.2.6" + jsdom "15.2.1" + marked "^2.0.1" + +"@verdaccio/streams@10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@verdaccio/streams/-/streams-10.0.0.tgz#8b06e1d6f06e906ebda0f1d4089cdb651a533541" + integrity sha512-PqxxY11HhweN6z1lwfn9ydLCdnOkCPpthMZs+SGCDz8Rt6gOyrjJVslV7o4uobDipjD9+hUPpJHDeO33Qt24uw== + +"@verdaccio/ui-theme@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@verdaccio/ui-theme/-/ui-theme-3.1.0.tgz#21108f3c1b97e6db5901509d935e1f4ce475950a" + integrity sha512-NmJOcv25/OtF84YrmYxi31beFde7rt+/y2qlnq0wYR4ZCFRE5TsuqisTVTe1OyJ8D8JwwPMyMSMSMtlMwUfqIQ== + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa" + integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ== + +"@webpack-cli/info@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b" + integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.2.tgz#ea584b637ff63c5a477f6f21604b5a205b72c9ec" + integrity sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +JSONStream@1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-import-assertions@^1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz#580e3ffcae6770eebeec76c3b9723201e9d01f78" + integrity sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA== + +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + +acorn@^6.0.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^7.1.0, acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" + integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.6.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" + integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +apache-md5@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.2.tgz#ee49736b639b4f108b6e9e626c6da99306b41692" + integrity sha1-7klza2ObTxCLbp5ibG2pkwa0FpI= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-includes@^3.1.2, array-includes@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" + integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + get-intrinsic "^1.1.1" + is-string "^1.0.5" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flatmap@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" + integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + function-bind "^1.1.1" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + +async@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8" + integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bcryptjs@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.14.5: + version "4.16.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.8.tgz#cb868b0b554f137ba6e33de0ecff2eda403c4fb0" + integrity sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ== + dependencies: + caniuse-lite "^1.0.30001251" + colorette "^1.3.0" + electron-to-chromium "^1.3.811" + escalade "^3.1.1" + node-releases "^1.1.75" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacache@^15.0.5: + version "15.2.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" + integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== + dependencies: + "@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.1" + tar "^6.0.2" + unique-filename "^1.1.1" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001251: + version "1.0.30001251" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz#6853a606ec50893115db660f82c094d18f096d85" + integrity sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +child_process@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/child_process/-/child_process-1.0.2.tgz#b1f7e7fc73d25e7fd1d455adc94e143830182b5a" + integrity sha1-sffn/HPSXn/R1FWtyU4UODAYK1o= + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +classnames@^2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +clipanion@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-3.0.1.tgz#cba44989fa18a625d7d02800ea98c835e973fc26" + integrity sha512-/ujK3YJ1MGjGr18w99Gl9XZjy4xcC/5bZRJXsgvYG6GbUTO4CTKriC+oUxDbo8G+G/dxDqSJhm8QIDnK6iH6Ig== + dependencies: + typanion "^3.3.1" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clsx@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + +codemirror@~5.61.0: + version "5.61.1" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.1.tgz#ccfc8a43b8fcfb8b12e8e75b5ffde48d541406e0" + integrity sha512-+D1NZjAucuzE93vJGbAaXzvoBHwp9nJZWWWF9utjv25+5AZUiah6CIlfb4ikG4MoDsFsCG8niiJH5++OO2LgIQ== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^1.2.1, colorette@^1.2.2, colorette@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af" + integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.0.0.tgz#2b270da94f8fb9014455312f829a1129dbf8887e" + integrity sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA== + +comment-parser@^0.7.2: + version "0.7.6" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12" + integrity sha512-GKNxVA7/iuTnAqGADlTWX4tkhzxZKXp5fLJqKTlQLHkE65XDUKutZ3BHaJC5IGcper2tT3QRD1xr4o3jNpgXXg== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + 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" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookies@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" + integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== + +css-loader@^5.0.1: + version "5.2.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" + integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== + dependencies: + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.5" + +css-vendor@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" + integrity sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ== + dependencies: + "@babel/runtime" "^7.8.3" + is-in-browser "^1.0.2" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssom@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +csstype@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" + integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== + +csstype@^2.5.2: + version "2.6.17" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" + integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== + +csstype@^3.0.2, csstype@~3.0.3: + version "3.0.8" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" + integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +dayjs@1.10.6: + version "1.10.6" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" + integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== + +debug@2.6.9, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-equal@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + 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" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +dependency-graph@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.9.0.tgz#11aed7e203bc8b00f48356d92db27b265c445318" + integrity sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w== + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-newline@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom4@^2.1.5: + version "2.1.6" + resolved "https://registry.yarnpkg.com/dom4/-/dom4-2.1.6.tgz#c90df07134aa0dbd81ed4d6ba1237b36fc164770" + integrity sha512-JkCVGnN4ofKGbjf5Uvc8mmxaATIErKQKSgACdBXpsQ3fY6DlIpAyWfiBSrGkttATssbDCp3psiAKWXk5gmjycA== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== + dependencies: + domelementtype "^2.2.0" + +dompurify@^2.2.6: + version "2.3.1" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.1.tgz#a47059ca21fd1212d3c8f71fdea6943b8bfbdf6a" + integrity sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw== + +domutils@^2.5.2: + version "2.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" + integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplicate-package-checker-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/duplicate-package-checker-webpack-plugin/-/duplicate-package-checker-webpack-plugin-3.0.0.tgz#78bb89e625fa7cf8c2a59c53f62b495fda9ba287" + integrity sha512-aO50/qPC7X2ChjRFniRiscxBLT/K01bALqfcDaf8Ih5OqQ1N4iT/Abx9Ofu3/ms446vHTm46FACIuJUmgUQcDQ== + dependencies: + chalk "^2.3.0" + find-root "^1.0.0" + lodash "^4.17.4" + semver "^5.4.1" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.811: + version "1.3.817" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.817.tgz#911b4775b5d9fa0c4729d4694adc81de85d8d8f6" + integrity sha512-Vw0Faepf2Id9Kf2e97M/c99qf168xg86JLKDxivvlpBQ9KDtjSeX0v+TiuSE25PqeQfTz+NJs375b64ca3XOIQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.8.0: + version "5.8.2" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" + integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +envinfo@7.8.1, envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: + version "1.18.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.5.tgz#9b10de7d4c206a3581fd5b2124233e04db49ae19" + integrity sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-module-lexer@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d" + integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw== + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^1.11.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-prettier@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" + integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== + dependencies: + get-stdin "^6.0.0" + +eslint-import-resolver-node@0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-plugin-jsdoc@^22.0.0: + version "22.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.2.0.tgz#b89159e01ed8eeee4f6512101e96cac6a2999461" + integrity sha512-r8yRB6jGay9tJkx1BherKFtOkpDud086VZenUqZiZe0F7cD4OABhte0xcj3/7mXPuJbaou8WF3JzEtTdDnCzhA== + dependencies: + comment-parser "^0.7.2" + debug "^4.1.1" + jsdoctypeparser "^6.1.0" + lodash "^4.17.15" + regextras "^0.7.0" + semver "^6.3.0" + spdx-expression-parse "^3.0.0" + +eslint-plugin-prettier@^3.1.4: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" + integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-react@^7.18.3: + version "7.24.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz#eadedfa351a6f36b490aa17f4fa9b14e842b9eb4" + integrity sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q== + dependencies: + array-includes "^3.1.3" + array.prototype.flatmap "^1.2.4" + doctrine "^2.1.0" + has "^1.0.3" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.0.4" + object.entries "^1.1.4" + object.fromentries "^2.0.4" + object.values "^1.1.4" + prop-types "^15.7.2" + resolve "^2.0.0-next.3" + string.prototype.matchall "^4.0.5" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint@^7.14.0: + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.3" + "@humanwhocodes/config-array" "^0.5.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.1.2" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.9" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.5.0.tgz#e93b97ae0cb23f8370380f6107d2d2b7887687ad" + integrity sha512-+ONcYoWj/SoQwUofMr94aGu05Ou4FepKi7N7b+O8T4jVfyIsZQV1/xeS8jpaBzF0csAk0KLXoHCxU7cKYZjo1Q== + dependencies: + type "^2.5.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.0.3, fast-glob@^3.1.1: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-redact@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.1.tgz#d6015b971e933d03529b01333ba7f22c29961e92" + integrity sha512-kYpn4Y/valC9MdrISg47tZOpYBNoTXKgT9GYXFpHN/jYFs+lFkPoisY+LcBODdKVMY96ATzvzsWv+ES/4Kmufw== + +fast-safe-stringify@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz#dc2af48c46cf712b683e849b2bbd446b32de936f" + integrity sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag== + +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + +fastq@^1.6.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.12.0.tgz#ed7b6ab5d62393fb2cc591c853652a5c318bf794" + integrity sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg== + dependencies: + reusify "^1.0.4" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-loader@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f" + integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.6.5" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-root@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatstr@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" + integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== + +flatted@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" + integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +free-style@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/free-style/-/free-style-3.1.0.tgz#4e2996029534e6b1731611d843437b9e2f473f08" + integrity sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +git-hooks-list@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-1.0.3.tgz#be5baaf78203ce342f2f844a9d2b03dba1b45156" + integrity sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3, glob@^7.1.4, glob@~7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.6.0, globals@^13.9.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" + integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== + dependencies: + type-fest "^0.20.2" + +globby@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.0.tgz#abfcd0630037ae174a88590132c2f6804e291072" + integrity sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + +globby@^11.0.3: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== + dependencies: + 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@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== + +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0, har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +htmlparser2@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-status-codes@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477" + integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +hyphenate-style-name@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.1, ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-core-module@^2.2.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" + integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-in-browser@^1.0.2, is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" + integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-promise@^2.1.0, is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.0.4, is-regex@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isomorphic.js@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.4.tgz#24ca374163ae54a7ce3b86ce63b701b91aa84969" + integrity sha512-Y4NjZceAwaPXctwsHgNsmfuPxR8lJ3f8X7QTAkhltrX4oGIv+eTlgHLXn4tWysC9zGTi929gapnPp+8F8cg7nA== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jest-worker@^26.5.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.0.2: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" + integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdoctypeparser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz#acfb936c26300d98f1405cb03e20b06748e512a8" + integrity sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA== + +jsdom@15.2.1: + version "15.2.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" + integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== + dependencies: + abab "^2.0.0" + acorn "^7.1.0" + acorn-globals "^4.3.2" + array-equal "^1.0.0" + cssom "^0.4.1" + cssstyle "^2.0.0" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.1" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.2.0" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.7" + saxes "^3.1.9" + symbol-tree "^3.2.2" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^7.0.0" + xml-name-validator "^3.0.0" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.1, json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +jsonwebtoken@8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jss-plugin-camel-case@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.7.1.tgz#e7f7097cf97e9deec599cef3275e213452318b93" + integrity sha512-+ioIyWvmAfgDCWXsQcW1NMnLBvRinOVFkSYJUgewQ6TynOcSj5F1bSU23B7z0p1iqK0PPHIU62xY1iNJD33WGA== + dependencies: + "@babel/runtime" "^7.3.1" + hyphenate-style-name "^1.0.3" + jss "10.7.1" + +jss-plugin-default-unit@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.7.1.tgz#826270e2ee38d7024a281ac67c30d6944f124786" + integrity sha512-tW+dfYVNARBQb/ONzBwd8uyImigyzMiAEDai+AbH5rcHg5h3TtqhAkxx06iuZiT/dZUiFdSKlbe3q9jZGAPIwA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + +jss-plugin-global@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.7.1.tgz#9725c46d662aac2e596a0a8741944c060e2b90a1" + integrity sha512-FbxCnu44IkK/bw8X3CwZKmcAnJqjAb9LujlAc/aP0bMSdVa3/MugKQRyeQSu00uGL44feJJDoeXXiHOakBr/Zw== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + +jss-plugin-nested@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.7.1.tgz#35563a7a710a45307fd6b9742ffada1d72a62eb7" + integrity sha512-RNbICk7FlYKaJyv9tkMl7s6FFfeLA3ubNIFKvPqaWtADK0KUaPsPXVYBkAu4x1ItgsWx67xvReMrkcKA0jSXfA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + tiny-warning "^1.0.2" + +jss-plugin-props-sort@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.7.1.tgz#1d12b26048541ed3a2ed1b69f7fc231605728362" + integrity sha512-eyd5FhA+J0QrpqXxO7YNF/HMSXXl4pB0EmUdY4vSJI4QG22F59vQ6AHtP6fSwhmBdQ98Qd9gjfO+RMxcE39P1A== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + +jss-plugin-rule-value-function@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.7.1.tgz#123eb796eb9982f8efa7a7e362daddd90c0c69fe" + integrity sha512-fGAAImlbaHD3fXAHI3ooX6aRESOl5iBt3LjpVjxs9II5u9tzam7pqFUmgTcrip9VpRqYHn8J3gA7kCtm8xKwHg== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + tiny-warning "^1.0.2" + +jss-plugin-vendor-prefixer@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.7.1.tgz#217821be2d6dacee31d2d464886760ba7742e19a" + integrity sha512-1UHFmBn7hZNsHXTkLLOL8abRl8vi+D1EVzWD4WmLFj55vawHZfnH1oEz6TUf5Y61XHv0smdHabdXds6BgOXe3A== + dependencies: + "@babel/runtime" "^7.3.1" + css-vendor "^2.0.8" + jss "10.7.1" + +jss@10.7.1, jss@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.7.1.tgz#16d846e1a22fb42e857b99f9c6a0c5a27341c804" + integrity sha512-5QN8JSVZR6cxpZNeGfzIjqPEP+ZJwJJfZbXmeABNdxiExyO+eJJDy6WDtqTf8SDKnbL5kZllEpAP71E/Lt7PXg== + dependencies: + "@babel/runtime" "^7.3.1" + csstype "^3.0.2" + is-in-browser "^1.1.3" + tiny-warning "^1.0.2" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" + integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q== + dependencies: + array-includes "^3.1.2" + object.assign "^4.1.2" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" + integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== + +klona@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-js@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-5.0.2.tgz#5e280b8f93abd9ef3a305b13faf0b5397c969b55" + integrity sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg== + dependencies: + abstract-leveldown "~6.2.3" + buffer "^5.5.0" + inherits "^2.0.3" + ltgt "^2.1.2" + +level-packager@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6" + integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw== + dependencies: + level-js "^5.0.0" + level-packager "^5.1.0" + leveldown "^5.4.0" + +leveldown@^5.4.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" + integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== + dependencies: + abstract-leveldown "~6.2.1" + napi-macros "~2.0.0" + node-gyp-build "~4.1.0" + +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lib0@^0.2.31, lib0@^0.2.41, lib0@^0.2.42: + version "0.2.42" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.42.tgz#6d8bf1fb8205dec37a953c521c5ee403fd8769b0" + integrity sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q== + dependencies: + isomorphic.js "^0.2.4" + +license-webpack-plugin@^2.3.14: + version "2.3.21" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.21.tgz#152f5e82d5f51f8bab78905731f2b8042aa5691b" + integrity sha512-rVaYU9TddZN3ao8M/0PrRSCdTp2EW6VQymlgsuScld1vef0Ou7fALx3ePe83KLP3xAEDcPK5fkqUVqGBnbz1zQ== + dependencies: + "@types/webpack-sources" "^0.1.5" + webpack-sources "^1.2.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +loader-runner@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + +loader-utils@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0, loader-utils@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lockfile@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" + integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== + dependencies: + signal-exit "^3.0.2" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + +lodash@4, lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowdb@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" + integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== + dependencies: + graceful-fs "^4.1.3" + is-promise "^2.1.0" + lodash "4" + pify "^3.0.0" + steno "^0.4.1" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@6.0.0, lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + +ltgt@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +lunr-mutable-indexes@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lunr-mutable-indexes/-/lunr-mutable-indexes-2.3.2.tgz#864253489735d598c5140f3fb75c0a5c8be2e98c" + integrity sha512-Han6cdWAPPFM7C2AigS2Ofl3XjAT0yVMrUixodJEpyg71zCtZ2yzXc3s+suc/OaNt4ca6WJBEzVnEIjxCTwFMw== + dependencies: + lunr ">= 2.3.0 < 2.4.0" + +"lunr@>= 2.3.0 < 2.4.0": + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +marked@2.1.3, marked@^2.0.0, marked@^2.0.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/marked/-/marked-2.1.3.tgz#bd017cef6431724fd4b27e0657f5ceb14bff3753" + integrity sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memoize-one@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + +memoizee@0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.53" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3, merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +mime-db@1.49.0, "mime-db@>= 1.43.0 < 2": + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== + dependencies: + mime-db "1.49.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mini-css-extract-plugin@~1.3.2: + version "1.3.9" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.9.tgz#47a32132b0fd97a119acd530e8421e8f6ab16d5e" + integrity sha512-Ac4s+xhVbqlyhXS5J/Vh/QXUz3ycXlCqoCPpg0vdfhsIBH9eg/It/9L1r1XhSCH737M1lqcWnMuWL13zcygn5A== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + webpack-sources "^1.1.0" + +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +moment@^2.24.0: + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mv@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + +nanoid@^3.1.23: + version "3.1.25" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" + integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== + +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +ncp@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.6.0, neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-tick@1, next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-fetch@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-gyp-build@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" + integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== + +node-releases@^1.1.75: + version "1.1.75" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe" + integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + +npm-run-all@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== + dependencies: + ansi-styles "^3.2.1" + chalk "^2.4.1" + cross-spawn "^6.0.5" + memorystream "^0.3.1" + minimatch "^3.0.4" + pidtree "^0.3.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + +object-is@^1.0.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.entries@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd" + integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.2" + +object.fromentries@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8" + integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + has "^1.0.3" + +object.values@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" + integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.2" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +os@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/os/-/os-0.1.2.tgz#f29a50c62908516ba42652de42f7038600cadbc2" + integrity sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== + +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= + +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +pidtree@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" + integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pino-std-serializers@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" + integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== + +pino@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-6.13.0.tgz#41810b9be213af6f8f7c23a1b17058d880267e7b" + integrity sha512-mRXSTfa34tbfrWqCIp1sUpZLqBhcoaGapoyxfEwaWwJGMpLijlRdDKIQUyvq4M3DUfFH5vEglwSw8POZYwbThA== + dependencies: + fast-redact "^3.0.0" + fast-safe-stringify "^2.0.8" + flatstr "^1.0.12" + pino-std-serializers "^3.1.0" + quick-format-unescaped "^4.0.3" + sonic-boom "^1.0.2" + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkginfo@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" + integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + +popper.js@1.16.1-lts: + version "1.16.1-lts" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05" + integrity sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA== + +popper.js@^1.14.4, popper.js@^1.16.1: + version "1.16.1" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" + integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.6" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^8.0.2, postcss@^8.2.15: + version "8.3.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea" + integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.23" + source-map-js "^0.6.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-bytes@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prettier-bytes/-/prettier-bytes-1.0.4.tgz#994b02aa46f699c50b6257b5faaa7fe2557e62d6" + integrity sha1-mUsCqkb2mcULYle1+qp/4lV+YtY= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.1.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" + integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== + +prettier@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" + integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== + +pretty-ms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.24, psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-format-unescaped@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz#6d6b66b8207aa2b35eef12be1421bb24c428f652" + integrity sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-loader@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dom@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + +react-input-autosize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-3.0.0.tgz#6b5898c790d4478d69420b55441fcc31d5c50a85" + integrity sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg== + dependencies: + prop-types "^15.5.8" + +react-is@^16.7.0, react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +"react-is@^16.8.0 || ^17.0.0": + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-popper@^1.3.7: + version "1.3.11" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.11.tgz#a2cc3f0a67b75b66cfa62d2c409f9dd1fcc71ffd" + integrity sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg== + dependencies: + "@babel/runtime" "^7.1.2" + "@hypnosphi/create-react-context" "^0.3.1" + deep-equal "^1.1.1" + popper.js "^1.14.4" + prop-types "^15.6.1" + typed-styles "^0.0.7" + warning "^4.0.2" + +react-select@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-4.3.1.tgz#389fc07c9bc7cf7d3c377b7a05ea18cd7399cb81" + integrity sha512-HBBd0dYwkF5aZk1zP81Wx5UsLIIT2lSvAY2JiJo199LjoLHoivjn9//KsmvQMEFGNhe58xyuOITjfxKCcGc62Q== + dependencies: + "@babel/runtime" "^7.12.0" + "@emotion/cache" "^11.4.0" + "@emotion/react" "^11.1.1" + memoize-one "^5.0.0" + prop-types "^15.6.0" + react-input-autosize "^3.0.0" + react-transition-group "^4.3.0" + +react-tooltip@^4.2.15: + version "4.2.21" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.21.tgz#840123ed86cf33d50ddde8ec8813b2960bfded7f" + integrity sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig== + dependencies: + prop-types "^15.7.2" + uuid "^7.0.3" + +react-transition-group@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + +react-transition-group@^4.3.0, react-transition-group@^4.4.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" + integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" + integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +regexpp@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +regextras@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.1.tgz#be95719d5f43f9ef0b9fa07ad89b7c606995a3b2" + integrity sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w== + +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +request@2.88.2, request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.10.0, resolve@^1.13.1, resolve@^1.9.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +resolve@^2.0.0-next.3: + version "2.0.0-next.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" + integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@~2.4.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= + dependencies: + glob "^6.0.1" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sanitize-html@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353" + integrity sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^6.0.0" + is-plain-object "^5.0.0" + klona "^2.0.3" + parse-srcset "^1.0.2" + postcss "^8.0.2" + +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.3.5, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.6.1: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +sonic-boom@^1.0.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" + integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== + dependencies: + atomic-sleep "^1.0.0" + flatstr "^1.0.12" + +sort-object-keys@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" + integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== + +sort-package-json@~1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-1.44.0.tgz#470330be868f8a524a4607b26f2a0233e93d8b6d" + integrity sha512-u9GUZvpavUCXV5SbEqXu9FRbsJrYU6WM10r3zA0gymGPufK5X82MblCLh9GW9l46pXKEZvK+FA3eVTqC4oMp4A== + dependencies: + detect-indent "^6.0.0" + detect-newline "3.1.0" + git-hooks-list "1.0.3" + globby "10.0.0" + is-plain-obj "2.1.0" + sort-object-keys "^1.1.3" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + +source-map-support@~0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.10" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" + integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +steno@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb" + integrity sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs= + dependencies: + graceful-fs "^4.1.3" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.matchall@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" + integrity sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.2" + get-intrinsic "^1.1.1" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.3.1" + side-channel "^1.0.4" + +string.prototype.padend@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" + integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +style-loader@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +stylis@^4.0.3: + version "4.0.10" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" + integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +svg-url-loader@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/svg-url-loader/-/svg-url-loader-6.0.0.tgz#b94861d9f6badfb8ca3e7d3ec4655c1bf732ac5d" + integrity sha512-Qr5SCKxyxKcRnvnVrO3iQj9EX/v40UiGEMshgegzV7vpo3yc+HexELOdtWcA3MKjL8IyZZ1zOdcILmDEa/8JJQ== + dependencies: + file-loader "~6.0.0" + loader-utils "~2.0.0" + +symbol-tree@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^6.0.9: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + +tar@^6.0.2: + version "6.1.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.10.tgz#8a320a74475fba54398fa136cd9883aa8ad11175" + integrity sha512-kvvfiVvjGMxeUNB6MyYv5z7vhfFRwbwCXJAeL0/lnbrttBVqcMOnpHUf0X42LrPMR8mMpgapkJMchFH4FSHzNA== + dependencies: + 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" + +terser-webpack-plugin@^4.1.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" + integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== + dependencies: + cacache "^15.0.5" + find-cache-dir "^3.3.1" + jest-worker "^26.5.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.3.4" + webpack-sources "^1.4.3" + +terser-webpack-plugin@^5.1.3: + version "5.1.4" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz#c369cf8a47aa9922bd0d8a94fe3d3da11a7678a1" + integrity sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA== + dependencies: + jest-worker "^27.0.2" + p-limit "^3.1.0" + schema-utils "^3.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + terser "^5.7.0" + +terser@^5.3.4, terser@^5.7.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.2.tgz#d4d95ed4f8bf735cb933e802f2a1829abf545e3f" + integrity sha512-0Omye+RD4X7X69O0eql3lC4Heh/5iLj3ggxR/B5ketZLOtLiOqukUgjw3q4PDnNQbsrkKr3UMypqStQG3XKRvw== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +"through@>=2.2.7 <3", through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timers-ext@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-string-loader@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/to-string-loader/-/to-string-loader-1.1.6.tgz#230529ccc63dd0ecca052a85e1fb82afe946b0ab" + integrity sha512-VNg62//PS1WfNwrK3n7t6wtK5Vdtx/qeYLLEioW46VMlYUwAYT6wnfB+OwS2FMTCalIHu0tk79D3RXX8ttmZTQ== + dependencies: + loader-utils "^1.0.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@^2.3.3, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +tslib@^1.8.1, tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@~1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +typanion@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.3.2.tgz#c31f3b2afb6e8ae74dbd3f96d5b1d8f9745e483e" + integrity sha512-m3v3wtFc6R0wtl0RpEn11bKXIOjS1zch5gmx0zg2G5qfGQ3A9TVZRMSL43O5eFuGXsrgzyvDcGRmSXGP5UqpDQ== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typed-styles@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" + integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== + +typescript@~4.1.3: + version "4.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.6.tgz#1becd85d77567c3c741172339e93ce2e69932138" + integrity sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow== + +typestyle@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/typestyle/-/typestyle-2.1.0.tgz#7c5cc567de72cd8bfb686813150b92791aaa7636" + integrity sha512-6uCYPdG4xWLeEcl9O0GtNFnNGhami+irKiLsXSuvWHC/aTS7wdj49WeikWAKN+xHN3b1hm+9v0svwwgSBhCsNA== + dependencies: + csstype "2.6.9" + free-style "3.1.0" + +uglify-js@^3.1.4: + version "3.14.1" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" + integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unix-crypt-td-js@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" + integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-loader@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-parse@~1.5.1: + version "1.5.3" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" + integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validator@13.6.0: + version "13.6.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" + integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verdaccio-audit@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/verdaccio-audit/-/verdaccio-audit-10.0.0.tgz#d3304d923c7f2c28c173a02425208c941f25217b" + integrity sha512-Epsh+C7ZEdq39PR9QeDBTWktbeqc0zOQjMzWte6Ut5Jh6fPLZzxGF8VK8O67B6mnTwLvGy50A1aPVM97Ysh5Rw== + dependencies: + express "4.17.1" + request "2.88.2" + +verdaccio-htpasswd@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/verdaccio-htpasswd/-/verdaccio-htpasswd-10.0.0.tgz#7a7f44e8ed4db40c53deef0f5101f2a16dce4ff1" + integrity sha512-3TKwiLwl8/fbaTDawHvjSYcsyMmdARg58keP/1plv74x+Jw0sC66HbbRwQ/tPO5mqoG0UwoWW+lkO8h/OiWi9w== + dependencies: + "@verdaccio/file-locking" "^10.0.0" + apache-md5 "1.1.2" + bcryptjs "2.4.3" + http-errors "1.8.0" + unix-crypt-td-js "1.1.4" + +verdaccio@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/verdaccio/-/verdaccio-5.1.3.tgz#a8e9d2c4b18a6e6cf01fa4d1f946ddbb89aa3ddd" + integrity sha512-Ub8w6lXaA+3qNnaB2J4JGrDTavZ9z42Mc4NLn+SpFC03UHFOGyG+tEiALhmFLBC8v/jSqPd1W4uAXi/AXKXsjQ== + dependencies: + "@verdaccio/commons-api" "10.0.0" + "@verdaccio/local-storage" "10.0.6" + "@verdaccio/readme" "10.0.0" + "@verdaccio/streams" "10.0.0" + "@verdaccio/ui-theme" "3.1.0" + JSONStream "1.3.5" + async "3.2.1" + body-parser "1.19.0" + clipanion "3.0.1" + compression "1.7.4" + cookies "0.8.0" + cors "2.8.5" + dayjs "1.10.6" + debug "^4.3.2" + envinfo "7.8.1" + eslint-import-resolver-node "0.3.4" + express "4.17.1" + fast-safe-stringify "^2.0.8" + handlebars "4.7.7" + http-errors "1.8.0" + js-yaml "4.1.0" + jsonwebtoken "8.5.1" + kleur "4.1.4" + lodash "4.17.21" + lru-cache "6.0.0" + lunr-mutable-indexes "2.3.2" + marked "2.1.3" + memoizee "0.4.15" + mime "2.5.2" + minimatch "3.0.4" + mkdirp "1.0.4" + mv "2.1.1" + pino "6.13.0" + pkginfo "0.4.1" + prettier-bytes "^1.0.4" + pretty-ms "^7.0.1" + request "2.88.0" + semver "7.3.5" + validator "13.6.0" + verdaccio-audit "10.0.0" + verdaccio-htpasswd "10.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +w3c-hr-time@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + +warning@^4.0.2, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +watchpack@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" + integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-cli@^4.1.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.8.0.tgz#5fc3c8b9401d3c8a43e2afceacfa8261962338d1" + integrity sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.0.4" + "@webpack-cli/info" "^1.3.0" + "@webpack-cli/serve" "^1.5.2" + colorette "^1.2.1" + commander "^7.0.0" + execa "^5.0.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + v8-compile-cache "^2.2.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.1.2, webpack-merge@^5.7.3: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d" + integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw== + +webpack@^5.41.1: + version "5.51.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.51.1.tgz#41bebf38dccab9a89487b16dbe95c22e147aac57" + integrity sha512-xsn3lwqEKoFvqn4JQggPSRxE4dhsRcysWTqYABAZlmavcoTmwlOb9b1N36Inbt/eIispSkuHa80/FJkDTPos1A== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.50" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.4.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.8.0" + es-module-lexer "^0.7.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.2.0" + webpack-sources "^3.2.0" + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +worker-loader@^3.0.2: + version "3.0.8" + resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37" + integrity sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== + dependencies: + async-limiter "~1.0.0" + +ws@^7.0.0, ws@^7.4.6: + version "7.5.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" + integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xtend@^4.0.2, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y-codemirror@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/y-codemirror/-/y-codemirror-2.1.1.tgz#e841fc3001b719d7fa457dd7a9748205e2874fe9" + integrity sha512-QXHaOkvEJs3pB82dkW1aGfWUd4S1RA1ORtXWtprHClbqBiCOY19VKiojScSTyl8rTaOZ/zblEq+SNH2sd3Umiw== + dependencies: + lib0 "^0.2.41" + +y-leveldb@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/y-leveldb/-/y-leveldb-0.1.0.tgz#8b60c1af020252445875ebc70d52666017bcb038" + integrity sha512-sMuitVrsAUNh+0b66I42nAuW3lCmez171uP4k0ePcTAJ+c+Iw9w4Yq3wwiyrDMFXBEyQSjSF86Inc23wEvWnxw== + dependencies: + level "^6.0.1" + lib0 "^0.2.31" + +y-protocols@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.5.tgz#91d574250060b29fcac8f8eb5e276fbad594245e" + integrity sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A== + dependencies: + lib0 "^0.2.42" + +y-websocket@^1.3.15: + version "1.3.16" + resolved "https://registry.yarnpkg.com/y-websocket/-/y-websocket-1.3.16.tgz#0ec1a141d593933dfbfba2fb9fa9d95dca332c89" + integrity sha512-538dwNOQeZCpMfhh67y40goxHQZKubjoXtfhQieUF2bIQfHVV44bGFeAiYiBHgwOSRdwp7qG4MmDwU0M3U3vng== + dependencies: + lib0 "^0.2.42" + lodash.debounce "^4.0.8" + y-protocols "^1.0.5" + optionalDependencies: + ws "^6.2.1" + y-leveldb "^0.1.0" + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yjs@^13.5.6: + version "13.5.12" + resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.5.12.tgz#7a0cf3119fb368c07243825e989a55de164b3f9c" + integrity sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg== + dependencies: + lib0 "^0.2.41" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 21c1cc272f5e95a5dfd1c80a4c1f97d625fcb9ee Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 13 Sep 2021 11:09:56 +0200 Subject: [PATCH 02/35] Update SwanProjects/README.md Co-authored-by: Enric Tejedor fixed documentation and comments --- SwanProjects/README.md | 4 ++-- SwanProjects/bin/swan_bash | 2 +- SwanProjects/bin/swan_env | 4 ++-- SwanProjects/src/ProjectDialog.tsx | 6 +++--- SwanProjects/src/index.ts | 2 +- SwanProjects/style/base.css | 6 +++--- SwanProjects/swanprojects/handlers.py | 2 +- .../swanprojects/kernelmanager/kernelspecmanager.py | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/SwanProjects/README.md b/SwanProjects/README.md index 3bcf960c..022b88d5 100644 --- a/SwanProjects/README.md +++ b/SwanProjects/README.md @@ -5,9 +5,9 @@ Server and Lab extension that provides: * Create and edit projects * Get project information * Get software stack information - * Customized Kernel Spec Manager to handle kernel metadata. + * A customized Kernel Spec Manager to handle kernel metadata * In the Lab extension: - * React dialogs with a set of components that allows to create and edit projects + * React dialogs to create and edit projects * LabIcons required for the dialogs ## Requirements diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index 0aca7e27..599e08aa 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -26,7 +26,7 @@ if [[ $# -gt 0 ]] ; then PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:$HOME/.local/bin/" \ bash -c "swan_env $PROJECT $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " else - echo "Error: project $PROJECT_PATH doesn't exists" >&2 + echo "Error: project $PROJECT_PATH doesn't exist" >&2 sleep 60 exit 1 fi diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index 5be7a923..c893ba63 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -7,14 +7,14 @@ # CWD: working directory path PARAMETERS=($@) #WARNING: empty options disappear -#Checking if jq is installed +# Check if jq is installed if ! [ -x "$(command -v jq)" ]; then echo 'Error: jq is not installed.' >&2 sleep 60 exit 1 fi -#Checking if we have more that 2 parameters +# Check arguments if [[ $# -lt 2 ]] ; then echo 'Error: project name and commands required.' >&2 echo 'Example: swan_env myproject python --version' >&2 diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 04e420c3..ef5a097c 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -84,12 +84,12 @@ export namespace ProjectDialog { if (dialogResult?.changesSaved && dialogResult?.newOptions) { options = dialogResult.newOptions; if (options.name?.trim() !== '') { - //check is project already exists + //check if project already exists if (create) { const content = await contentRequest( 'SWAN_projects/' + options.name ).catch((): void => { - //not message here, it is not needed, + // No message here, it is not needed, //I am checking if the directory doesn't exist in order //to make valid the creation of the project folder. }); @@ -108,7 +108,7 @@ export namespace ProjectDialog { const content = await contentRequest( 'SWAN_projects/' + options.name ).catch(() => { - //not message here, it is not needed, + // No message here, it is not needed, //I am checking if the directory doesn't exist in order //to make valid the edition of the name of the project folder. }); diff --git a/SwanProjects/src/index.ts b/SwanProjects/src/index.ts index 1b48cbbf..f82cc25c 100644 --- a/SwanProjects/src/index.ts +++ b/SwanProjects/src/index.ts @@ -116,7 +116,7 @@ const extension: JupyterFrontEndPlugin = { }); } - // // Add the command to the palette + // Add the command to the palette if (palette) { palette.addItem({ command: CommandIDs.projectDialog, diff --git a/SwanProjects/style/base.css b/SwanProjects/style/base.css index f49a25ae..5505acf3 100644 --- a/SwanProjects/style/base.css +++ b/SwanProjects/style/base.css @@ -56,9 +56,9 @@ width: 100%; } -/* required to disable "new" notebook and console from the main menu */ -/* with the concept of project, notebook/console can be only created in the launcher, when kernel manager */ -/* have kernels available, otherwise the user will have a kernel error, becuase of kernels are not available*/ +/* Required to disable "new" notebook and console from the main menu. */ +/* Notebooks/consoles can be only created from within the launcher of a project, since kernels are only available */ +/* inside projects, i.e. kernels are provided by the software environment of a project. */ li[data-command='notebook:create-new'] { display: none; } diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 0b79f384..9368db1b 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -251,7 +251,7 @@ def post(self): self.log.info(f"swan_kmspecs return code: {proc.returncode}") if proc.returncode != 0: data = {"status": False, "project_dir": f"SWAN_projects/{name}", - "msg": f"Error editing stack, platform or relase for project {name}, traceback: {output}"} + "msg": f"Error editing stack, platform or release for project {name}, traceback: {output}"} self.finish(json.dumps(data)) return data = {"status": True, "project_dir": f"SWAN_projects/{name}", diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index 12b4390a..f98259bf 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -34,7 +34,7 @@ def __init__(self, **kwargs): def save_native_spec(self, kernel_dir, python_path, display_name): """ This function creates a default kernel with the info from the stack. - It's necessary for CMSSW stacks and those that doesn't have a Python kernel in json file. + It's necessary for CMSSW stacks and those that don't provide a Python kernel as a JSON file. """ self.log.info( f"copying resources from {self.ksmconfig.kernel_resources} to {kernel_dir}") From 80a44db95010352e228f8577bb98ad0d767da755 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 13 Sep 2021 14:37:09 +0200 Subject: [PATCH 03/35] removed SECURITY file, its not really needed --- SwanProjects/MANIFEST.in | 1 - SwanProjects/SECURITY.md | 18 ------------------ 2 files changed, 19 deletions(-) delete mode 100644 SwanProjects/SECURITY.md diff --git a/SwanProjects/MANIFEST.in b/SwanProjects/MANIFEST.in index e474447c..e5bacd41 100644 --- a/SwanProjects/MANIFEST.in +++ b/SwanProjects/MANIFEST.in @@ -1,6 +1,5 @@ include LICENSE include README.md -include SECURITY.md include pyproject.toml recursive-include jupyter-config *.json diff --git a/SwanProjects/SECURITY.md b/SwanProjects/SECURITY.md deleted file mode 100644 index 232a333e..00000000 --- a/SwanProjects/SECURITY.md +++ /dev/null @@ -1,18 +0,0 @@ -# Security Policy - -## Supported Versions - -Use this section to tell people about which versions of your project are -currently being supported with security updates. - -| Version | Supported | -| ------- | ------------------ | -| 0.1.0 | :white_check_mark: | - -## Reporting a Vulnerability - -Use this section to tell people how to report a vulnerability. - -Tell them where to go, how often they can expect to get an update on a -reported vulnerability, what to expect if the vulnerability is accepted or -declined, etc. From 6704f9c8ce30cf5f6c19d190865c7c985b2d5669 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 14 Sep 2021 17:57:54 +0200 Subject: [PATCH 04/35] * improved documentation for swan_env, swan_bash and swan_kmspecs * removed env variable PATH to put it work like user in the isolated environment * added FIXME: comments in the code that will be removed when isolation is not needed anymore * added comment in the sleep, it is required to display the message the message to the user in the terminal. --- SwanProjects/bin/swan_bash | 20 +++- SwanProjects/bin/swan_env | 62 +++++++---- SwanProjects/bin/swan_kmspecs | 198 ++++++++++++++++++++++++++-------- 3 files changed, 208 insertions(+), 72 deletions(-) diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index 599e08aa..8d6c3233 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -1,5 +1,19 @@ #!/bin/bash -i #Author Omar.Zapata@cern.ch 2021 +### +# This script allows to start a bash interpreter inside a project environment. +# This is called from the extension SwanTerminal and it requires like a parameter the project name to +# build the path to the project with $HOME/SWAN_projects/$PROJECT to load the project environment. +# When the bash session is started we modify the prompt indicating in which project the terminal is located. +# Aditionally bash run completed isolated from other possible environments then we pass like parameters to the isolated environment some +# required basic environment variables such as: +# +# * HOME with path to the user home +# * PATH with default paths for the system +# * OAUTH2_TOKEN and OAUTH_INSPECTION_ENDPOINT required by EOS storage +# +# swan_env will load the other variables from the stack, inside the isolated enviroment. +### clear if ! [ -x "$(command -v jq)" ]; then @@ -18,16 +32,18 @@ if [[ $# -gt 0 ]] ; then RELEASE=`jq '.release' $PROJECT_FILE` PLATFORM=`jq '.platform' $PROJECT_FILE` USER_SCRIPT="$PROJECT_PATH/.userscript" + # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary env -i HOME=$HOME OAUTH2_FILE=$OAUTH2_FILE \ OAUTH2_TOKEN=$OAUTH2_TOKEN \ OAUTH_INSPECTION_ENDPOINT=$OAUTH_INSPECTION_ENDPOINT \ PROJECT=$PROJECT \ PROJECT_PATH=$PROJECT_PATH PS1="$PS1" \ - PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:$HOME/.local/bin/" \ bash -c "swan_env $PROJECT $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " else echo "Error: project $PROJECT_PATH doesn't exist" >&2 + # JupyterLab closes the terminal window immediately after the process ends + # this sleep is to allow the user to see the message sleep 60 exit 1 fi -fi \ No newline at end of file +fi diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index c893ba63..27abaf08 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -1,27 +1,46 @@ #!/bin/bash -#DEFINITIONS: -# STACK: LCG/CMSSW -# RELEASE: version of the stack ex (LCG_99) -# PLATFORM: usually os and compiler version -# USER_SCRIPT: customized bash script provided by the user -# CWD: working directory path -PARAMETERS=($@) #WARNING: empty options disappear +#Author Omar.Zapata@cern.ch 2021 +### +# This script allows to execute code inside the project environment. +# +# The parameters are: +# * Project name: Name of the project. +# * Cwd: Current working directory, where the command will be executed. +# * Command: command to execute inside the project environment. +# +# +# With the project name we search the project located at $HOME/SWAN_projects/ +# reading the configuration to load the environment and the bash script provided by the user. +# +# This command is called from SwanKernelSpecManager to start the kernel and swan_bash to start a bash session inside the project environment. +## +PARAMETERS=($@) # Check if jq is installed if ! [ -x "$(command -v jq)" ]; then echo 'Error: jq is not installed.' >&2 + # JupyterLab closes the terminal window immediately after the process ends + # this sleep is to allow the user to see the message sleep 60 exit 1 fi # Check arguments -if [[ $# -lt 2 ]] ; then - echo 'Error: project name and commands required.' >&2 - echo 'Example: swan_env myproject python --version' >&2 +if [[ $# -lt 3 ]] ; then + echo 'Error: project name, cwd and commands required.' >&2 + echo 'Format is: swan_env myproject cwd command command_options' >&2 + echo 'Example: swan_env myproject . python --version (shows version for the python inside the project environment)' >&2 + # JupyterLab closes the terminal window immediately after the process ends + # this sleep is to allow the user to see the message sleep 60 exit 1 fi +### +# In the next block I made the path to the project to read the information from .swanproject file such as +# stack, release and platform. +# I also create the path to use bash user script to source it. +### PROJECT=$1 PROJECT_PATH="$HOME/SWAN_projects/$PROJECT" PROJECT_FILE="$PROJECT_PATH/.swanproject" @@ -30,47 +49,42 @@ RELEASE=`jq -r '.release' $PROJECT_FILE` PLATFORM=`jq -r '.platform' $PROJECT_FILE` USER_SCRIPT="$PROJECT_PATH/.userscript" +# Working directory parameter CWD="$2" - i=0 if [ "$CWD" != "" ]; then i=$((i+1)) fi + for j in $(seq 0 1 $((i)));do unset PARAMETERS[$j] done - +# After project name and working directory the rest of the options passed to this script is the command to be execute inside the environment. COMMAND=${PARAMETERS[@]} if [[ $STACK == "LCG" ]]; then - #example: swan_env SFT LCG_96 x86_64-centos7-gcc8-opt "" $PWD which python CVMFS_PATH="/cvmfs/sft.cern.ch/lcg/views/$RELEASE/$PLATFORM/setup.sh" - if [ "$SWAN_ENV_SILENCE" != "1" ]; then - echo "Loading $RELEASE with plafortm $PLATFORM " - fi + echo "Loading $RELEASE with plafortm $PLATFORM " source $CVMFS_PATH fi if [[ $STACK == "CMSSW" ]]; then - #example: swan_env CMS CMSSW_10_6_19 slc7_amd64_gcc820 ipython kernelspec list source /cvmfs/cms.cern.ch/cmsset_default.sh - if [ "$SWAN_ENV_SILENCE" != "1" ]; then - echo "Loading $RELEASE with plafortm $PLATFORM " - fi + echo "Loading $RELEASE with plafortm $PLATFORM " CMS_BASEDIR=/cvmfs/cms.cern.ch CMSSW=$RELEASE SCRAM=$PLATFORM export PATH=${CMS_BASEDIR}/common:$PATH - #by default I will load the environment in the cvmfs path in read only cd /cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW eval `scramv1 runtime -sh` #requires to prepend the lib and bin paths export LD_LIBRARY_PATH=/cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/lib/:$LD_LIBRARY_PATH export PATH=/cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/bin/:$PATH - fi + +# The next variables allows the user to know in the environment basic information about the project such as stack, name and path. export SWAN_STACK="$RELEASE($PLATFORM)" export SWAN_PROJECT_NAME=$PROJECT export SWAN_PROJECT_PATH=$PROJECT_PATH @@ -78,6 +92,8 @@ export SWAN_PROJECT_PATH=$PROJECT_PATH if [ "$USER_SCRIPT" != "" ] && [ -f "$USER_SCRIPT" ]; then . ${USER_SCRIPT} fi + +# The command is executed inside the $CWD folder. +# if the command produces output it will be there. cd $CWD $COMMAND - diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index 67d8f4ab..4b2aecc0 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -1,4 +1,14 @@ #!/usr/bin/env python +# Copyright (c) SWAN Development Team. +# Author: Omar.Zapata@cern.ch 2021 + +""" +This script allows to find the kernels for python2/3 and kernel spec paths for our kernel spec manager. + +The script run a subprocess inside the project environment trying to find the package ipykernel +and run jupyter_path('kernels') to get the list of available paths for the differents kernels inside the project environment as well. + +""" import argparse import json import os @@ -7,73 +17,127 @@ import subprocess import sys from distutils.spawn import find_executable from shutil import rmtree +try: + from jupyter_core.paths import jupyter_path +except ImportError: + print("Package jupyter_core not found in the environment, kernel paths can not be found.") + sys.exit(1) -def _checkipykernel(project_path,python_interpreter,python_version): - python_code='import ipykernel' - command = [python_interpreter,"-c",python_code] - proc = subprocess.Popen(command, stdout = subprocess.PIPE) +def checkipykernel(python_interpreter): + """ + Checks if ipykernel is available for the given python interpreter, + this function is executed inside the project environment. + + Parameters + ---------- + python_interpreter : str + full path to python interpreter. + + Returns + ------- + int + zero if ipykernel was found. + """ + python_code = 'import ipykernel' + command = [python_interpreter, "-c", python_code] + proc = subprocess.Popen(command, stdout=subprocess.PIPE) proc.wait() data = proc.stdout.read().decode("utf-8") - print(data) proc.communicate() - if proc.returncode !=0 : - print("Error ipykernel not found for {} in project {}".format(python_version,project_path)) return proc.returncode + def check_native_kernel(project_path): - project_file = project_path+os.path.sep+".swanproject" - f = open(project_file,"r+") - project_data = f.read() + """ + Checks if ipykernel is available for python2 and python3. + + This routine is called inside the project environment, + It checks if python2/3 are available to check if the package ipykernel is installed, + if the package was found the results is saved in the .swanproject file. + + Why? kernel.json is not always available in the software stacks but the package ipykern is there, + then we can create the json file locally in the project to put it work, because our kernel spec manager requires + the kernel path with the kernel.json file. + + Parameters + ---------- + project_path : str + path to the project we are trying to find the ipykernel packages. + + """ + project_file = os.path.join(project_path, ".swanproject") + f = open(project_file, "r+") + project_data = f.read() if project_data.strip() == "": project_data = {} else: project_data = json.loads(project_data) - print(project_data) f.seek(0) + # checking if python2 is found python2 = find_executable("python2") if python2 is not None: print("python2 found = "+python2) - project_data["python2"]={"found":True,"path":python2} - rcode = _checkipykernel(project_path,python2,"python2") + project_data["python2"] = {"found": True, "path": python2} + # checking is ipython is found for python2 + rcode = checkipykernel(python2) + if rcode != 0: + print("Error ipykernel not found for python2 in project " + project_path) if rcode == 0: project_data["python2"]["ipykernel"] = True else: project_data["python2"]["ipykernel"] = False else: - project_data["python2"]["found"] = False - + project_data["python2"]["found"] = False + + # checking if python2 is found python3 = find_executable("python3") if python3 is not None: print("python3 found = "+python3) - project_data["python3"]={"found":True,"path":python3} - rcode = _checkipykernel(project_path,python3,"python3") + project_data["python3"] = {"found": True, "path": python3} + # checking is ipython is found for python3 + rcode = checkipykernel(python3) + if rcode != 0: + print("Error ipykernel not found for python3 in project " + project_path) if rcode == 0: project_data["python3"]["ipykernel"] = True else: project_data["python3"]["ipykernel"] = False else: - project_data["python3"]["found"] = False - + project_data["python3"]["found"] = False + + # removing the previuos information of the .swanproject file to put the new one. f.seek(0) f.truncate() - json.dump(project_data,f,indent=4) + # Saving the results in the .swanproject file. + json.dump(project_data, f, indent=4) f.close() + def save_kernel_paths(project_path): - from jupyter_core.paths import jupyter_path - kernels_blacklist_paths = [os.environ["HOME"]+os.sep+'.local/share/jupyter/kernels','/usr/local/share/jupyter/kernels','/usr/share/jupyter/kernels'] + """ + Allows to find kernel paths inside the environment + and save then in the project config file. + + Parameters + ---------- + project_path : str + path to the project we are trying to find the kernel paths. + + """ + kernels_blacklist_paths = [os.path.join( + os.environ["HOME"], '.local/share/jupyter/kernels'), '/usr/local/share/jupyter/kernels', '/usr/share/jupyter/kernels'] tmp_paths = jupyter_path('kernels') paths = [] for path in tmp_paths: - found=False + found = False for bl_path in kernels_blacklist_paths: if bl_path in path: - found=True + found = True if not found: paths.append(path) - project_file = project_path+os.path.sep+".swanproject" - - with open(project_file,"r+") as f: + project_file = os.path.join(project_path, ".swanproject") + + with open(project_file, "r+") as f: project_data = f.read() if project_data == "": project_data = {} @@ -85,44 +149,84 @@ def save_kernel_paths(project_path): project_data["kernel_dirs"] = project_data["kernel_dirs"] + paths else: project_data["kernel_dirs"] = paths - json.dump(project_data,f,indent=4) + json.dump(project_data, f, indent=4) def generate_ksminfo(project_path): + """ + Function to generated all the kernel spec manager info, + calling the function 'check_native_kernel' and to save the kernel paths calling the function save_kernel_paths. + + This function is called inside the project environment. + + Parameters + ---------- + project_path : str + path to the project we are trying to find the information about the kernels.. + + """ check_native_kernel(project_path) save_kernel_paths(project_path) + def swan_kmspecs(project_name): - command = ["env","-i","HOME=%s"%os.environ["HOME"]] - #checking if we are on EOS to add the env variables - #we required this to read/write in a isolate environment with EOS + """ + This functions launches a subprocess to find the information of the kernels inside the environment of the project. + + Parameters + ---------- + project_path : str + path to the project we are trying to find the information about the kernels.. + + """ + command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] + # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary. + # checking if we are on EOS to add the env variables + # we required this to read/write in a isolate environment with EOS if "OAUTH2_FILE" in os.environ: - command.append("OAUTH2_FILE=%s"%os.environ["OAUTH2_FILE"]) + command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) if "OAUTH2_TOKEN" in os.environ: - command.append("OAUTH2_TOKEN=%s"%os.environ["OAUTH2_TOKEN"]) + command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) if "OAUTH_INSPECTION_ENDPOINT" in os.environ: - command.append("OAUTH_INSPECTION_ENDPOINT=%s"%os.environ["OAUTH_INSPECTION_ENDPOINT"]) - - #special case when the package was not installed like root, useful for development - command.append("PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:{}/.local/bin/".format(os.environ["HOME"])) - command += ["/bin/bash","swan_env",project_name, ".","python",__file__,"--generate_ksminfo","--project_name",project_name] - print(" ".join(command)) - proc = subprocess.Popen(command, stdout = subprocess.PIPE) + command.append("OAUTH_INSPECTION_ENDPOINT=%s" % + os.environ["OAUTH_INSPECTION_ENDPOINT"]) + command += ["/bin/bash", "swan_env", project_name, ".", "python", + __file__, "--generate_ksminfo", "--project_name", project_name] + + proc = subprocess.Popen(command, stdout=subprocess.PIPE) proc.wait() data = proc.stdout.read().decode("utf-8") proc.communicate() - if proc.returncode !=0 : - print("Error creating navite kernel for project {}".format(project_name)) + if proc.returncode != 0: + print("Error creating navite kernel and finding kernels paths for project{}".format( + project_name)) return proc.returncode + if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Swan Enviroment Kernel Manager CMD options.') - parser.add_argument('--project_name', type=str,required=True, help='Project name') - parser.add_argument('--generate_ksminfo', action='store_true',default=None, help='Generates Kernel Spec Manager info') - args = parser.parse_args() + """ + entry point to use this script, the parameter project name is required, + the other parameter is used when this script is executed inside the project environment. + + Parameters + ---------- + project_name : str + project name + generate_ksminfo: None + called by the subprocess inside the project environment to generated the kernel information. + + """ + parser = argparse.ArgumentParser( + description='Swan Environment Kernel Manager CMD options.') + parser.add_argument('--project_name', type=str, + required=True, help='Project name') + parser.add_argument('--generate_ksminfo', action='store_true', + default=None, help='Generates Kernel Spec Manager info') + args = parser.parse_args() project_name = args.project_name - project_path = os.environ["HOME"]+"/SWAN_projects/"+project_name + project_path = os.path.join( + os.environ["HOME"], "SWAN_projects", project_name) if args.generate_ksminfo: generate_ksminfo(project_path) else: From edd7fd6dc2cbfc4c881e7c9782a4ef8d2e58fdbb Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Wed, 15 Sep 2021 14:02:05 +0200 Subject: [PATCH 05/35] * moved style from ToolTip component to the css file. * added extra comments about the components * removed unneeded comment in swan_kmspecs command --- SwanProjects/bin/swan_kmspecs | 1 - SwanProjects/src/Components.tsx | 22 +++++++++------------- SwanProjects/style/base.css | 12 ++++++++++++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index 4b2aecc0..b90bad10 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -180,7 +180,6 @@ def swan_kmspecs(project_name): """ command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary. - # checking if we are on EOS to add the env variables # we required this to read/write in a isolate environment with EOS if "OAUTH2_FILE" in os.environ: command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) diff --git a/SwanProjects/src/Components.tsx b/SwanProjects/src/Components.tsx index e2b9ce63..0d7ca406 100644 --- a/SwanProjects/src/Components.tsx +++ b/SwanProjects/src/Components.tsx @@ -1,5 +1,12 @@ // Copyright (c) SWAN Development Team. // Author: Omar.Zapata@cern.ch 2021 + +/** + * Some components needed for the react Dialogs, + * such as ToolTip, to display information about the relase and platform in the project dialog + * and Card, that is based in the code from launcher's Card in JupyetrLab wich is not exported to reuse it. + */ + import { classes, LabIcon } from '@jupyterlab/ui-components'; import * as React from 'react'; import ReactTooltip from 'react-tooltip'; @@ -15,18 +22,6 @@ export function HelpTooltip(props: { }): React.ReactElement { return (
-
? @@ -36,7 +31,6 @@ export function HelpTooltip(props: { multiline={true} getContent={(dataTip): string => `${dataTip}`} /> -
); } @@ -71,6 +65,8 @@ export function Card(props: { }; return (
Date: Thu, 16 Sep 2021 09:40:17 +0200 Subject: [PATCH 06/35] * improved documentation for components file * modified the import for React * fixed comment suggested by Enric in the review. --- SwanProjects/src/Components.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwanProjects/src/Components.tsx b/SwanProjects/src/Components.tsx index 0d7ca406..b4d08825 100644 --- a/SwanProjects/src/Components.tsx +++ b/SwanProjects/src/Components.tsx @@ -8,7 +8,7 @@ */ import { classes, LabIcon } from '@jupyterlab/ui-components'; -import * as React from 'react'; +import React from 'react'; import ReactTooltip from 'react-tooltip'; /** From ee09134258442b1ca50f608331aafa7192a798e9 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 20 Sep 2021 13:29:20 +0200 Subject: [PATCH 07/35] * Improved documentation for ProjectDialog, ProjectWidget and dialog files. * Also fixed css for tooltip --- SwanProjects/src/ProjectDialog.tsx | 14 +++++++++++--- SwanProjects/src/ProjectWidget.tsx | 17 ++++++++++++++++- SwanProjects/src/dialog.tsx | 15 +++++++++++++++ SwanProjects/style/base.css | 26 +++++++++++++++----------- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index ef5a097c..c1cbcd72 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -1,6 +1,15 @@ // Copyright (c) SWAN Team. // Author: Omar Zapata CERN 2021 +/** + * ProjectDialog is a modal dialog that allows to create and edit projects, + * the dialog allows to select the stack, release, platform and write in a textbox + * a bash script to run inside the project. + * + * If the .swanproject file is corrupted for any reason, this dialog will appear + * for the user in order to recover the project with the right information provide by he/she. + */ + import { showErrorMessage } from '@jupyterlab/apputils'; import { showDialog } from './dialog'; import { @@ -40,7 +49,7 @@ export namespace ProjectDialog { * @param create - true for a new project, false to modify. * @param commands - CommandRegistry object * @param theme - colors in the interface 'light' | 'dark'. - * @returns A promise that resolves with whether the dialog was accepted + * @returns A promise that resolves with the dialog results */ // eslint-disable-next-line no-inner-declarations export async function OpenModal( @@ -128,8 +137,7 @@ export namespace ProjectDialog { valid = true; break; } - - //verifying that options where changed, othewise I will not send the request + // verifying that options changed, otherwise I will not send the request if (JSON.stringify(old_options) !== JSON.stringify(options)) { valid = true; } else { diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 5a4bcc4d..2f7ac705 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -1,7 +1,12 @@ // Copyright (c) SWAN Development Team. // Author: Omar.Zapata@cern.ch 2021 -import * as React from 'react'; +/** + * This is the file with the React widget that has the components and callbacks + * to capture the information for the project, such as name, stack, release, platform and bash user script. + */ + +import React from 'react'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; @@ -15,6 +20,16 @@ import { swanProjectIcon, sftIcon, cmsIcon } from './icons'; import { ProjectDialog } from './ProjectDialog'; + +/** + * Functio to create the widget required for the modal dialog, it is basically a form, + * also it has the callbacks to handle the events. + * + * @param options - The dialog setup options. + * @param onSubmit - callback to execute on submit action + * @param onCancel - callback to execute on cancel action. + * @returns the DOM element with the form. + */ export const ProjectWidget: React.FunctionComponent<{ options: ProjectDialog.ISWANOptions; onSubmit: (selectedOptions: ProjectDialog.ISWANOptions) => void; diff --git a/SwanProjects/src/dialog.tsx b/SwanProjects/src/dialog.tsx index e56899a1..3bbd283b 100644 --- a/SwanProjects/src/dialog.tsx +++ b/SwanProjects/src/dialog.tsx @@ -1,6 +1,11 @@ // Copyright (c) SWAN Development Team. // Author: Omar.Zapata@cern.ch 2021 +/** + * File with utility function "showDialog" to display the react widget with mutliple components of the dialog, + * see showDialog documentation for more details. + */ + import React from 'react'; import { ReactWidget } from '@jupyterlab/apputils'; import { Widget } from '@lumino/widgets'; @@ -12,6 +17,16 @@ import { ThemeProvider } from './theme-provider'; import { ProjectWidget } from './ProjectWidget'; import { ProjectDialog } from './ProjectDialog'; + /** + * Utility function to display the ProjectWidget. + * This function allows to create a ReactWidget to embed all the components + * and attach it to the document body. + * + * @param options - The dialog setup options. + * @param theme - colors in the interface 'light' | 'dark'. + * @returns A promise that resolves with whether the dialog was accepted + */ + export async function showDialog( options: ProjectDialog.ISWANOptions & { theme: 'light' | 'dark' } ): Promise<{ diff --git a/SwanProjects/style/base.css b/SwanProjects/style/base.css index 85365df4..be47397a 100644 --- a/SwanProjects/style/base.css +++ b/SwanProjects/style/base.css @@ -4,6 +4,13 @@ | Distributed under the terms of the AGPL License. |----------------------------------------------------------------------------*/ +/** +* css file for ProjectDialog and its components additionally +* required entries to disable "new" notebook and console from the main menu, +* Notebooks/consoles can be only created from within the launcher of a project, since kernels are only available +* inside projects, i.e. kernels are provided by the software environment of a project. +*/ + .sw-Dialog-content { display: flex; flex-direction: column; @@ -56,9 +63,6 @@ width: 100%; } -/* Required to disable "new" notebook and console from the main menu. */ -/* Notebooks/consoles can be only created from within the launcher of a project, since kernels are only available */ -/* inside projects, i.e. kernels are provided by the software environment of a project. */ li[data-command='notebook:create-new'] { display: none; } @@ -109,12 +113,12 @@ li[data-command='terminal:create-new'] { .sw-Component-tooltip { - border-radius: '50%'; - padding: '5px'; - width: '6px'; - height: '6px'; - background-color: '#d5d5d5'; - display: ' flex'; - justify-content: 'center'; - align-items: 'center'; + border-radius: 50%; + padding: 5px; + width: 6px; + height: 6px; + background-color: #d5d5d5; + display: flex; + justify-content: center; + align-items: center; } From c46c6c5d4fd903bed45ebf338b616fd452315197 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 20 Sep 2021 14:05:05 +0200 Subject: [PATCH 08/35] * Added messages for the tooltips in the ProjectWidget --- SwanProjects/src/ProjectWidget.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 2f7ac705..1356450c 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -137,9 +137,7 @@ export const ProjectWidget: React.FunctionComponent<{
@@ -166,9 +164,7 @@ export const ProjectWidget: React.FunctionComponent<{ From fcb1bf8c274a8319f73b6c5c33aa3b7fca1d80c6 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Wed, 13 Oct 2021 14:35:09 +0200 Subject: [PATCH 09/35] added support to dinamically load supported stacks from a path, every stack requires a folder with a config.json file with the releases and platforms, a logo.svg file for the icon in the dialog and a setup.sh with the commands to properly load the stack. --- SwanProjects/MANIFEST.in | 2 +- SwanProjects/bin/swan_bash | 6 +- SwanProjects/bin/swan_env | 37 +- SwanProjects/bin/swan_kmspecs | 11 +- SwanProjects/setup.py | 2 - SwanProjects/src/ProjectDialog.tsx | 6 +- SwanProjects/src/ProjectWidget.tsx | 49 +- SwanProjects/swanprojects/config.py | 26 + SwanProjects/swanprojects/handlers.py | 61 +- .../kernelmanager/kernelspecmanager.py | 21 +- .../swanprojects/stacks/CMSSW/config.json | 19 + .../swanprojects/stacks/CMSSW/logo.svg | 239 ++++ .../swanprojects/stacks/CMSSW/setup.sh | 14 + .../swanprojects/stacks/LCG/config.json | 36 + SwanProjects/swanprojects/stacks/LCG/logo.svg | 1190 +++++++++++++++++ SwanProjects/swanprojects/stacks/LCG/setup.sh | 4 + 16 files changed, 1636 insertions(+), 87 deletions(-) create mode 100644 SwanProjects/swanprojects/config.py create mode 100644 SwanProjects/swanprojects/stacks/CMSSW/config.json create mode 100644 SwanProjects/swanprojects/stacks/CMSSW/logo.svg create mode 100644 SwanProjects/swanprojects/stacks/CMSSW/setup.sh create mode 100644 SwanProjects/swanprojects/stacks/LCG/config.json create mode 100644 SwanProjects/swanprojects/stacks/LCG/logo.svg create mode 100644 SwanProjects/swanprojects/stacks/LCG/setup.sh diff --git a/SwanProjects/MANIFEST.in b/SwanProjects/MANIFEST.in index e5bacd41..0b50b10b 100644 --- a/SwanProjects/MANIFEST.in +++ b/SwanProjects/MANIFEST.in @@ -4,7 +4,7 @@ include pyproject.toml recursive-include jupyter-config *.json include swanprojects/kernelmanager/resources/* -include swanprojects/stacks.json +include swanprojects/stacks/*/* include swanprojects/static/index.html include package.json include install.json diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index 8d6c3233..a7019f02 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -21,11 +21,13 @@ if ! [ -x "$(command -v jq)" ]; then sleep 60 exit 1 fi -if [[ $# -gt 0 ]] ; then +if [[ $# -gt 1 ]] ; then PROJECT=$1 PROJECT_PATH="$HOME/SWAN_projects/$PROJECT" PROJECT_FILE="$PROJECT_PATH/.swanproject" + STACKS_PATH="$2" + if [ -d "$PROJECT_PATH" ] then STACK=`jq '.stack' $PROJECT_FILE` @@ -38,7 +40,7 @@ if [[ $# -gt 0 ]] ; then OAUTH_INSPECTION_ENDPOINT=$OAUTH_INSPECTION_ENDPOINT \ PROJECT=$PROJECT \ PROJECT_PATH=$PROJECT_PATH PS1="$PS1" \ - bash -c "swan_env $PROJECT $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " + bash -c "swan_env $PROJECT $STACKS_PATH $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " else echo "Error: project $PROJECT_PATH doesn't exist" >&2 # JupyterLab closes the terminal window immediately after the process ends diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index 27abaf08..2730d7f3 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -49,11 +49,17 @@ RELEASE=`jq -r '.release' $PROJECT_FILE` PLATFORM=`jq -r '.platform' $PROJECT_FILE` USER_SCRIPT="$PROJECT_PATH/.userscript" +# Path to the stack where the setup.sh for this project is located. +# The available stackas are provided in the extension with --SwanProjects.stacks_path +STACKS_PATH="$2" +STACK_PATH="$STACKS_PATH/$STACK" +STACK_SETUP="$STACK_PATH/setup.sh" + # Working directory parameter -CWD="$2" +CWD="$3" i=0 if [ "$CWD" != "" ]; then - i=$((i+1)) + i=$((i+2)) fi @@ -63,25 +69,18 @@ done # After project name and working directory the rest of the options passed to this script is the command to be execute inside the environment. COMMAND=${PARAMETERS[@]} -if [[ $STACK == "LCG" ]]; then - CVMFS_PATH="/cvmfs/sft.cern.ch/lcg/views/$RELEASE/$PLATFORM/setup.sh" - echo "Loading $RELEASE with plafortm $PLATFORM " - source $CVMFS_PATH +if [ ! -d $STACK_PATH ]; then + echo "Error sourcing the environment, the stack $STACK was not found in stacks path $STACKS_PATH" + echo "project $PROJECT can not be loeaded." + exit 1 fi -if [[ $STACK == "CMSSW" ]]; then - source /cvmfs/cms.cern.ch/cmsset_default.sh - echo "Loading $RELEASE with plafortm $PLATFORM " - CMS_BASEDIR=/cvmfs/cms.cern.ch - CMSSW=$RELEASE - SCRAM=$PLATFORM - - export PATH=${CMS_BASEDIR}/common:$PATH - cd /cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW - eval `scramv1 runtime -sh` - #requires to prepend the lib and bin paths - export LD_LIBRARY_PATH=/cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/lib/:$LD_LIBRARY_PATH - export PATH=/cvmfs/cms.cern.ch/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/bin/:$PATH +if [ -f $STACK_SETUP ]; then + echo "Loading $RELEASE with plafortm $PLATFORM " + . $STACK_SETUP +else + echo "Error loading stack setup.sh on $STACK_SETUP, file doesn't exists." + exit 1 fi # The next variables allows the user to know in the environment basic information about the project such as stack, name and path. diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index b90bad10..5b854b27 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -162,13 +162,12 @@ def generate_ksminfo(project_path): ---------- project_path : str path to the project we are trying to find the information about the kernels.. - """ check_native_kernel(project_path) save_kernel_paths(project_path) -def swan_kmspecs(project_name): +def swan_kmspecs(project_name,stacks_path): """ This functions launches a subprocess to find the information of the kernels inside the environment of the project. @@ -176,6 +175,8 @@ def swan_kmspecs(project_name): ---------- project_path : str path to the project we are trying to find the information about the kernels.. + stacks_path : str + path to the stacks folder with the information aboud the stacks available. """ command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] @@ -188,7 +189,7 @@ def swan_kmspecs(project_name): if "OAUTH_INSPECTION_ENDPOINT" in os.environ: command.append("OAUTH_INSPECTION_ENDPOINT=%s" % os.environ["OAUTH_INSPECTION_ENDPOINT"]) - command += ["/bin/bash", "swan_env", project_name, ".", "python", + command += ["/bin/bash", "swan_env", project_name,stacks_path, ".", "python", __file__, "--generate_ksminfo", "--project_name", project_name] proc = subprocess.Popen(command, stdout=subprocess.PIPE) @@ -219,6 +220,8 @@ if __name__ == '__main__': description='Swan Environment Kernel Manager CMD options.') parser.add_argument('--project_name', type=str, required=True, help='Project name') + parser.add_argument('--stacks_path', type=str, + help='Stacks path') parser.add_argument('--generate_ksminfo', action='store_true', default=None, help='Generates Kernel Spec Manager info') args = parser.parse_args() @@ -229,5 +232,5 @@ if __name__ == '__main__': if args.generate_ksminfo: generate_ksminfo(project_path) else: - rcode = swan_kmspecs(project_name) + rcode = swan_kmspecs(project_name, args.stacks_path) sys.exit(rcode) diff --git a/SwanProjects/setup.py b/SwanProjects/setup.py index 4fd78d7c..1f425bc2 100644 --- a/SwanProjects/setup.py +++ b/SwanProjects/setup.py @@ -10,8 +10,6 @@ HERE = Path(__file__).parent.resolve() -site.ENABLE_USER_SITE = "--user" in sys.argv[1:] - # The name of the project name = "swanprojects" diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index c1cbcd72..85f2169f 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -23,6 +23,10 @@ import { CommandRegistry } from '@lumino/commands'; * Namespace for project dialogs */ export namespace ProjectDialog { + export interface ISWANStackNodeOptions{ + releases:{ [release: string]: Array }, + logo:string + } export interface ISWANOptions { name?: string; stack?: string; @@ -30,7 +34,7 @@ export namespace ProjectDialog { platform?: string; user_script?: string; corrupted?: boolean; - stacks_options?: { [stack: string]: { [release: string]: Array } }; + stacks_options?: { [stack: string]: ISWANStackNodeOptions }; } /** diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 1356450c..6a836ad7 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -16,7 +16,8 @@ import { Card, HelpTooltip } from './Components'; export interface IStackOptions { visible: boolean; } -import { swanProjectIcon, sftIcon, cmsIcon } from './icons'; +import { swanProjectIcon } from './icons'; +import { LabIcon } from '@jupyterlab/ui-components'; import { ProjectDialog } from './ProjectDialog'; @@ -38,25 +39,36 @@ export const ProjectWidget: React.FunctionComponent<{ const options = props.options; const [projectName, setProjectName] = React.useState(options.name || ''); - const availableStacks = Object.keys(options.stacks_options); + const availableStacks = Object.keys(options.stacks_options).filter(function(e) { return e !== 'path' }); const defaultStack = availableStacks.includes(options.stack) ? options.stack : availableStacks[0]; const [stack, setStack] = React.useState(defaultStack); - const availableReleases = Object.keys(options.stacks_options[stack]); + const availableReleases = Object.keys(options.stacks_options[stack]['releases']); const defaultRelease = availableReleases.includes(options.release) ? options.release : availableReleases[0]; const [release, setRelease] = React.useState(defaultRelease); - const availablePlatforms = options.stacks_options[stack][release]; + const availablePlatforms = options.stacks_options[stack]['releases'][release]; const defaultPlatform = availablePlatforms.includes(options.platform) ? options.platform : availablePlatforms[0]; const [platform, setPlatform] = React.useState(defaultPlatform); const [userScript, setUserScript] = React.useState(options.user_script || ''); + var stack_icons:{ [item: string]: LabIcon} = {} + + const randomId = () => { + return Math.random().toString(36).substring(2, 15) + }; + + availableStacks.map(item => { + stack_icons[item] = new LabIcon({ + name: 'jupyterlab_swan_stack:'+randomId(), + svgstr: options.stacks_options[item]['logo'] + })}) const onClickSubmit = () => { props.onSubmit({ @@ -65,7 +77,7 @@ export const ProjectWidget: React.FunctionComponent<{ release, platform, user_script: userScript, - stacks_options: options.stacks_options // TODO remove this + stacks_options: options.stacks_options }); }; @@ -79,16 +91,16 @@ export const ProjectWidget: React.FunctionComponent<{ const onChangeStack = (newStack: string) => { setStack(newStack); - const newRelease = Object.keys(options.stacks_options[newStack])[0]; + const newRelease = Object.keys(options.stacks_options[newStack]['releases'])[0]; setRelease(newRelease); - const newPlatform = options.stacks_options[newStack][newRelease][0]; + const newPlatform = options.stacks_options[newStack]['releases'][newRelease][0]; setPlatform(newPlatform); }; const onChangeRelease = (event: React.ChangeEvent<{ value: unknown }>) => { const newRelease = event.target.value as string; setRelease(newRelease); - const newPlatform = options.stacks_options[stack][newRelease][0]; + const newPlatform = options.stacks_options[stack]['releases'][newRelease][0]; setPlatform(newPlatform); }; @@ -117,18 +129,15 @@ export const ProjectWidget: React.FunctionComponent<{ />
- onChangeStack('LCG')} - isSelected={stack === 'LCG'} - /> - onChangeStack('CMSSW')} - isSelected={stack === 'CMSSW'} - /> + {availableStacks.map(item => ( + onChangeStack(item)} + isSelected={stack === item} + /> + ))}
diff --git a/SwanProjects/swanprojects/config.py b/SwanProjects/swanprojects/config.py new file mode 100644 index 00000000..8594c16b --- /dev/null +++ b/SwanProjects/swanprojects/config.py @@ -0,0 +1,26 @@ + +# Copyright (c) SWAN Development Team. +# Author: Omar.Zapata@cern.ch 2021 + +""" +This file has the configurable class SwanConfig to parse options from command line. +""" +from traitlets import Unicode +from traitlets.config import Configurable +import os + + +class SwanConfig(Configurable): + """ + Class to parse configuration options from command line. + """ + stacks_path = Unicode( + os.path.dirname(os.path.abspath(__file__)) + '/stacks', + config=True, + help="The path to the folder containing stack configuration") + kernel_resources = Unicode( + os.path.dirname(os.path.abspath(__file__)) + + '/kernelmanager/resources', + config=True, + help="The path to the folder containg the resources to add to the kernel" + ) diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 9368db1b..76e64c97 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -4,24 +4,27 @@ import os import shutil import subprocess +from glob import glob import tornado from notebook.base.handlers import APIHandler from notebook.utils import url_path_join from tornado.web import StaticFileHandler -from traitlets import Unicode -from traitlets.config import Configurable from .utils import (get_project_info, get_project_path, get_project_readme, get_user_script_content, get_env_isolated) -class SwanProjects(Configurable): - stacks_path = Unicode( - os.path.dirname(os.path.abspath(__file__)) + '/stacks.json', - config=True, - help="The path to the JSON containing stack configuration") +from swanprojects.config import SwanConfig -class ProjectInfoHandler(APIHandler): + +class SwanAPIHandler(APIHandler): + swan_config = None + + def initialize(self): + self.swan_config = SwanConfig(config=self.config) + + +class ProjectInfoHandler(SwanAPIHandler): @tornado.web.authenticated def post(self): """ @@ -46,23 +49,29 @@ def post(self): payload = {"project_data": project_data} self.finish(json.dumps(payload)) -class StacksInfoHandler(APIHandler): - - swan_projects_config = None - - def initialize(self): - self.swan_projects_config = SwanProjects(config=self.config) +class StacksInfoHandler(SwanAPIHandler): @tornado.web.authenticated def get(self): """ This endpoint is required for the project dialog, it's returning the information saved on stacks.json """ - with open(self.swan_projects_config.stacks_path) as f: - stacks = json.loads(f.read()) + stacks = {} + stacks["path"] = self.swan_config.stacks_path + for stack in glob(os.path.join(self.swan_config.stacks_path, "*")): + stack_name = stack.split(os.sep)[-1] + with open(os.path.join(stack, "config.json")) as f: + stack_info = json.loads(f.read()) + with open(os.path.join(stack, "logo.svg")) as f: + stack_logo = f.read() + stacks[stack_name] = {} + stacks[stack_name]["logo"] = stack_logo + stacks[stack_name]["releases"] = stack_info["releases"] + self.finish(json.dumps({"stacks": stacks})) -class KernelSpecManagerPathHandler(APIHandler): + +class KernelSpecManagerPathHandler(SwanAPIHandler): @tornado.web.authenticated def post(self): """ @@ -84,7 +93,8 @@ def post(self): "msg": f"Error setting SWAN kernel spec manager to path: {path}"} self.finish(json.dumps(data)) -class CreateProjectHandler(APIHandler): + +class CreateProjectHandler(SwanAPIHandler): @tornado.web.authenticated def post(self): """ @@ -101,7 +111,7 @@ def post(self): release = input_data["release"] # CMSSW_X_Y_Z/LCG_XYZ user_script = input_data["user_script"] - project_dir = os.environ["HOME"] + "/SWAN_projects/" + name + project_dir = os.path.join(os.environ["HOME"], "SWAN_projects", name) try: os.makedirs(project_dir) except Exception as msg: @@ -109,7 +119,7 @@ def post(self): "msg": f"Error creating folder for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return - swan_project_file = project_dir + os.path.sep + '.swanproject' + swan_project_file = os.path.join(project_dir, '.swanproject') swan_project_content = {'stack': stack, 'release': release, 'platform': platform} try: @@ -135,7 +145,8 @@ def post(self): return command = get_env_isolated() - command += ["/bin/bash", "-c", "swan_kmspecs --project_name %s" % name] + command += ["/bin/bash", "-c", + f"swan_kmspecs --project_name {name} --stacks_path {self.swan_config.stacks_path}"] self.log.info(f"running {command} ") proc = subprocess.Popen(command, stdout=subprocess.PIPE) proc.wait() @@ -153,7 +164,8 @@ def post(self): "msg": f"created project {name}"} self.finish(json.dumps(data)) -class EditProjectHandler(APIHandler): + +class EditProjectHandler(SwanAPIHandler): @tornado.web.authenticated def post(self): @@ -241,7 +253,8 @@ def post(self): indent=4, sort_keys=True)) f.close() command = get_env_isolated() - command += ["swan_kmspecs", "--project_name", name] + command += ["swan_kmspecs", "--project_name", name, + "--stacks_path", self.swan_config.stacks_path] self.log.info(f"running {command} ") proc = subprocess.Popen(command, stdout=subprocess.PIPE) proc.wait() @@ -259,6 +272,8 @@ def post(self): self.finish(json.dumps(data)) # URL to handler mappings + + def setup_handlers(web_app, url_path): host_pattern = ".*$" base_url = web_app.settings["base_url"] diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index f98259bf..4bbe724e 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -8,17 +8,8 @@ from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel from swanprojects.utils import (get_project_info, get_project_name, get_project_path, get_env_isolated) +from swanprojects.config import SwanConfig from traitlets import Unicode -from traitlets.config import Configurable - - -class SwanKSMConfig(Configurable): - kernel_resources = Unicode( - os.path.dirname(os.path.abspath(__file__)) + '/resources', - config=True, - help="The path to the folder containg the resources to add to the kernel" - ) - class SwanKernelSpecManager(KernelSpecManager): path = Unicode("", config=True, allow_none=True, @@ -29,7 +20,7 @@ def __init__(self, **kwargs): self.log.info("JupyterLab swankernelspecmanager is activated!") self.project = None self.kernel_dirs = [] - self.ksmconfig = SwanKSMConfig(config=self.config) + self.swan_config = SwanConfig(config=self.config) def save_native_spec(self, kernel_dir, python_path, display_name): """ @@ -37,8 +28,8 @@ def save_native_spec(self, kernel_dir, python_path, display_name): It's necessary for CMSSW stacks and those that don't provide a Python kernel as a JSON file. """ self.log.info( - f"copying resources from {self.ksmconfig.kernel_resources} to {kernel_dir}") - shutil.copytree(self.ksmconfig.kernel_resources, kernel_dir) + f"copying resources from {self.swan_config.kernel_resources} to {kernel_dir}") + shutil.copytree(self.swan_config.kernel_resources, kernel_dir) spec = {"argv": [python_path, "-m", "ipykernel_launcher", @@ -85,8 +76,8 @@ def set_path(self, path): def wrap_kernel_specs(self, project_name, kspec): argv = get_env_isolated() - argv += ["/bin/bash", "-c", "swan_env {} {} ".format( - project_name, ".") + "'" + " ".join(kspec.argv) + "'" + argv += ["/bin/bash", "-c", "swan_env {} {} {} ".format( + project_name, self.swan_config.stacks_path, ".") + "'" + " ".join(kspec.argv) + "'" ] kspec.argv = argv diff --git a/SwanProjects/swanprojects/stacks/CMSSW/config.json b/SwanProjects/swanprojects/stacks/CMSSW/config.json new file mode 100644 index 00000000..86d3e5b9 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/CMSSW/config.json @@ -0,0 +1,19 @@ +{ + "releases":{ + "CMSSW_11_1_1": [ + "slc7_amd64_gcc820" + ], + "CMSSW_11_1_2": [ + "slc7_amd64_gcc820" + ], + "CMSSW_11_1_3": [ + "slc7_amd64_gcc820" + ], + "CMSSW_11_1_4": [ + "slc7_amd64_gcc820" + ], + "CMSSW_11_1_5": [ + "slc7_amd64_gcc820" + ] + } +} diff --git a/SwanProjects/swanprojects/stacks/CMSSW/logo.svg b/SwanProjects/swanprojects/stacks/CMSSW/logo.svg new file mode 100644 index 00000000..1e40f126 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/CMSSW/logo.svg @@ -0,0 +1,239 @@ + + + + diff --git a/SwanProjects/swanprojects/stacks/CMSSW/setup.sh b/SwanProjects/swanprojects/stacks/CMSSW/setup.sh new file mode 100644 index 00000000..8ba8c974 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/CMSSW/setup.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# This script allows to source CMSSW stack for a given release and platform. +# variables $RELEASE and $PLATFORM have to be exported before source this script by swan_env +CMS_BASEDIR=/cvmfs/cms.cern.ch +source $CMS_BASEDIR/cmsset_default.sh +CMSSW=$RELEASE +SCRAM=$PLATFORM + +export PATH=${CMS_BASEDIR}/common:$PATH +cd $CMS_BASEDIR/$SCRAM/cms/cmssw/$CMSSW +eval `scramv1 runtime -sh` +#requires to prepend the lib and bin paths +export LD_LIBRARY_PATH=$CMS_BASEDIR/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/lib/:$LD_LIBRARY_PATH +export PATH=$CMS_BASEDIR/$SCRAM/cms/cmssw/$CMSSW/external/$SCRAM/bin/:$PATH diff --git a/SwanProjects/swanprojects/stacks/LCG/config.json b/SwanProjects/swanprojects/stacks/LCG/config.json new file mode 100644 index 00000000..dadc3bb8 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/LCG/config.json @@ -0,0 +1,36 @@ +{ + "releases":{ + "LCG_100": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_99": [ + "x86_64-centos7-gcc8-opt", + "x86_64-centos7-gcc10-opt", + "x86_64-ubuntu2004-gcc9-opt" + ], + "LCG_99python2": [ + "x86_64-centos7-gcc8-opt", + "x86_64-centos7-gcc9-opt" + ], + "LCG_97a": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_97apython3": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_96": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_96python3": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_95a": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_95apython3": [ + "x86_64-centos7-gcc8-opt" + ], + "LCG_95apython3_nxcals": [ + "x86_64-centos7-gcc7-opt" + ]} +} diff --git a/SwanProjects/swanprojects/stacks/LCG/logo.svg b/SwanProjects/swanprojects/stacks/LCG/logo.svg new file mode 100644 index 00000000..c2721aa2 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/LCG/logo.svg @@ -0,0 +1,1190 @@ + + + + diff --git a/SwanProjects/swanprojects/stacks/LCG/setup.sh b/SwanProjects/swanprojects/stacks/LCG/setup.sh new file mode 100644 index 00000000..552cf652 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/LCG/setup.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# This script allows to source LCG stack for a given release and platform. +# variables $RELEASE and $PLATFORM have to be exported before source this script by swan_env +source /cvmfs/sft.cern.ch/lcg/views/$RELEASE/$PLATFORM/setup.sh \ No newline at end of file From 7c23c4f84b0cf13951c4954a79d8dc477beeb13f Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 14 Oct 2021 13:22:06 +0200 Subject: [PATCH 10/35] - removed hardcode code from handler - added more options to SwanConfig class to remove the hardcode code - fixed bug for editing project on ProjectWidget --- SwanProjects/src/ProjectWidget.tsx | 3 +- SwanProjects/swanprojects/config.py | 16 +++ SwanProjects/swanprojects/handlers.py | 148 +++++++++++++------------- 3 files changed, 94 insertions(+), 73 deletions(-) diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 6a836ad7..4bdcd8e6 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -77,7 +77,8 @@ export const ProjectWidget: React.FunctionComponent<{ release, platform, user_script: userScript, - stacks_options: options.stacks_options + stacks_options: options.stacks_options, + corrupted: options.corrupted }); }; diff --git a/SwanProjects/swanprojects/config.py b/SwanProjects/swanprojects/config.py index 8594c16b..3d103926 100644 --- a/SwanProjects/swanprojects/config.py +++ b/SwanProjects/swanprojects/config.py @@ -24,3 +24,19 @@ class SwanConfig(Configurable): config=True, help="The path to the folder containg the resources to add to the kernel" ) + project_file_name = Unicode( + '.swanproject', + config=False, + help="Project file name.") + userscript_file_name = Unicode( + '.userscript', + config=False, + help="User script file name.") + projects_folder_name = Unicode( + "SWAN_projects", + config=False, + help="Projects folder name.") + projects_folder_path = Unicode( + os.path.join(os.environ["HOME"],projects_folder_name.default_value), + config=False, + help="Projects full path for SWAN_projects.") diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 76e64c97..72336e51 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -21,8 +21,37 @@ class SwanAPIHandler(APIHandler): swan_config = None def initialize(self): + """ + Initialization of the handler with the swan configuration object. + """ self.swan_config = SwanConfig(config=self.config) + def subprocess(self, command): + """ + Method to call a sub process in a isolated environment + + Parameters + ---------- + command : list + commands to execute in the isolated environment. + + Returns + ------- + Popen + object to get information from the executed process such as output and return code. + """ + command = get_env_isolated() + command + self.log.info(f"running {command} ") + proc = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc.wait() + stdout = proc.stdout.read().decode("utf-8") + stderr = proc.stderr.read().decode("utf-8") + proc.communicate() + self.log.info(f"result stdout: {stdout} ") + self.log.info(f"result stderr: {stderr} ") + return (stderr,proc.returncode) + class ProjectInfoHandler(SwanAPIHandler): @tornado.web.authenticated @@ -75,8 +104,8 @@ class KernelSpecManagerPathHandler(SwanAPIHandler): @tornado.web.authenticated def post(self): """ - This endpoint is required for the project kernel spec manager, it's it is setting the path to - check if we are inside a project to change the kernel spec manager path + This endpoint is required for the project kernel spec manager, it's setting the path to + check if we are inside a project to change the kernel spec manager path. """ input_data = self.get_json_body() path = input_data["path"] @@ -110,16 +139,18 @@ def post(self): platform = input_data["platform"] # SCRAM/x86_64..centos7..gccX release = input_data["release"] # CMSSW_X_Y_Z/LCG_XYZ user_script = input_data["user_script"] - - project_dir = os.path.join(os.environ["HOME"], "SWAN_projects", name) + project_dir = os.path.join(self.swan_config.projects_folder_path, name) + project_relative_dir = os.path.join( + self.swan_config.projects_folder_name, name) try: os.makedirs(project_dir) except Exception as msg: - data = {"status": False, "project_dir": f"SWAN_projects/{name}", + data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating folder for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return - swan_project_file = os.path.join(project_dir, '.swanproject') + swan_project_file = os.path.join( + project_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} try: @@ -128,39 +159,33 @@ def post(self): indent=4, sort_keys=True)) f.close() except Exception as msg: - data = {"status": False, "project_dir": f"SWAN_projects/{name}", - "msg": f"Error creating .swanproject file for project {name}, traceback: {msg}"} + data = {"status": False, "project_dir": project_relative_dir, + "msg": f"Error creating {self.swan_config.project_file_name} file for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return try: - swan_user_script_file = project_dir + os.path.sep + '.userscript' + swan_user_script_file = os.path.join( + project_dir, self.swan_config.userscript_file_name) with open(swan_user_script_file, 'w') as f: f.write(user_script) f.close() except Exception as msg: - data = {"status": False, "project_dir": f"SWAN_projects/{name}", - "msg": f"Error creating .userscript file for project {name}, traceback: {msg}"} + data = {"status": False, "project_dir": project_relative_dir, + "msg": f"Error creating {self.swan_config.userscript_file_name} file for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return - - command = get_env_isolated() - command += ["/bin/bash", "-c", + command = ["/bin/bash", "-c", f"swan_kmspecs --project_name {name} --stacks_path {self.swan_config.stacks_path}"] - self.log.info(f"running {command} ") - proc = subprocess.Popen(command, stdout=subprocess.PIPE) - proc.wait() - output = proc.stdout.read().decode("utf-8") - self.log.info(f"swan_kmspecs output: {output}") - proc.communicate() - self.log.info(f"swan_kmspecs return code: {proc.returncode}") - if proc.returncode != 0: - data = {"status": False, "project_dir": f"SWAN_projects/{name}", - "msg": f"Error collecting the information from cvmfs for project {name}, traceback: {output}"} + stderr, returncode = self.subprocess(command) + self.log.info(f"swan_kmspecs return code: {returncode}") + if returncode != 0: + data = {"status": False, "project_dir": project_relative_dir, + "msg": f"Error collecting the information from cvmfs for project {name}, traceback: {stderr}"} self.finish(json.dumps(data)) return - data = {"status": True, "project_dir": f"SWAN_projects/{name}", + data = {"status": True, "project_dir": project_relative_dir, "msg": f"created project {name}"} self.finish(json.dumps(data)) @@ -180,29 +205,12 @@ def post(self): input_data = self.get_json_body() print(f"EditProjectHandler = {input_data}") - corrupted = False - if "corrupted" in input_data.keys(): - corrupted = input_data["corrupted"] - - old_name = "" - if "old_name" in input_data.keys(): - old_name = input_data["old_name"] - - old_stack = "" - if "old_stack" in input_data.keys(): - old_stack = input_data["old_stack"] - - old_platform = "" - if "old_platform" in input_data.keys(): - old_platform = input_data["old_platform"] - - old_release = "" - if "old_release" in input_data.keys(): - old_release = input_data["old_release"] - - old_userscript = "" - if "old_userscript" in input_data.keys(): - old_userscript = input_data["old_userscript"] + corrupted = input_data.get("corrupted") + old_name = input_data.get("old_name") + old_stack = input_data.get("old_stack") + old_platform = input_data.get("old_platform") + old_release = input_data.get("old_release") + old_userscript = input_data.get("old_userscript") name = input_data["name"] stack = input_data["stack"] @@ -210,33 +218,37 @@ def post(self): release = input_data["release"] user_script = input_data["user_script"] - project_dir = os.environ["HOME"] + "/SWAN_projects/" + name + project_dir = os.path.join(self.swan_config.projects_folder_path, name) + project_relative_dir = os.path.join( + self.swan_config.projects_folder_name, name) if old_name != name: try: - old_project_dir = os.environ["HOME"] + \ - "/SWAN_projects/" + old_name + old_project_dir = os.path.join( + self.swan_config.projects_folder_path, old_name) os.rename(old_project_dir, project_dir) except Exception as msg: - data = {"status": False, "project_dir": f"SWAN_projects/{old_name}", + data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error editing project folder {old_name}, traceback: {msg}"} # this will stop the execution here, it's the same for the next exceptions. self.finish(json.dumps(data)) return if old_userscript != user_script: - userscript_file = project_dir + os.path.sep + '.userscript' + swan_userscript_file = os.path.join( + project_dir, self.swan_config.userscript_file_name) try: - with open(userscript_file, 'w') as f: + with open(swan_userscript_file, 'w') as f: f.write(user_script) f.close() except Exception as msg: - data = {"status": False, "project_dir": f"SWAN_projects/{name}", - "msg": f"Error editing .userscript for project {name}, traceback: {msg}"} + data = {"status": False, "project_dir": project_relative_dir, + "msg": f"Error editing {self.swan_config.userscript_file_name} for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return if stack != old_stack or platform != old_platform or release != old_release or corrupted: - swan_project_file = project_dir + os.path.sep + '.swanproject' + swan_project_file = os.path.join( + project_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} kernel_dir = project_dir + "/.local/share/jupyter/kernels" @@ -252,28 +264,20 @@ def post(self): f.write(json.dumps(swan_project_content, indent=4, sort_keys=True)) f.close() - command = get_env_isolated() - command += ["swan_kmspecs", "--project_name", name, + command = ["swan_kmspecs", "--project_name", name, "--stacks_path", self.swan_config.stacks_path] - self.log.info(f"running {command} ") - proc = subprocess.Popen(command, stdout=subprocess.PIPE) - proc.wait() - output = proc.stdout.read().decode("utf-8") - self.log.info(f"result {output} ") - proc.communicate() - self.log.info(f"swan_kmspecs return code: {proc.returncode}") - if proc.returncode != 0: - data = {"status": False, "project_dir": f"SWAN_projects/{name}", - "msg": f"Error editing stack, platform or release for project {name}, traceback: {output}"} + stderr, returncode = self.subprocess(command) + self.log.info(f"swan_kmspecs return code: {returncode}") + if returncode != 0: + data = {"status": False, "project_dir": project_relative_dir, + "msg": f"Error editing stack, platform or release for project {name}, traceback: {stderr}"} self.finish(json.dumps(data)) return - data = {"status": True, "project_dir": f"SWAN_projects/{name}", + data = {"status": True, "project_dir": project_relative_dir, "msg": f"edited project {name}"} self.finish(json.dumps(data)) # URL to handler mappings - - def setup_handlers(web_app, url_path): host_pattern = ".*$" base_url = web_app.settings["base_url"] From 5a477696939fc7dcfdd224fc4220c0bfdf1bdc1a Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 18 Oct 2021 17:07:21 +0200 Subject: [PATCH 11/35] - updated readme - changed endpoint edit project by PUT - changed endpoint project info by GET - implemented contents manager support in the handlers --- SwanProjects/README.md | 5 +- SwanProjects/src/request.ts | 4 +- SwanProjects/swanprojects/config.py | 2 +- SwanProjects/swanprojects/handlers.py | 72 ++++++++++++--------------- SwanProjects/swanprojects/utils.py | 4 +- 5 files changed, 40 insertions(+), 47 deletions(-) diff --git a/SwanProjects/README.md b/SwanProjects/README.md index 022b88d5..97c11d74 100644 --- a/SwanProjects/README.md +++ b/SwanProjects/README.md @@ -25,11 +25,10 @@ pip install swanprojects To replace the default Jupyter Contents Manager and Kernel Spec Manager in the JupyterLab Notebook configuration (i.e in `jupyter_notebook_config.py`), set the following: ```python -c.NotebookApp.default_url = 'lab' c.NotebookApp.contents_manager_class = 'swancontents.filemanager.swanfilemanager.SwanFileManager' c.NotebookApp.kernel_spec_manager_class = 'swanprojects.kernelmanager.kernelspecmanager.SwanKernelSpecManager' c.KernelSpecManager.ensure_native_kernel = False -c.SwanProjects.stacks_path=path_to_stacks.json -c.SwanKSMConfig.kernel_resources=path_to_native_kernel_resources +c.SwanConfig.stacks_path=path_to_stacks_folder +c.SwanConfig.kernel_resources=path_to_native_kernel_resources ``` diff --git a/SwanProjects/src/request.ts b/SwanProjects/src/request.ts index 468cb771..2d350477 100644 --- a/SwanProjects/src/request.ts +++ b/SwanProjects/src/request.ts @@ -106,10 +106,10 @@ export function editProjectRequest( try { return request('swan/project/edit', { body: JSON.stringify(dataToSend), - method: 'POST' + method: 'PUT' }); } catch (reason) { - const msg = `Error on POST swan/project/edit ${options}.\n${reason}`; + const msg = `Error on PUT swan/project/edit ${options}.\n${reason}`; return { status: 'error', reason: reason, param: options, msg: msg }; } } diff --git a/SwanProjects/swanprojects/config.py b/SwanProjects/swanprojects/config.py index 3d103926..a8d2d93e 100644 --- a/SwanProjects/swanprojects/config.py +++ b/SwanProjects/swanprojects/config.py @@ -37,6 +37,6 @@ class SwanConfig(Configurable): config=False, help="Projects folder name.") projects_folder_path = Unicode( - os.path.join(os.environ["HOME"],projects_folder_name.default_value), + os.path.join(os.environ["HOME"], projects_folder_name.default_value), config=False, help="Projects full path for SWAN_projects.") diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 72336e51..33578e9a 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -50,20 +50,20 @@ def subprocess(self, command): proc.communicate() self.log.info(f"result stdout: {stdout} ") self.log.info(f"result stderr: {stderr} ") - return (stderr,proc.returncode) + return (stderr, proc.returncode) class ProjectInfoHandler(SwanAPIHandler): @tornado.web.authenticated - def post(self): + def get(self): """ - Post request for the SwanLauncher/SwanFileBrowser, + Get request for the SwanLauncher/SwanFileBrowser, this endpoint returns project information such as stack, release, platform etc.. if the path is not inside the project return and empty project data. """ - input_data = self.get_json_body() - self.log.info(f"ProjectInfoHandler = {input_data}") - path = input_data["path"] + path = self.get_argument('path') + caller = self.get_argument('caller') + self.log.info(f"ProjectInfoHandler caller = {caller} path = {path}") project = get_project_path(path) project_data = {} @@ -139,25 +139,22 @@ def post(self): platform = input_data["platform"] # SCRAM/x86_64..centos7..gccX release = input_data["release"] # CMSSW_X_Y_Z/LCG_XYZ user_script = input_data["user_script"] - project_dir = os.path.join(self.swan_config.projects_folder_path, name) project_relative_dir = os.path.join( self.swan_config.projects_folder_name, name) try: - os.makedirs(project_dir) + self.contents_manager.new({'type':'directory'},project_relative_dir) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating folder for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return swan_project_file = os.path.join( - project_dir, self.swan_config.project_file_name) + project_relative_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} try: - with open(swan_project_file, 'w+') as f: - f.write(json.dumps(swan_project_content, - indent=4, sort_keys=True)) - f.close() + self.contents_manager.new({'type':'file','content':json.dumps(swan_project_content, + indent=4, sort_keys=True),'format':'text'},swan_project_file) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating {self.swan_config.project_file_name} file for project {name}, traceback: {msg}"} @@ -166,17 +163,15 @@ def post(self): try: swan_user_script_file = os.path.join( - project_dir, self.swan_config.userscript_file_name) - with open(swan_user_script_file, 'w') as f: - f.write(user_script) - f.close() + project_relative_dir, self.swan_config.userscript_file_name) + self.contents_manager.new({'type':'file','content':user_script,'format':'text'},swan_user_script_file) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating {self.swan_config.userscript_file_name} file for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return command = ["/bin/bash", "-c", - f"swan_kmspecs --project_name {name} --stacks_path {self.swan_config.stacks_path}"] + f"swan_kmspecs --project_name {name} --stacks_path {self.swan_config.stacks_path}"] stderr, returncode = self.subprocess(command) self.log.info(f"swan_kmspecs return code: {returncode}") if returncode != 0: @@ -193,7 +188,7 @@ def post(self): class EditProjectHandler(SwanAPIHandler): @tornado.web.authenticated - def post(self): + def put(self): """ This endpoint allows to edit project information, such as name, stack, platform etc.. The project can be renamed from $HOME/SWAN_projects/old_name to $HOME/SWAN_projects/name @@ -203,7 +198,7 @@ def post(self): the edit project dialog will send the information again to this endpoint to fix the project information. """ input_data = self.get_json_body() - print(f"EditProjectHandler = {input_data}") + self.log.info(f"EditProjectHandler = {input_data}") corrupted = input_data.get("corrupted") old_name = input_data.get("old_name") @@ -218,14 +213,13 @@ def post(self): release = input_data["release"] user_script = input_data["user_script"] - project_dir = os.path.join(self.swan_config.projects_folder_path, name) project_relative_dir = os.path.join( self.swan_config.projects_folder_name, name) if old_name != name: try: old_project_dir = os.path.join( - self.swan_config.projects_folder_path, old_name) - os.rename(old_project_dir, project_dir) + self.swan_config.projects_folder_name, old_name) + self.contents_manager.rename(old_project_dir, project_relative_dir) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error editing project folder {old_name}, traceback: {msg}"} @@ -234,12 +228,10 @@ def post(self): return if old_userscript != user_script: - swan_userscript_file = os.path.join( - project_dir, self.swan_config.userscript_file_name) try: - with open(swan_userscript_file, 'w') as f: - f.write(user_script) - f.close() + swan_user_script_file = os.path.join( + project_relative_dir, self.swan_config.userscript_file_name) + self.contents_manager.new({'type':'file','content':user_script,'format':'text'},swan_user_script_file) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error editing {self.swan_config.userscript_file_name} for project {name}, traceback: {msg}"} @@ -248,24 +240,24 @@ def post(self): if stack != old_stack or platform != old_platform or release != old_release or corrupted: swan_project_file = os.path.join( - project_dir, self.swan_config.project_file_name) + project_relative_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} - kernel_dir = project_dir + "/.local/share/jupyter/kernels" + kernel_dir = project_relative_dir + "/.local/share/jupyter/kernels" + kernel_dir_python2 = os.path.join(kernel_dir,'python2') + kernel_dir_python3 = os.path.join(kernel_dir,'python3') # removing old native kernels for python only(this is generated by us) - if os.path.exists(kernel_dir + "/python2"): - shutil.rmtree(kernel_dir + "/python2") + if os.path.exists(kernel_dir_python2): + shutil.rmtree(kernel_dir_python2) - if os.path.exists(kernel_dir + "/python3"): - shutil.rmtree(kernel_dir + "/python3") + if os.path.exists(kernel_dir_python3): + shutil.rmtree(kernel_dir_python3) - with open(swan_project_file, 'w+') as f: - f.write(json.dumps(swan_project_content, - indent=4, sort_keys=True)) - f.close() + self.contents_manager.new({'type':'file','content':json.dumps(swan_project_content, + indent=4, sort_keys=True),'format':'text'},swan_project_file) command = ["swan_kmspecs", "--project_name", name, - "--stacks_path", self.swan_config.stacks_path] + "--stacks_path", self.swan_config.stacks_path] stderr, returncode = self.subprocess(command) self.log.info(f"swan_kmspecs return code: {returncode}") if returncode != 0: @@ -278,6 +270,8 @@ def post(self): self.finish(json.dumps(data)) # URL to handler mappings + + def setup_handlers(web_app, url_path): host_pattern = ".*$" base_url = web_app.settings["base_url"] diff --git a/SwanProjects/swanprojects/utils.py b/SwanProjects/swanprojects/utils.py index 8590ca81..0308d129 100644 --- a/SwanProjects/swanprojects/utils.py +++ b/SwanProjects/swanprojects/utils.py @@ -25,7 +25,7 @@ def get_project_info(path): def get_project_path(cwd): - if cwd.startswith('/'): + if cwd.startswith(os.sep): cwd = cwd[1:] paths = cwd.split(os.path.sep) @@ -63,7 +63,7 @@ def get_project_name(project_path): path = get_project_path(project_path) name = None if path is not None: - name = path.split('/')[-1] + name = path.split(os.sep)[-1] return name From 2a3462850f82521f52510677b8fe3c548aad2cbd Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 21 Oct 2021 14:03:48 +0200 Subject: [PATCH 12/35] -> created a class SwanUtils to encapsulate mutiples functions that requires the contents manages -> Impleted contents manager in the handlers, kernel spec manager and in the SwanUtils class -> Implented documentation in the new class SwanUtils --- SwanProjects/swanprojects/config.py | 22 +- SwanProjects/swanprojects/handlers.py | 64 ++-- .../kernelmanager/kernelspecmanager.py | 33 ++- SwanProjects/swanprojects/utils.py | 274 +++++++++++------- 4 files changed, 242 insertions(+), 151 deletions(-) diff --git a/SwanProjects/swanprojects/config.py b/SwanProjects/swanprojects/config.py index a8d2d93e..7b7ba042 100644 --- a/SwanProjects/swanprojects/config.py +++ b/SwanProjects/swanprojects/config.py @@ -17,26 +17,30 @@ class SwanConfig(Configurable): stacks_path = Unicode( os.path.dirname(os.path.abspath(__file__)) + '/stacks', config=True, - help="The path to the folder containing stack configuration") + help='The path to the folder containing stack configuration') kernel_resources = Unicode( - os.path.dirname(os.path.abspath(__file__)) + - '/kernelmanager/resources', + os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'kernelmanager/resources'), config=True, - help="The path to the folder containg the resources to add to the kernel" + help='The path to the folder containg the resources to add to the kernel' ) project_file_name = Unicode( '.swanproject', config=False, - help="Project file name.") + help='Project file name.') userscript_file_name = Unicode( '.userscript', config=False, - help="User script file name.") + help='User script file name.') projects_folder_name = Unicode( - "SWAN_projects", + 'SWAN_projects', config=False, - help="Projects folder name.") + help='Projects folder name.') projects_folder_path = Unicode( os.path.join(os.environ["HOME"], projects_folder_name.default_value), config=False, - help="Projects full path for SWAN_projects.") + help='Projects full path for SWAN_projects.') + kernel_folder_path = Unicode( + '.local/share/jupyter/kernels', + config=False, + help='Path for native kernels generated by the us inside the project') diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 33578e9a..476e0110 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -2,7 +2,6 @@ # Author: Omar.Zapata@cern.ch 2021 import json import os -import shutil import subprocess from glob import glob @@ -11,20 +10,24 @@ from notebook.utils import url_path_join from tornado.web import StaticFileHandler -from .utils import (get_project_info, get_project_path, get_project_readme, - get_user_script_content, get_env_isolated) - +from swanprojects.utils import SwanUtils from swanprojects.config import SwanConfig +from traitlets import Any class SwanAPIHandler(APIHandler): - swan_config = None + """ + Base class for all SwanProjects API handlers, + provides SwanUtils and SwanConfig object with command line and some other options. + """ def initialize(self): """ Initialization of the handler with the swan configuration object. """ self.swan_config = SwanConfig(config=self.config) + self.swan_utils = SwanUtils(self.contents_manager) + self.kernel_spec_manager.set_swan_utils(self.swan_utils) def subprocess(self, command): """ @@ -40,7 +43,7 @@ def subprocess(self, command): Popen object to get information from the executed process such as output and return code. """ - command = get_env_isolated() + command + command = self.swan_utils.get_env_isolated() + command self.log.info(f"running {command} ") proc = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -65,16 +68,14 @@ def get(self): caller = self.get_argument('caller') self.log.info(f"ProjectInfoHandler caller = {caller} path = {path}") - project = get_project_path(path) + project = self.swan_utils.get_project_path(path) project_data = {} if project is not None: - project_data = get_project_info(project) + project_data = self.swan_utils.get_project_info(project) project_data["name"] = project.split(os.path.sep)[-1] - readme = get_project_readme(project) - if readme is not None: - project_data["readme"] = readme - project_data["user_script"] = get_user_script_content(project) + project_data["user_script"] = self.swan_utils.get_user_script_content( + project) payload = {"project_data": project_data} self.finish(json.dumps(payload)) @@ -112,7 +113,7 @@ def post(self): self.log.info(f"KernelSpecManagerPathHandler = {input_data}") - project = get_project_path(path) + project = self.swan_utils.get_project_path(path) if self.kernel_spec_manager.set_path(path): data = {"status": True, "is_project": project is not None, "msg": f"SWAN kernel spec manager set to path: {path}"} @@ -141,8 +142,10 @@ def post(self): user_script = input_data["user_script"] project_relative_dir = os.path.join( self.swan_config.projects_folder_name, name) + try: - self.contents_manager.new({'type':'directory'},project_relative_dir) + self.contents_manager.new( + {'type': 'directory'}, project_relative_dir) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating folder for project {name}, traceback: {msg}"} @@ -153,8 +156,8 @@ def post(self): swan_project_content = {'stack': stack, 'release': release, 'platform': platform} try: - self.contents_manager.new({'type':'file','content':json.dumps(swan_project_content, - indent=4, sort_keys=True),'format':'text'},swan_project_file) + self.contents_manager.new({'type': 'file', 'content': json.dumps(swan_project_content, + indent=4, sort_keys=True), 'format': 'text'}, swan_project_file) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating {self.swan_config.project_file_name} file for project {name}, traceback: {msg}"} @@ -164,7 +167,8 @@ def post(self): try: swan_user_script_file = os.path.join( project_relative_dir, self.swan_config.userscript_file_name) - self.contents_manager.new({'type':'file','content':user_script,'format':'text'},swan_user_script_file) + self.contents_manager.new( + {'type': 'file', 'content': user_script, 'format': 'text'}, swan_user_script_file) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating {self.swan_config.userscript_file_name} file for project {name}, traceback: {msg}"} @@ -219,7 +223,8 @@ def put(self): try: old_project_dir = os.path.join( self.swan_config.projects_folder_name, old_name) - self.contents_manager.rename(old_project_dir, project_relative_dir) + self.contents_manager.rename( + old_project_dir, project_relative_dir) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error editing project folder {old_name}, traceback: {msg}"} @@ -230,8 +235,9 @@ def put(self): if old_userscript != user_script: try: swan_user_script_file = os.path.join( - project_relative_dir, self.swan_config.userscript_file_name) - self.contents_manager.new({'type':'file','content':user_script,'format':'text'},swan_user_script_file) + project_relative_dir, self.swan_config.userscript_file_name) + self.contents_manager.new( + {'type': 'file', 'content': user_script, 'format': 'text'}, swan_user_script_file) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error editing {self.swan_config.userscript_file_name} for project {name}, traceback: {msg}"} @@ -243,19 +249,19 @@ def put(self): project_relative_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} - kernel_dir = project_relative_dir + "/.local/share/jupyter/kernels" - kernel_dir_python2 = os.path.join(kernel_dir,'python2') - kernel_dir_python3 = os.path.join(kernel_dir,'python3') + kernel_dir = os.path.join(project_relative_dir, self.swan_config.kernel_folder_path) + kernel_dir_python2 = os.path.join(kernel_dir, 'python2') + kernel_dir_python3 = os.path.join(kernel_dir, 'python3') # removing old native kernels for python only(this is generated by us) - if os.path.exists(kernel_dir_python2): - shutil.rmtree(kernel_dir_python2) + if self.contents_manager.dir_exists(kernel_dir_python2): + self.contents_manager.delete(kernel_dir_python2, True) - if os.path.exists(kernel_dir_python3): - shutil.rmtree(kernel_dir_python3) + if self.contents_manager.dir_exists(kernel_dir_python3): + self.contents_manager.delete(kernel_dir_python3, True) - self.contents_manager.new({'type':'file','content':json.dumps(swan_project_content, - indent=4, sort_keys=True),'format':'text'},swan_project_file) + self.contents_manager.new({'type': 'file', 'content': json.dumps(swan_project_content, + indent=4, sort_keys=True), 'format': 'text'}, swan_project_file) command = ["swan_kmspecs", "--project_name", name, "--stacks_path", self.swan_config.stacks_path] stderr, returncode = self.subprocess(command) diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index 4bbe724e..899c230c 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -1,16 +1,19 @@ # Copyright (c) SWAN Development Team. # Author: Omar.Zapata@cern.ch 2021 +""" +Customized Kernel Spec Manager that allows handle kernels in multiple environments +for different projects. +""" import json import os import shutil from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel -from swanprojects.utils import (get_project_info, get_project_name, - get_project_path, get_env_isolated) from swanprojects.config import SwanConfig from traitlets import Unicode + class SwanKernelSpecManager(KernelSpecManager): path = Unicode("", config=True, allow_none=True, help="SWAN Project path") @@ -21,6 +24,10 @@ def __init__(self, **kwargs): self.project = None self.kernel_dirs = [] self.swan_config = SwanConfig(config=self.config) + self.swan_utils = None + + def set_swan_utils(self, swan_utils): + self.swan_utils = swan_utils def save_native_spec(self, kernel_dir, python_path, display_name): """ @@ -39,28 +46,28 @@ def save_native_spec(self, kernel_dir, python_path, display_name): "display_name": display_name, "language": "python" } - kernel_file = kernel_dir + "/kernel.json" - f = open(kernel_file, "w+") - json.dump(spec, f, indent=4) - f.close() + kernel_file = os.path.join(kernel_dir, "kernel.json") + self.swan_utils.contents_manager.new({'type': 'file', 'content': json.dumps( + spec, indent=4), 'format': 'text'}, kernel_file) def set_path(self, path): self.path = path - self.project = get_project_path(path) + self.project = self.swan_utils.get_project_path(path) if self.project is None: self.kernel_dirs = [] return True else: - self.project_info = get_project_info(self.project) - self.project_name = get_project_name(self.project) + self.project_info = self.swan_utils.get_project_info(self.project) + self.project_name = self.swan_utils.get_project_name(self.project) if "kernel_dirs" in self.project_info: self.kernel_dirs = self.project_info["kernel_dirs"] - local_kernels = self.project + "/.local/share/jupyter/kernels/" + local_kernels = os.path.join( + self.project, self.swan_config.kernel_folder_path) for version in ["2", "3"]: python = "python" + version if self.project_info[python]["found"] and self.project_info[python]["ipykernel"]: - kerne_dir = local_kernels + python - if not os.path.exists(kerne_dir): + kerne_dir = os.path.join(local_kernels, python) + if not self.swan_utils.contents_manager.dir_exists(kerne_dir): self.save_native_spec( kerne_dir, self.project_info[python]["path"], "Python " + version) self.kernel_dirs.append(local_kernels) @@ -75,7 +82,7 @@ def set_path(self, path): def wrap_kernel_specs(self, project_name, kspec): - argv = get_env_isolated() + argv = self.swan_utils.get_env_isolated() argv += ["/bin/bash", "-c", "swan_env {} {} {} ".format( project_name, self.swan_config.stacks_path, ".") + "'" + " ".join(kspec.argv) + "'" ] diff --git a/SwanProjects/swanprojects/utils.py b/SwanProjects/swanprojects/utils.py index 0308d129..efe8d4a9 100644 --- a/SwanProjects/swanprojects/utils.py +++ b/SwanProjects/swanprojects/utils.py @@ -1,107 +1,181 @@ -import json -import os +# Copyright (c) SWAN Development Team. +# Author: Omar.Zapata@cern.ch 2021 + +""" +This file has the class SwanUtils with several utility methods +to handle projects and their environments. +""" -def has_project_file(path): - """ - Method to check if .swanproject exists - path: path to check - """ - return os.path.exists(path + os.path.sep + ".swanproject") +from traitlets.config import Configurable +from swanprojects.config import SwanConfig +import json +import os -def get_project_info(path): - if has_project_file(path): - swanfile = path + os.path.sep + ".swanproject" - try: - with open(swanfile) as json_file: - data = json.load(json_file) - return data - except Exception as e: - print(e) +class SwanUtils(Configurable): + + def __init__(self, contents_manager): + self.contents_manager = contents_manager + self.swan_config = SwanConfig(config=self.config) + + def has_project_file(self, path): + """ + Method to check if .swanproject exists + + Parameters + ---------- + path : str + project path. + + Returns + ------- + bool + True if the project file .swanprojects was found. + """ + return self.contents_manager.file_exists(os.path.join(path, self.swan_config.project_file_name)) + + def get_project_path(self, path): + """ + This method returns the project path, the contents_manager._get_project_path function + returns three possible values 'invalid', None or the path, + then if it is invalid I return just None to simplify the error control. + + Invalid means for SwanContents a project outside of SWAN_projects folder. + + Parameters + ---------- + path : str + A path. + + Returns + ------- + path: str || None + The path to the project or None if not project found + """ + if not path.startswith(os.sep): + path = os.sep + path # initial '/' is required by swanconents otherwise it is invalid + path = self.contents_manager._get_project_path(path) + if path == 'invalid': + return None + else: + return path + + def get_project_info(self, path): + """ + This method returns the project info such as stack, release, platform etc.. + + Parameters + ---------- + path : str + A path to the project. + + Returns + ------- + path: Dict || None + Project information in a dictionary or a empty dictionary in case of error. + """ + if self.has_project_file(path): + swanfile = os.path.join(path, self.swan_config.project_file_name) + swanfile_model = self.contents_manager.get(swanfile) + try: + data = json.loads(swanfile_model['content']) + return data + except Exception as e: + print(e) + return {} + else: return {} - else: - return {} - - -def get_project_path(cwd): - if cwd.startswith(os.sep): - cwd = cwd[1:] - - paths = cwd.split(os.path.sep) - cwd_current = cwd - for i in range(len(paths)): - if has_project_file(cwd_current): - return cwd_current - cwd_current = cwd_current[:-(len(paths[len(paths) - i - 1]) + 1)] - return None - - -def get_project_readme(project_path): - readme_path = project_path + os.path.sep + "README.md" - if os.path.exists(readme_path): - f = open(readme_path, "r") - text = f.read() - f.close() - return text - else: - return None - - -def get_user_script_content(project_path): - user_script_path = project_path + os.path.sep + ".userscript" - if os.path.exists(user_script_path): - f = open(user_script_path, "r") - text = f.read() - f.close() - return text - else: - return "" - - -def get_project_name(project_path): - path = get_project_path(project_path) - name = None - if path is not None: - name = path.split(os.sep)[-1] - return name - - -def check_project_info(project_info): - """ - Allows to check if the .swanproject file content is corrupted. - """ - project_keys = ["stack", "platform", "release", - "user_script", "python3", "python2", "kernel_dirs"] - not_found = [] - status = True - for key in project_keys: - if key not in project_info.keys(): - status = False - not_found.append(key) - return {"status": status, "not_found": not_found} - - -def get_env_isolated(): - """ - Command line required with environmental variables to isolate the environment. - """ - command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] - # checking if we are on EOS to add the env variables - # we required this to read/write in a isolate environment with EOS - if "OAUTH2_FILE" in os.environ: - command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) - if "OAUTH2_TOKEN" in os.environ: - command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) - if "OAUTH_INSPECTION_ENDPOINT" in os.environ: - command.append("OAUTH_INSPECTION_ENDPOINT=%s" % - os.environ["OAUTH_INSPECTION_ENDPOINT"]) - - # special case when the package was not installed like root, useful for development - command.append( - "PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:{}/.local/bin/".format(os.environ["HOME"])) - command.append( - "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib" + + def get_user_script_content(self, project_path): + """ + Returns the content of the user script saved in .user_script file. + + Parameters + ---------- + path : str + A path to the project. + + Returns + ------- + path: str + The content of the user script or an empty string the the project doesn't have a user script file. + """ + user_script_path = os.path.join( + project_path, self.swan_config.userscript_file_name) + + if self.contents_manager.file_exists(user_script_path): + user_script_path_model = self.contents_manager.get( + user_script_path) + return user_script_path_model['content'] + else: + return "" + + def get_project_name(self, project_path): + """ + Returns the project name. + + Parameters + ---------- + path : str + A path to the project. + + Returns + ------- + path: str || None + The name of the project or None is the path provided is not a project. + """ + + path = self.get_project_path(project_path) + name = None + if path is not None: + name = path.split(os.sep)[-1] + return name + + def check_project_info(self, project_info): + """ + Allows to check if the .swanproject file content is corrupted. + + Parameters + ---------- + path : Dict + Project information such as stack, release, platform etc... + + Returns + ------- + path: Dict + Dict with the status and missing fields in case of error. + """ + project_keys = ["stack", "platform", "release", + "user_script", "python3", "python2", "kernel_dirs"] + not_found = [] + status = True + for key in project_keys: + if key not in project_info.keys(): + status = False + not_found.append(key) + return {"status": status, "not_found": not_found} + + def get_env_isolated(self): + """ + Command line required with environment variables to isolate execution. + """ + command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] + # checking if we are on EOS to add the env variables + # we required this to read/write in a isolate environment with EOS + if "OAUTH2_FILE" in os.environ: + command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) + if "OAUTH2_TOKEN" in os.environ: + command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) + if "OAUTH_INSPECTION_ENDPOINT" in os.environ: + command.append("OAUTH_INSPECTION_ENDPOINT=%s" % + os.environ["OAUTH_INSPECTION_ENDPOINT"]) + + # special case when the package was not installed like root, useful for development + command.append( + "PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:{}/.local/bin/".format(os.environ["HOME"])) + command.append( + "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib" ) - return command + return command From c8e523d447b1e17be4b59f891c419bb7d222bda7 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 21 Oct 2021 14:23:24 +0200 Subject: [PATCH 13/35] removed unneeded EOS variables for isolated environment. --- SwanProjects/bin/swan_kmspecs | 5 ----- SwanProjects/swanprojects/utils.py | 5 ----- 2 files changed, 10 deletions(-) diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index 5b854b27..b9b786ff 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -182,13 +182,8 @@ def swan_kmspecs(project_name,stacks_path): command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary. # we required this to read/write in a isolate environment with EOS - if "OAUTH2_FILE" in os.environ: - command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) if "OAUTH2_TOKEN" in os.environ: command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) - if "OAUTH_INSPECTION_ENDPOINT" in os.environ: - command.append("OAUTH_INSPECTION_ENDPOINT=%s" % - os.environ["OAUTH_INSPECTION_ENDPOINT"]) command += ["/bin/bash", "swan_env", project_name,stacks_path, ".", "python", __file__, "--generate_ksminfo", "--project_name", project_name] diff --git a/SwanProjects/swanprojects/utils.py b/SwanProjects/swanprojects/utils.py index efe8d4a9..c36dbba4 100644 --- a/SwanProjects/swanprojects/utils.py +++ b/SwanProjects/swanprojects/utils.py @@ -163,13 +163,8 @@ def get_env_isolated(self): command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] # checking if we are on EOS to add the env variables # we required this to read/write in a isolate environment with EOS - if "OAUTH2_FILE" in os.environ: - command.append("OAUTH2_FILE=%s" % os.environ["OAUTH2_FILE"]) if "OAUTH2_TOKEN" in os.environ: command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) - if "OAUTH_INSPECTION_ENDPOINT" in os.environ: - command.append("OAUTH_INSPECTION_ENDPOINT=%s" % - os.environ["OAUTH_INSPECTION_ENDPOINT"]) # special case when the package was not installed like root, useful for development command.append( From b98417e369a086f6521ccbec1d8845c22872a3b7 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 26 Oct 2021 15:12:11 +0200 Subject: [PATCH 14/35] fixed problem testing on swan-spare node implementing the Contents Manager Running locally the root_dir is not set on https://github.com/swan-cern/jupyter-extensions/blob/master/SwanContents/swancontents/filemanager/swanfilemanager.py#L49 but in swan-spare it is set, because at the moment it is not using jupyter_server. to run it locally I found the solution with https://github.com/jupyterlab/jupyterlab/issues/9633 https://github.com/jupyter-server/jupyter_server/pull/400 passing the parameter --NotebookApp.notebook_dir=/path/to/home to jupyterlab --- SwanProjects/swanprojects/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SwanProjects/swanprojects/utils.py b/SwanProjects/swanprojects/utils.py index c36dbba4..19d01e1a 100644 --- a/SwanProjects/swanprojects/utils.py +++ b/SwanProjects/swanprojects/utils.py @@ -53,8 +53,9 @@ def get_project_path(self, path): path: str || None The path to the project or None if not project found """ - if not path.startswith(os.sep): - path = os.sep + path # initial '/' is required by swanconents otherwise it is invalid + if path.startswith(os.sep): + # removing '/' is required by swanconents otherwise it is invalid + path = path[1:] path = self.contents_manager._get_project_path(path) if path == 'invalid': return None From 047567261d729e511d32763ea47b29ca75273d3c Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 26 Oct 2021 16:02:51 +0200 Subject: [PATCH 15/35] added FCCSW stack support --- .../swanprojects/stacks/FCCSW/README.md | 2 + .../swanprojects/stacks/FCCSW/config.json | 7 + .../swanprojects/stacks/FCCSW/logo.svg | 179 ++++++++++++++++++ .../swanprojects/stacks/FCCSW/setup.sh | 6 + 4 files changed, 194 insertions(+) create mode 100644 SwanProjects/swanprojects/stacks/FCCSW/README.md create mode 100644 SwanProjects/swanprojects/stacks/FCCSW/config.json create mode 100644 SwanProjects/swanprojects/stacks/FCCSW/logo.svg create mode 100644 SwanProjects/swanprojects/stacks/FCCSW/setup.sh diff --git a/SwanProjects/swanprojects/stacks/FCCSW/README.md b/SwanProjects/swanprojects/stacks/FCCSW/README.md new file mode 100644 index 00000000..090b6e97 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/FCCSW/README.md @@ -0,0 +1,2 @@ +At the moment there is only on release that works, because is the only one that +has ipykernel package. diff --git a/SwanProjects/swanprojects/stacks/FCCSW/config.json b/SwanProjects/swanprojects/stacks/FCCSW/config.json new file mode 100644 index 00000000..470d008e --- /dev/null +++ b/SwanProjects/swanprojects/stacks/FCCSW/config.json @@ -0,0 +1,7 @@ +{ + "releases":{ + "2021-09-01": [ + "x86_64-centos7-gcc8.3.0-opt" + ] + } +} diff --git a/SwanProjects/swanprojects/stacks/FCCSW/logo.svg b/SwanProjects/swanprojects/stacks/FCCSW/logo.svg new file mode 100644 index 00000000..44cf6d5b --- /dev/null +++ b/SwanProjects/swanprojects/stacks/FCCSW/logo.svg @@ -0,0 +1,179 @@ + + + + diff --git a/SwanProjects/swanprojects/stacks/FCCSW/setup.sh b/SwanProjects/swanprojects/stacks/FCCSW/setup.sh new file mode 100644 index 00000000..ede7a804 --- /dev/null +++ b/SwanProjects/swanprojects/stacks/FCCSW/setup.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# This script allows to source FCCSW stack for a given release and platform. +# variables $RELEASE and $PLATFORM have to be exported before source this script by swan_env +# This stack in particlar has an extra folder with an id generated by spack, in a similar way +# they are doing in /cvmfs/fcc.cern.ch/sw/latest/setup.sh we put the regular expression "*" to complete the path. +source /cvmfs/sw.hsf.org/spackages2/key4hep-stack/$RELEASE/$PLATFORM/*/setup.sh From fd65b5414db6b765a73f36db02402f44f7ad6c67 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 28 Oct 2021 10:10:57 +0200 Subject: [PATCH 16/35] Modified the way to get information from the environment for the kernels. The handlers are calling a subprocess and capturing the stdout, the infortmation is saved in the project using the content manager. --- SwanProjects/bin/swan_bash | 4 +- SwanProjects/bin/swan_env | 7 +- SwanProjects/bin/swan_kmspecs | 153 +++++--------------------- SwanProjects/swanprojects/handlers.py | 124 +++++++++++++++------ 4 files changed, 122 insertions(+), 166 deletions(-) diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index a7019f02..0978c420 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -34,10 +34,10 @@ if [[ $# -gt 1 ]] ; then RELEASE=`jq '.release' $PROJECT_FILE` PLATFORM=`jq '.platform' $PROJECT_FILE` USER_SCRIPT="$PROJECT_PATH/.userscript" + echo "Loading $RELEASE with plafortm $PLATFORM " # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary - env -i HOME=$HOME OAUTH2_FILE=$OAUTH2_FILE \ + env -i HOME=$HOME \ OAUTH2_TOKEN=$OAUTH2_TOKEN \ - OAUTH_INSPECTION_ENDPOINT=$OAUTH_INSPECTION_ENDPOINT \ PROJECT=$PROJECT \ PROJECT_PATH=$PROJECT_PATH PS1="$PS1" \ bash -c "swan_env $PROJECT $STACKS_PATH $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index 2730d7f3..6a02e553 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -70,16 +70,15 @@ done COMMAND=${PARAMETERS[@]} if [ ! -d $STACK_PATH ]; then - echo "Error sourcing the environment, the stack $STACK was not found in stacks path $STACKS_PATH" - echo "project $PROJECT can not be loeaded." + echo "Error sourcing the environment, the stack $STACK was not found in stacks path $STACKS_PATH" >&2 + echo "project $PROJECT can not be loeaded." >&2 exit 1 fi if [ -f $STACK_SETUP ]; then - echo "Loading $RELEASE with plafortm $PLATFORM " . $STACK_SETUP else - echo "Error loading stack setup.sh on $STACK_SETUP, file doesn't exists." + echo "Error loading stack setup.sh on $STACK_SETUP, file doesn't exists." >&2 exit 1 fi diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index b9b786ff..507b4603 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -6,8 +6,9 @@ This script allows to find the kernels for python2/3 and kernel spec paths for our kernel spec manager. The script run a subprocess inside the project environment trying to find the package ipykernel -and run jupyter_path('kernels') to get the list of available paths for the differents kernels inside the project environment as well. +and run jupyter_path('kernels') to get the list of available paths for the differents kernels inside the environment as well. +This is execute in the Create/Edit project handlers and the stdout is captured, getting the json information printed in the function generate_ksminfo() """ import argparse import json @@ -20,13 +21,14 @@ from shutil import rmtree try: from jupyter_core.paths import jupyter_path except ImportError: - print("Package jupyter_core not found in the environment, kernel paths can not be found.") + print("{'status':0, 'msg':'Package jupyter_core not found in the environment, kernel paths can not be found.'}") sys.exit(1) + def checkipykernel(python_interpreter): """ Checks if ipykernel is available for the given python interpreter, - this function is executed inside the project environment. + this function have to be executed inside the project environment. Parameters ---------- @@ -42,46 +44,34 @@ def checkipykernel(python_interpreter): command = [python_interpreter, "-c", python_code] proc = subprocess.Popen(command, stdout=subprocess.PIPE) proc.wait() - data = proc.stdout.read().decode("utf-8") proc.communicate() return proc.returncode -def check_native_kernel(project_path): +def check_native_kernel(): """ Checks if ipykernel is available for python2 and python3. This routine is called inside the project environment, It checks if python2/3 are available to check if the package ipykernel is installed, - if the package was found the results is saved in the .swanproject file. + if the package was found the results is saved in the dictionary with the results. - Why? kernel.json is not always available in the software stacks but the package ipykern is there, + Why? kernel.json is not always available in the software stacks but the package ipykernel is there, then we can create the json file locally in the project to put it work, because our kernel spec manager requires the kernel path with the kernel.json file. - Parameters - ---------- - project_path : str - path to the project we are trying to find the ipykernel packages. - + Returns + ------- + dict + information about python version found and if ipykernel was found for the python version. """ - project_file = os.path.join(project_path, ".swanproject") - f = open(project_file, "r+") - project_data = f.read() - if project_data.strip() == "": - project_data = {} - else: - project_data = json.loads(project_data) - f.seek(0) + project_data = {} # checking if python2 is found python2 = find_executable("python2") if python2 is not None: - print("python2 found = "+python2) project_data["python2"] = {"found": True, "path": python2} # checking is ipython is found for python2 rcode = checkipykernel(python2) - if rcode != 0: - print("Error ipykernel not found for python2 in project " + project_path) if rcode == 0: project_data["python2"]["ipykernel"] = True else: @@ -92,37 +82,26 @@ def check_native_kernel(project_path): # checking if python2 is found python3 = find_executable("python3") if python3 is not None: - print("python3 found = "+python3) project_data["python3"] = {"found": True, "path": python3} # checking is ipython is found for python3 rcode = checkipykernel(python3) - if rcode != 0: - print("Error ipykernel not found for python3 in project " + project_path) if rcode == 0: project_data["python3"]["ipykernel"] = True else: project_data["python3"]["ipykernel"] = False else: project_data["python3"]["found"] = False - - # removing the previuos information of the .swanproject file to put the new one. - f.seek(0) - f.truncate() - # Saving the results in the .swanproject file. - json.dump(project_data, f, indent=4) - f.close() + return project_data -def save_kernel_paths(project_path): +def get_kernel_paths(): """ Allows to find kernel paths inside the environment - and save then in the project config file. - - Parameters - ---------- - project_path : str - path to the project we are trying to find the kernel paths. + Returns + ------- + list + kernel paths found inside the environment. """ kernels_blacklist_paths = [os.path.join( os.environ["HOME"], '.local/share/jupyter/kernels'), '/usr/local/share/jupyter/kernels', '/usr/share/jupyter/kernels'] @@ -135,97 +114,21 @@ def save_kernel_paths(project_path): found = True if not found: paths.append(path) - project_file = os.path.join(project_path, ".swanproject") + return paths - with open(project_file, "r+") as f: - project_data = f.read() - if project_data == "": - project_data = {} - else: - project_data = json.loads(project_data) - f.seek(0) - f.truncate() - if "kernel_dirs" in list(project_data.keys()): - project_data["kernel_dirs"] = project_data["kernel_dirs"] + paths - else: - project_data["kernel_dirs"] = paths - json.dump(project_data, f, indent=4) -def generate_ksminfo(project_path): +def generate_ksminfo(): """ Function to generated all the kernel spec manager info, - calling the function 'check_native_kernel' and to save the kernel paths calling the function save_kernel_paths. - - This function is called inside the project environment. - - Parameters - ---------- - project_path : str - path to the project we are trying to find the information about the kernels.. + calling the function check_native_kernel and get_kernel_paths. """ - check_native_kernel(project_path) - save_kernel_paths(project_path) - - -def swan_kmspecs(project_name,stacks_path): - """ - This functions launches a subprocess to find the information of the kernels inside the environment of the project. - - Parameters - ---------- - project_path : str - path to the project we are trying to find the information about the kernels.. - stacks_path : str - path to the stacks folder with the information aboud the stacks available. - - """ - command = ["env", "-i", "HOME=%s" % os.environ["HOME"]] - # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary. - # we required this to read/write in a isolate environment with EOS - if "OAUTH2_TOKEN" in os.environ: - command.append("OAUTH2_TOKEN=%s" % os.environ["OAUTH2_TOKEN"]) - command += ["/bin/bash", "swan_env", project_name,stacks_path, ".", "python", - __file__, "--generate_ksminfo", "--project_name", project_name] - - proc = subprocess.Popen(command, stdout=subprocess.PIPE) - proc.wait() - data = proc.stdout.read().decode("utf-8") - proc.communicate() - if proc.returncode != 0: - print("Error creating navite kernel and finding kernels paths for project{}".format( - project_name)) - - return proc.returncode - + ksminfo = check_native_kernel() + ksminfo["kernel_dirs"] = get_kernel_paths() + ksminfo["status"] = 1 + print(json.dumps(ksminfo, indent=4)) if __name__ == '__main__': """ - entry point to use this script, the parameter project name is required, - the other parameter is used when this script is executed inside the project environment. - - Parameters - ---------- - project_name : str - project name - generate_ksminfo: None - called by the subprocess inside the project environment to generated the kernel information. - + Entry point to use this script, calls generate_ksminfo routine to generate the kernel info. """ - parser = argparse.ArgumentParser( - description='Swan Environment Kernel Manager CMD options.') - parser.add_argument('--project_name', type=str, - required=True, help='Project name') - parser.add_argument('--stacks_path', type=str, - help='Stacks path') - parser.add_argument('--generate_ksminfo', action='store_true', - default=None, help='Generates Kernel Spec Manager info') - args = parser.parse_args() - project_name = args.project_name - - project_path = os.path.join( - os.environ["HOME"], "SWAN_projects", project_name) - if args.generate_ksminfo: - generate_ksminfo(project_path) - else: - rcode = swan_kmspecs(project_name, args.stacks_path) - sys.exit(rcode) + generate_ksminfo() diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 476e0110..d1300b65 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -1,9 +1,16 @@ # Copyright (c) SWAN Development Team. # Author: Omar.Zapata@cern.ch 2021 + +""" +File with the handlers for our API. +The handlers allows to Create/Edit projects, get project info +and set parameters to our Kernel Spec Manager. +""" import json import os import subprocess from glob import glob +from distutils.spawn import find_executable import tornado from notebook.base.handlers import APIHandler @@ -40,8 +47,8 @@ def subprocess(self, command): Returns ------- - Popen - object to get information from the executed process such as output and return code. + (stdout, stderr, returncode): tuple + Output for stout, stderr and return code. """ command = self.swan_utils.get_env_isolated() + command self.log.info(f"running {command} ") @@ -51,9 +58,66 @@ def subprocess(self, command): stdout = proc.stdout.read().decode("utf-8") stderr = proc.stderr.read().decode("utf-8") proc.communicate() - self.log.info(f"result stdout: {stdout} ") - self.log.info(f"result stderr: {stderr} ") - return (stderr, proc.returncode) + self.log.debug(f"result stdout: {stdout} ") + self.log.debug(f"result stderr: {stderr} ") + return (stdout, stderr, proc.returncode) + + def save_project_file(self, project_dir, content): + """ + Method to save contents in the project file. + + Parameters + ---------- + project_dir : str + Path to the project. + + content:dict + Data to save in the project file. + + Returns + ------- + status:bool + True if the content was saved or False if any error. + """ + project_file = os.path.join( + project_dir, self.swan_config.project_file_name) + + try: + self.contents_manager.new({'type': 'file', 'content': json.dumps(content, + indent=4, sort_keys=True), 'format': 'text'}, project_file) + return True + except Exception as msg: + data = {"status": False, "project_dir": project_dir, + "msg": f"Error saving {self.swan_config.project_file_name} file for the project, traceback: {msg}"} + self.finish(json.dumps(data)) + return False + + def get_kmanager_info(self, project_dir): + """ + Retrieve kernel manager information executing swan_kmspecs inside the project's environment. + + Parameters + ---------- + project_dir : str + Path to the project. + + Returns + ------- + kinfo: dict + Kernel information such as kernei_dir and ipykernel for python2/3 in a dictionary. + """ + name = project_dir.split(os.sep)[-1] + swan_kmspecs = find_executable("swan_kmspecs") + command = ["swan_env", name, self.swan_config.stacks_path, + ".", "python", swan_kmspecs] + stdout, stderr, returncode = self.subprocess(command) + self.log.info(f"swan_kmspecs return code: {returncode}") + if returncode != 0: + data = {"status": False, "project_dir": project_dir, + "msg": f"Error collecting the information from cvmfs for project {name}, traceback: {stderr}"} + self.finish(json.dumps(data)) + kinfo = json.loads(stdout) + return kinfo class ProjectInfoHandler(SwanAPIHandler): @@ -131,6 +195,10 @@ def post(self): Endpoint to create a project, receive project information such as name, stack, platform, release, user_script. The project is created at $HOME/SWAN_projects/project_name and a hidden json ".swanproject" file with the information project is set inside the project folder. + + Using a subprocess inside the enviroment, we retrieve information for kernel spec manager + such as kernel dirs and ipykernel package availability for python2/3 and this + information is saved in the project file too. """ input_data = self.get_json_body() self.log.info(f"creating project {input_data}") @@ -144,24 +212,16 @@ def post(self): self.swan_config.projects_folder_name, name) try: - self.contents_manager.new( - {'type': 'directory'}, project_relative_dir) + self.contents_manager._save_project(project_relative_dir, None) except Exception as msg: data = {"status": False, "project_dir": project_relative_dir, "msg": f"Error creating folder for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return - swan_project_file = os.path.join( - project_relative_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} - try: - self.contents_manager.new({'type': 'file', 'content': json.dumps(swan_project_content, - indent=4, sort_keys=True), 'format': 'text'}, swan_project_file) - except Exception as msg: - data = {"status": False, "project_dir": project_relative_dir, - "msg": f"Error creating {self.swan_config.project_file_name} file for project {name}, traceback: {msg}"} - self.finish(json.dumps(data)) + + if not self.save_project_file(project_relative_dir, swan_project_content): return try: @@ -174,14 +234,11 @@ def post(self): "msg": f"Error creating {self.swan_config.userscript_file_name} file for project {name}, traceback: {msg}"} self.finish(json.dumps(data)) return - command = ["/bin/bash", "-c", - f"swan_kmspecs --project_name {name} --stacks_path {self.swan_config.stacks_path}"] - stderr, returncode = self.subprocess(command) - self.log.info(f"swan_kmspecs return code: {returncode}") - if returncode != 0: - data = {"status": False, "project_dir": project_relative_dir, - "msg": f"Error collecting the information from cvmfs for project {name}, traceback: {stderr}"} - self.finish(json.dumps(data)) + + km_info = self.get_kmanager_info(project_relative_dir) + swan_project_content.update(km_info) + + if not self.save_project_file(project_relative_dir, swan_project_content): return data = {"status": True, "project_dir": project_relative_dir, @@ -249,7 +306,10 @@ def put(self): project_relative_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} - kernel_dir = os.path.join(project_relative_dir, self.swan_config.kernel_folder_path) + if not self.save_project_file(project_relative_dir, swan_project_content): + return + kernel_dir = os.path.join( + project_relative_dir, self.swan_config.kernel_folder_path) kernel_dir_python2 = os.path.join(kernel_dir, 'python2') kernel_dir_python3 = os.path.join(kernel_dir, 'python3') @@ -260,17 +320,11 @@ def put(self): if self.contents_manager.dir_exists(kernel_dir_python3): self.contents_manager.delete(kernel_dir_python3, True) - self.contents_manager.new({'type': 'file', 'content': json.dumps(swan_project_content, - indent=4, sort_keys=True), 'format': 'text'}, swan_project_file) - command = ["swan_kmspecs", "--project_name", name, - "--stacks_path", self.swan_config.stacks_path] - stderr, returncode = self.subprocess(command) - self.log.info(f"swan_kmspecs return code: {returncode}") - if returncode != 0: - data = {"status": False, "project_dir": project_relative_dir, - "msg": f"Error editing stack, platform or release for project {name}, traceback: {stderr}"} - self.finish(json.dumps(data)) + km_info = self.get_kmanager_info(project_relative_dir) + swan_project_content.update(km_info) + if not self.save_project_file(project_relative_dir, swan_project_content): return + data = {"status": True, "project_dir": project_relative_dir, "msg": f"edited project {name}"} self.finish(json.dumps(data)) From 6bc92ce72cd5f064b7767bc5bf057f4b3c57622f Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 28 Oct 2021 14:25:03 +0200 Subject: [PATCH 17/35] fixed path home for kernels, taking it from the funciton jupyter_core.paths.get_home_dir instead from os environment. They are different. --- SwanProjects/bin/swan_kmspecs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index 507b4603..dc647c9c 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -19,7 +19,7 @@ import sys from distutils.spawn import find_executable from shutil import rmtree try: - from jupyter_core.paths import jupyter_path + from jupyter_core.paths import jupyter_path, get_home_dir except ImportError: print("{'status':0, 'msg':'Package jupyter_core not found in the environment, kernel paths can not be found.'}") sys.exit(1) @@ -104,7 +104,7 @@ def get_kernel_paths(): kernel paths found inside the environment. """ kernels_blacklist_paths = [os.path.join( - os.environ["HOME"], '.local/share/jupyter/kernels'), '/usr/local/share/jupyter/kernels', '/usr/share/jupyter/kernels'] + get_home_dir(), '.local/share/jupyter/kernels'), '/usr/local/share/jupyter/kernels', '/usr/share/jupyter/kernels'] tmp_paths = jupyter_path('kernels') paths = [] for path in tmp_paths: From 4b43c4da3ff2e9aed6a08988b02c5c743cf83de6 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 1 Nov 2021 15:22:37 +0100 Subject: [PATCH 18/35] remove kernels path from list. --- SwanProjects/bin/swan_kmspecs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index dc647c9c..37799ddf 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -106,6 +106,8 @@ def get_kernel_paths(): kernels_blacklist_paths = [os.path.join( get_home_dir(), '.local/share/jupyter/kernels'), '/usr/local/share/jupyter/kernels', '/usr/share/jupyter/kernels'] tmp_paths = jupyter_path('kernels') + if "kernels" in tmp_paths: + tmp_paths.remove("kernels") paths = [] for path in tmp_paths: found = False From d9fc48fad2e10b47d135b01550b04929ad894a9b Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 1 Nov 2021 16:18:36 +0100 Subject: [PATCH 19/35] fixed relative path to absule path in the kernel spec manager for local kernels "native" inside the project --- SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index 899c230c..ac05f6a5 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -70,7 +70,7 @@ def set_path(self, path): if not self.swan_utils.contents_manager.dir_exists(kerne_dir): self.save_native_spec( kerne_dir, self.project_info[python]["path"], "Python " + version) - self.kernel_dirs.append(local_kernels) + self.kernel_dirs.append(os.path.join(self.swan_utils.contents_manager.root_dir,local_kernels)) self.log.debug(f"KERNEL DIRS = {self.kernel_dirs}") self.log.debug(f"specs:\n {self.get_all_specs()}") return True From ddd5886407a4782b3d68737bbafa3dd3398ebca5 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 1 Nov 2021 17:17:54 +0100 Subject: [PATCH 20/35] fixed bumpversion file, the version is only available in the package.json --- SwanProjects/.bumpversion.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/SwanProjects/.bumpversion.cfg b/SwanProjects/.bumpversion.cfg index 7f6a9647..ea448844 100644 --- a/SwanProjects/.bumpversion.cfg +++ b/SwanProjects/.bumpversion.cfg @@ -5,7 +5,5 @@ tag = True tag_name = SwanProjects/v{new_version} message = SwanProjects v{new_version} -[bumpversion:file:swanprojects/_version.py] - [bumpversion:file:package.json] From 9034e6ace5b14a5145cb78e23f21508b6f8c197e Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 2 Nov 2021 15:42:27 +0100 Subject: [PATCH 21/35] Removed stacks from ISWANOptions to optimize the data send to the backend, new interface created named ISWANStackOptions --- SwanProjects/src/Components.tsx | 20 +++++----- SwanProjects/src/ProjectDialog.tsx | 34 ++++++++++------ SwanProjects/src/ProjectWidget.tsx | 61 +++++++++++++++++------------ SwanProjects/src/dialog.tsx | 31 ++++++++------- SwanProjects/src/icons.ts | 8 ++-- SwanProjects/src/index.ts | 31 +++++++++------ SwanProjects/src/request.ts | 24 +++--------- SwanProjects/src/theme-provider.tsx | 16 ++++---- 8 files changed, 121 insertions(+), 104 deletions(-) diff --git a/SwanProjects/src/Components.tsx b/SwanProjects/src/Components.tsx index b4d08825..1bf19348 100644 --- a/SwanProjects/src/Components.tsx +++ b/SwanProjects/src/Components.tsx @@ -22,15 +22,15 @@ export function HelpTooltip(props: { }): React.ReactElement { return (
- - ? - - `${dataTip}`} - /> + + ? + + `${dataTip}`} + />
); } @@ -70,7 +70,7 @@ export function Card(props: { style={{ height: '75px', width: '75px', - border: props.isSelected ? '2px solid var(--jp-brand-color1)' : '' + border: props.isSelected ? '2px solid var(--jp-brand-color1)' : '', }} className="jp-LauncherCard" id={props.label} diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 85f2169f..18d9fecd 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -15,7 +15,7 @@ import { showDialog } from './dialog'; import { contentRequest, createProjectRequest, - editProjectRequest + editProjectRequest, } from './request'; import { Spinner } from '@jupyterlab/apputils'; import { CommandRegistry } from '@lumino/commands'; @@ -23,10 +23,15 @@ import { CommandRegistry } from '@lumino/commands'; * Namespace for project dialogs */ export namespace ProjectDialog { - export interface ISWANStackNodeOptions{ - releases:{ [release: string]: Array }, - logo:string + export interface ISWANStackNodeOptions { + releases: { [release: string]: Array }; + logo: string; } + + export interface ISWANStackOptions { + stacks_options?: { [stack: string]: ISWANStackNodeOptions }; + } + export interface ISWANOptions { name?: string; stack?: string; @@ -34,7 +39,6 @@ export namespace ProjectDialog { platform?: string; user_script?: string; corrupted?: boolean; - stacks_options?: { [stack: string]: ISWANStackNodeOptions }; } /** @@ -58,6 +62,7 @@ export namespace ProjectDialog { // eslint-disable-next-line no-inner-declarations export async function OpenModal( options: ISWANOptions, + stacks: ISWANStackOptions, create: boolean, commands: CommandRegistry, theme: 'light' | 'dark' @@ -90,10 +95,13 @@ export namespace ProjectDialog { newOptions?: ISWANOptions; } | null = null; do { - dialogResult = await showDialog({ - ...options, - theme - }); + dialogResult = await showDialog( + { + ...options, + theme, + }, + { ...stacks } + ); if (dialogResult?.changesSaved && dialogResult?.newOptions) { options = dialogResult.newOptions; if (options.name?.trim() !== '') { @@ -102,7 +110,7 @@ export namespace ProjectDialog { const content = await contentRequest( 'SWAN_projects/' + options.name ).catch((): void => { - // No message here, it is not needed, + // No message here, it is not needed, //I am checking if the directory doesn't exist in order //to make valid the creation of the project folder. }); @@ -169,7 +177,7 @@ export namespace ProjectDialog { if (res.status) { commands.execute('filebrowser:go-to-path', { path: res.project_dir, - showBrowser: false + showBrowser: false, }); } else { stopSpinner(); @@ -185,7 +193,7 @@ export namespace ProjectDialog { await commands .execute('filebrowser:go-to-path', { path: '/SWAN_projects', - showBrowser: false + showBrowser: false, }) .then(async () => { await editProjectRequest(old_options, options) @@ -194,7 +202,7 @@ export namespace ProjectDialog { await commands .execute('filebrowser:go-to-path', { path: res.project_dir, - showBrowser: false + showBrowser: false, }) .catch((msg: any) => { stopSpinner(); diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 4bdcd8e6..8213b045 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -3,7 +3,7 @@ /** * This is the file with the React widget that has the components and callbacks - * to capture the information for the project, such as name, stack, release, platform and bash user script. + * to capture the information for the project, such as name, stack, release, platform and bash user script. */ import React from 'react'; @@ -21,54 +21,61 @@ import { LabIcon } from '@jupyterlab/ui-components'; import { ProjectDialog } from './ProjectDialog'; - /** * Functio to create the widget required for the modal dialog, it is basically a form, * also it has the callbacks to handle the events. * * @param options - The dialog setup options. + * @param stacks - Available stack options. * @param onSubmit - callback to execute on submit action * @param onCancel - callback to execute on cancel action. * @returns the DOM element with the form. */ export const ProjectWidget: React.FunctionComponent<{ options: ProjectDialog.ISWANOptions; + stacks: ProjectDialog.ISWANStackOptions; onSubmit: (selectedOptions: ProjectDialog.ISWANOptions) => void; onCancel: () => void; -}> = props => { +}> = (props) => { const options = props.options; + const stacks = props.stacks; const [projectName, setProjectName] = React.useState(options.name || ''); - const availableStacks = Object.keys(options.stacks_options).filter(function(e) { return e !== 'path' }); + const availableStacks = Object.keys(stacks.stacks_options).filter((e) => { + return e !== "path"; + }); const defaultStack = availableStacks.includes(options.stack) ? options.stack : availableStacks[0]; const [stack, setStack] = React.useState(defaultStack); - const availableReleases = Object.keys(options.stacks_options[stack]['releases']); + const availableReleases = Object.keys( + stacks.stacks_options[stack]['releases'] + ); const defaultRelease = availableReleases.includes(options.release) ? options.release : availableReleases[0]; const [release, setRelease] = React.useState(defaultRelease); - const availablePlatforms = options.stacks_options[stack]['releases'][release]; + const availablePlatforms = stacks.stacks_options[stack]['releases'][release]; const defaultPlatform = availablePlatforms.includes(options.platform) ? options.platform : availablePlatforms[0]; const [platform, setPlatform] = React.useState(defaultPlatform); const [userScript, setUserScript] = React.useState(options.user_script || ''); - var stack_icons:{ [item: string]: LabIcon} = {} + const stack_icons: { [item: string]: LabIcon } = {}; const randomId = () => { - return Math.random().toString(36).substring(2, 15) + return Math.random().toString(36).substring(2, 15); }; - availableStacks.map(item => { + availableStacks.map((item) => { stack_icons[item] = new LabIcon({ - name: 'jupyterlab_swan_stack:'+randomId(), - svgstr: options.stacks_options[item]['logo'] - })}) + name: 'jupyterlab_swan_stack:' + randomId(), + svgstr: stacks.stacks_options[item]['logo'], + }); + }); const onClickSubmit = () => { props.onSubmit({ @@ -77,8 +84,7 @@ export const ProjectWidget: React.FunctionComponent<{ release, platform, user_script: userScript, - stacks_options: options.stacks_options, - corrupted: options.corrupted + corrupted: options.corrupted, }); }; @@ -92,16 +98,19 @@ export const ProjectWidget: React.FunctionComponent<{ const onChangeStack = (newStack: string) => { setStack(newStack); - const newRelease = Object.keys(options.stacks_options[newStack]['releases'])[0]; + const newRelease = Object.keys( + stacks.stacks_options[newStack]['releases'] + )[0]; setRelease(newRelease); - const newPlatform = options.stacks_options[newStack]['releases'][newRelease][0]; + const newPlatform = + stacks.stacks_options[newStack]['releases'][newRelease][0]; setPlatform(newPlatform); }; const onChangeRelease = (event: React.ChangeEvent<{ value: unknown }>) => { const newRelease = event.target.value as string; setRelease(newRelease); - const newPlatform = options.stacks_options[stack]['releases'][newRelease][0]; + const newPlatform = stacks.stacks_options[stack]['releases'][newRelease][0]; setPlatform(newPlatform); }; @@ -130,15 +139,15 @@ export const ProjectWidget: React.FunctionComponent<{ />
- {availableStacks.map(item => ( - onChangeStack(item)} - isSelected={stack === item} - /> - ))} + {availableStacks.map((item) => ( + onChangeStack(item)} + isSelected={stack === item} + /> + ))}
diff --git a/SwanProjects/src/dialog.tsx b/SwanProjects/src/dialog.tsx index 3bbd283b..d1a9836d 100644 --- a/SwanProjects/src/dialog.tsx +++ b/SwanProjects/src/dialog.tsx @@ -17,40 +17,43 @@ import { ThemeProvider } from './theme-provider'; import { ProjectWidget } from './ProjectWidget'; import { ProjectDialog } from './ProjectDialog'; - /** - * Utility function to display the ProjectWidget. - * This function allows to create a ReactWidget to embed all the components - * and attach it to the document body. - * - * @param options - The dialog setup options. - * @param theme - colors in the interface 'light' | 'dark'. - * @returns A promise that resolves with whether the dialog was accepted - */ +/** + * Utility function to display the ProjectWidget. + * This function allows to create a ReactWidget to embed all the components + * and attach it to the document body. + * + * @param options - The dialog setup options. + * @param stacks - Available stack options. + * @param theme - colors in the interface 'light' | 'dark'. + * @returns A promise that resolves with whether the dialog was accepted + */ export async function showDialog( - options: ProjectDialog.ISWANOptions & { theme: 'light' | 'dark' } + options: ProjectDialog.ISWANOptions & { theme: 'light' | 'dark' }, + stacks: ProjectDialog.ISWANStackOptions ): Promise<{ changesSaved: boolean; newOptions?: ProjectDialog.ISWANOptions; }> { - return new Promise(resolve => { + return new Promise((resolve) => { const widget = ReactWidget.create( { + stacks={stacks} + onSubmit={(newOptions) => { Widget.detach(widget); resolve({ changesSaved: true, - newOptions + newOptions, }); }} onCancel={() => { Widget.detach(widget); resolve({ - changesSaved: false + changesSaved: false, }); }} /> diff --git a/SwanProjects/src/icons.ts b/SwanProjects/src/icons.ts index 7d8b88f9..89e511e5 100644 --- a/SwanProjects/src/icons.ts +++ b/SwanProjects/src/icons.ts @@ -10,20 +10,20 @@ import sftIconStr from '../style/sft.svg'; export const swanProjectIcon = new LabIcon({ name: 'jupyterlab_swan:project', - svgstr: swanProjectIconStr + svgstr: swanProjectIconStr, }); export const swanReadmeIcon = new LabIcon({ name: 'jupyterlab_swan:reame', - svgstr: swanReadmeIconStr + svgstr: swanReadmeIconStr, }); export const cmsIcon = new LabIcon({ name: 'jupyterlab_swan:cms', - svgstr: cmsIconStr + svgstr: cmsIconStr, }); export const sftIcon = new LabIcon({ name: 'jupyterlab_swan:sft', - svgstr: sftIconStr + svgstr: sftIconStr, }); diff --git a/SwanProjects/src/index.ts b/SwanProjects/src/index.ts index f82cc25c..2f331d0e 100644 --- a/SwanProjects/src/index.ts +++ b/SwanProjects/src/index.ts @@ -3,7 +3,7 @@ import { JupyterFrontEnd, - JupyterFrontEndPlugin + JupyterFrontEndPlugin, } from '@jupyterlab/application'; import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils'; @@ -65,7 +65,7 @@ const extension: JupyterFrontEndPlugin = { icon: swanProjectIcon, label: 'New Project', caption: 'New Project', - execute: async args => { + execute: async (args) => { const stacks = await kernelsInfoRequest(); ProjectDialog.OpenModal( { @@ -74,20 +74,22 @@ const extension: JupyterFrontEndPlugin = { release: '', platform: '', user_script: '', - stacks_options: stacks['stacks'] + }, + { + stacks_options: stacks['stacks'], }, true, commands, theme ); - } + }, }); commands.addCommand(CommandIDs.projectDialogEdit, { icon: swanProjectIcon, label: 'Edit', caption: 'Edit', - execute: async args => { + execute: async (args) => { const stacks = await kernelsInfoRequest(); ProjectDialog.OpenModal( { @@ -96,14 +98,16 @@ const extension: JupyterFrontEndPlugin = { release: args.release as string, platform: args.platform as string, user_script: args.user_script as string, + corrupted: args.corrupted as boolean, + }, + { stacks_options: stacks['stacks'], - corrupted: args.corrupted as boolean }, false, commands, theme ); - } + }, }); // Add the command to the launcher @@ -112,7 +116,7 @@ const extension: JupyterFrontEndPlugin = { command: CommandIDs.projectDialog, category: PALETTE_CATEGORY, rank: 1, - kernelIconUrl: '' + kernelIconUrl: '', }); } @@ -121,20 +125,25 @@ const extension: JupyterFrontEndPlugin = { palette.addItem({ command: CommandIDs.projectDialog, args: { isPalette: true }, - category: PALETTE_CATEGORY + category: PALETTE_CATEGORY, }); } + + // Add the command to context menu in the file browser, + // when you press right click a entry in the menu is available + // to create a new project. const command = CommandIDs.projectDialog; app.contextMenu.addItem({ command: command, rank: 0, - selector: '.jp-DirListing-content' + selector: '.jp-DirListing-content', }); + // Add the command to the main menu, when you click File->New->New Project if (mainMenu) { mainMenu.fileMenu.newMenu.addGroup([{ command: command }], 0); } - } + }, }; export default extension; diff --git a/SwanProjects/src/request.ts b/SwanProjects/src/request.ts index 2d350477..c60372a0 100644 --- a/SwanProjects/src/request.ts +++ b/SwanProjects/src/request.ts @@ -46,7 +46,7 @@ export async function request( export function contentRequest(cwd: string): any { try { return request('api/contents/' + cwd, { - method: 'GET' + method: 'GET', }); } catch (reason) { const msg = `Error on GET 'api/contents'+ ${cwd}.\n${reason}`; @@ -61,17 +61,10 @@ export function contentRequest(cwd: string): any { * @returns json object with the keys 'project_dir' and 'msg' or json object with the information of the error. */ export function createProjectRequest(options: ProjectDialog.ISWANOptions): any { - const dataToSend = { - name: options.name, - stack: options.stack, - release: options.release, - platform: options.platform, - user_script: options.user_script - }; try { return request('swan/project/create', { - body: JSON.stringify(dataToSend), - method: 'POST' + body: JSON.stringify(options), + method: 'POST', }); } catch (reason) { const msg = `Error on POST /swan/project/create ${options}.\n${reason}`; @@ -96,17 +89,12 @@ export function editProjectRequest( old_release: old_options.release, old_platform: old_options.platform, old_userscript: old_options.user_script, - name: options.name, - stack: options.stack, - release: options.release, - platform: options.platform, - user_script: options.user_script, - corrupted: options.corrupted + ...options, }; try { return request('swan/project/edit', { body: JSON.stringify(dataToSend), - method: 'PUT' + method: 'PUT', }); } catch (reason) { const msg = `Error on PUT swan/project/edit ${options}.\n${reason}`; @@ -122,7 +110,7 @@ export function editProjectRequest( export function kernelsInfoRequest(): any { try { return request('swan/stacks/info', { - method: 'GET' + method: 'GET', }); } catch (reason) { console.error(`Error on GET 'swan/stacks/info'.\n${reason}`); diff --git a/SwanProjects/src/theme-provider.tsx b/SwanProjects/src/theme-provider.tsx index 10f1468f..00d1efc2 100644 --- a/SwanProjects/src/theme-provider.tsx +++ b/SwanProjects/src/theme-provider.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { createTheme, - ThemeProvider as MaterialUIThemeProvider + ThemeProvider as MaterialUIThemeProvider, } from '@material-ui/core/styles'; const createLightorDarkTheme = (color: 'light' | 'dark') => { return createTheme({ shadows: Array(25).fill('none') as any, shape: { - borderRadius: 2 + borderRadius: 2, }, typography: { fontSize: 12, @@ -22,15 +22,15 @@ const createLightorDarkTheme = (color: 'light' | 'dark') => { 'sans-serif', '"Apple Color Emoji"', '"Segoe UI Emoji"', - '"Segoe UI Symbol"' - ].join(',') + '"Segoe UI Symbol"', + ].join(','), }, palette: { type: color || 'light', primary: { - main: '#0153a1' - } - } + main: '#0153a1', + }, + }, }); }; @@ -39,7 +39,7 @@ const darkTheme = createLightorDarkTheme('dark'); export const ThemeProvider: React.FunctionComponent<{ theme: 'light' | 'dark'; -}> = props => { +}> = (props) => { const currentTheme = props.theme === 'light' ? lightTheme : darkTheme; return ( From 624ce696c9eed9593136b26bc4b0e5286f7b4146 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 2 Nov 2021 15:51:31 +0100 Subject: [PATCH 22/35] removed unneeded icons for SwanProjects --- SwanProjects/src/icons.ts | 18 - SwanProjects/style/cms.svg | 239 ------- SwanProjects/style/list-alt.svg | 1 - SwanProjects/style/sft.svg | 1190 ------------------------------- 4 files changed, 1448 deletions(-) delete mode 100644 SwanProjects/style/cms.svg delete mode 100644 SwanProjects/style/list-alt.svg delete mode 100644 SwanProjects/style/sft.svg diff --git a/SwanProjects/src/icons.ts b/SwanProjects/src/icons.ts index 89e511e5..04cb3b20 100644 --- a/SwanProjects/src/icons.ts +++ b/SwanProjects/src/icons.ts @@ -4,26 +4,8 @@ import { LabIcon } from '@jupyterlab/ui-components'; import swanProjectIconStr from '../style/project.svg'; -import swanReadmeIconStr from '../style/list-alt.svg'; -import cmsIconStr from '../style/cms.svg'; -import sftIconStr from '../style/sft.svg'; export const swanProjectIcon = new LabIcon({ name: 'jupyterlab_swan:project', svgstr: swanProjectIconStr, }); - -export const swanReadmeIcon = new LabIcon({ - name: 'jupyterlab_swan:reame', - svgstr: swanReadmeIconStr, -}); - -export const cmsIcon = new LabIcon({ - name: 'jupyterlab_swan:cms', - svgstr: cmsIconStr, -}); - -export const sftIcon = new LabIcon({ - name: 'jupyterlab_swan:sft', - svgstr: sftIconStr, -}); diff --git a/SwanProjects/style/cms.svg b/SwanProjects/style/cms.svg deleted file mode 100644 index 1e40f126..00000000 --- a/SwanProjects/style/cms.svg +++ /dev/null @@ -1,239 +0,0 @@ - - - - diff --git a/SwanProjects/style/list-alt.svg b/SwanProjects/style/list-alt.svg deleted file mode 100644 index 1113bbcc..00000000 --- a/SwanProjects/style/list-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/SwanProjects/style/sft.svg b/SwanProjects/style/sft.svg deleted file mode 100644 index c2721aa2..00000000 --- a/SwanProjects/style/sft.svg +++ /dev/null @@ -1,1190 +0,0 @@ - - - - From 0861e56e7bff2d39fe50852cc00931c0b3ff2450 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 2 Nov 2021 18:37:37 +0100 Subject: [PATCH 23/35] -> removed hardcoded project path in swan_env, instead get the name of the project it's required the project. -> passing the full path to the scripts required a modification in SwanLauncher to pass the full path to the Terminal, for this I added a new entry in project info called full_path. -> removed unneeded code and improved the documentation for kernelspecmanager. --- SwanProjects/bin/swan_bash | 10 +-- SwanProjects/bin/swan_env | 8 +- SwanProjects/setup.py | 2 - SwanProjects/swanprojects/config.py | 2 +- SwanProjects/swanprojects/handlers.py | 13 +-- .../kernelmanager/kernelspecmanager.py | 86 +++++++++++++++---- 6 files changed, 86 insertions(+), 35 deletions(-) diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index 0978c420..6b4093fc 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -10,7 +10,7 @@ # # * HOME with path to the user home # * PATH with default paths for the system -# * OAUTH2_TOKEN and OAUTH_INSPECTION_ENDPOINT required by EOS storage +# * OAUTH2_TOKEN required by EOS storage # # swan_env will load the other variables from the stack, inside the isolated enviroment. ### @@ -22,8 +22,8 @@ if ! [ -x "$(command -v jq)" ]; then exit 1 fi if [[ $# -gt 1 ]] ; then - PROJECT=$1 - PROJECT_PATH="$HOME/SWAN_projects/$PROJECT" + PROJECT_PATH=$1 + PROJECT_NAME=`IFS='/'; ARR=($PROJECT_PATH);echo "${ARR[-1]}"` PROJECT_FILE="$PROJECT_PATH/.swanproject" STACKS_PATH="$2" @@ -38,9 +38,9 @@ if [[ $# -gt 1 ]] ; then # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary env -i HOME=$HOME \ OAUTH2_TOKEN=$OAUTH2_TOKEN \ - PROJECT=$PROJECT \ + PROJECT=$PROJECT_NAME \ PROJECT_PATH=$PROJECT_PATH PS1="$PS1" \ - bash -c "swan_env $PROJECT $STACKS_PATH $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT) $PS1 \"') " + bash -c "swan_env $PROJECT_PATH $STACKS_PATH $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT_NAME) $PS1 \"') " else echo "Error: project $PROJECT_PATH doesn't exist" >&2 # JupyterLab closes the terminal window immediately after the process ends diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index 6a02e553..f73d7566 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -41,8 +41,8 @@ fi # stack, release and platform. # I also create the path to use bash user script to source it. ### -PROJECT=$1 -PROJECT_PATH="$HOME/SWAN_projects/$PROJECT" +PROJECT_PATH=$1 +PROJECT_NAME=`IFS='/'; ARR=($PROJECT_PATH);echo "${ARR[-1]}"` PROJECT_FILE="$PROJECT_PATH/.swanproject" STACK=`jq -r '.stack' $PROJECT_FILE` RELEASE=`jq -r '.release' $PROJECT_FILE` @@ -71,7 +71,7 @@ COMMAND=${PARAMETERS[@]} if [ ! -d $STACK_PATH ]; then echo "Error sourcing the environment, the stack $STACK was not found in stacks path $STACKS_PATH" >&2 - echo "project $PROJECT can not be loeaded." >&2 + echo "project $PROJECT_NAME can not be loeaded." >&2 exit 1 fi @@ -84,7 +84,7 @@ fi # The next variables allows the user to know in the environment basic information about the project such as stack, name and path. export SWAN_STACK="$RELEASE($PLATFORM)" -export SWAN_PROJECT_NAME=$PROJECT +export SWAN_PROJECT_NAME=$PROJECT_NAME export SWAN_PROJECT_PATH=$PROJECT_PATH if [ "$USER_SCRIPT" != "" ] && [ -f "$USER_SCRIPT" ]; then diff --git a/SwanProjects/setup.py b/SwanProjects/setup.py index 1f425bc2..386f0e81 100644 --- a/SwanProjects/setup.py +++ b/SwanProjects/setup.py @@ -1,8 +1,6 @@ """ swanprojects setup """ -import site -import sys import json from pathlib import Path diff --git a/SwanProjects/swanprojects/config.py b/SwanProjects/swanprojects/config.py index 7b7ba042..e77b60c5 100644 --- a/SwanProjects/swanprojects/config.py +++ b/SwanProjects/swanprojects/config.py @@ -12,7 +12,7 @@ class SwanConfig(Configurable): """ - Class to parse configuration options from command line. + Class to parse configuration options from command line. """ stacks_path = Unicode( os.path.dirname(os.path.abspath(__file__)) + '/stacks', diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index d1300b65..2db4ba45 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -19,7 +19,6 @@ from swanprojects.utils import SwanUtils from swanprojects.config import SwanConfig -from traitlets import Any class SwanAPIHandler(APIHandler): @@ -83,8 +82,9 @@ def save_project_file(self, project_dir, content): project_dir, self.swan_config.project_file_name) try: - self.contents_manager.new({'type': 'file', 'content': json.dumps(content, - indent=4, sort_keys=True), 'format': 'text'}, project_file) + self.contents_manager.new({'type': 'file', + 'content': json.dumps(content, + indent=4, sort_keys=True), 'format': 'text'}, project_file) return True except Exception as msg: data = {"status": False, "project_dir": project_dir, @@ -108,7 +108,7 @@ def get_kmanager_info(self, project_dir): """ name = project_dir.split(os.sep)[-1] swan_kmspecs = find_executable("swan_kmspecs") - command = ["swan_env", name, self.swan_config.stacks_path, + command = ["swan_env", project_dir, self.swan_config.stacks_path, ".", "python", swan_kmspecs] stdout, stderr, returncode = self.subprocess(command) self.log.info(f"swan_kmspecs return code: {returncode}") @@ -140,6 +140,9 @@ def get(self): project_data["name"] = project.split(os.path.sep)[-1] project_data["user_script"] = self.swan_utils.get_user_script_content( project) + project_data["full_path"] = os.path.join( + self.contents_manager.root_dir, project) + payload = {"project_data": project_data} self.finish(json.dumps(payload)) @@ -302,8 +305,6 @@ def put(self): return if stack != old_stack or platform != old_platform or release != old_release or corrupted: - swan_project_file = os.path.join( - project_relative_dir, self.swan_config.project_file_name) swan_project_content = {'stack': stack, 'release': release, 'platform': platform} if not self.save_project_file(project_relative_dir, swan_project_content): diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index ac05f6a5..f7730c56 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -11,12 +11,13 @@ from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel from swanprojects.config import SwanConfig -from traitlets import Unicode class SwanKernelSpecManager(KernelSpecManager): - path = Unicode("", config=True, allow_none=True, - help="SWAN Project path") + """ + This class allows to wrap the kernels in the specs to run the kernel + in multiple environments. + """ def __init__(self, **kwargs): super(SwanKernelSpecManager, self).__init__(**kwargs) @@ -25,14 +26,36 @@ def __init__(self, **kwargs): self.kernel_dirs = [] self.swan_config = SwanConfig(config=self.config) self.swan_utils = None + self.path = "" def set_swan_utils(self, swan_utils): + """ + Method to set the a SwanUtils class object. + The class SwanUtils is instatiated in the SwanAPIHandler because it + requires the contents manager and this class requires those methods to get information + about the projects. + + Parameters + ---------- + swan_utils : object + SwanUtils object required to get projects information. + + """ self.swan_utils = swan_utils def save_native_spec(self, kernel_dir, python_path, display_name): """ This function creates a default kernel with the info from the stack. - It's necessary for CMSSW stacks and those that don't provide a Python kernel as a JSON file. + It's necessary for those stacks that don't provide a Python kernel as a JSON file. + + Parameters + ---------- + kernel_dir : string + Path to the folder to save the native kernel. + python_path : string + Path to the python required for this kernel. + display_name : string + Name for the kernel. """ self.log.info( f"copying resources from {self.swan_config.kernel_resources} to {kernel_dir}") @@ -51,6 +74,16 @@ def save_native_spec(self, kernel_dir, python_path, display_name): spec, indent=4), 'format': 'text'}, kernel_file) def set_path(self, path): + """ + This method get a path and check if we are inside the project, if so, then + the kernel paths for that project is set, otherwise kernel dirs is set to an empty + list and the kernel are no available anymore for the system. + + Parameters + ---------- + path : string + Path to a folder in the SwanFileBrowser. + """ self.path = path self.project = self.swan_utils.get_project_path(path) if self.project is None: @@ -70,7 +103,8 @@ def set_path(self, path): if not self.swan_utils.contents_manager.dir_exists(kerne_dir): self.save_native_spec( kerne_dir, self.project_info[python]["path"], "Python " + version) - self.kernel_dirs.append(os.path.join(self.swan_utils.contents_manager.root_dir,local_kernels)) + self.kernel_dirs.append(os.path.join( + self.swan_utils.contents_manager.root_dir, local_kernels)) self.log.debug(f"KERNEL DIRS = {self.kernel_dirs}") self.log.debug(f"specs:\n {self.get_all_specs()}") return True @@ -80,35 +114,53 @@ def set_path(self, path): self.kernel_dirs = [] return False - def wrap_kernel_specs(self, project_name, kspec): + def wrap_kernel_specs(self, project_path, kspec): + """ + Helps to wrap the kernels with an extra command to run the kernel inside the project's environment. + + Parameters + ---------- + project_path : string + Peth to a project folder. + kspec : dict + Kernel spec to wrap + Returns + ------- + kspec: dict + wrapped kernel spec with the required information to run inside the enviroment of the project. + """ + print(f"project = {self.project}") argv = self.swan_utils.get_env_isolated() argv += ["/bin/bash", "-c", "swan_env {} {} {} ".format( - project_name, self.swan_config.stacks_path, ".") + "'" + " ".join(kspec.argv) + "'" + os.path.join( + self.swan_utils.contents_manager.root_dir, project_path), + self.swan_config.stacks_path, ".") + "'" + " ".join(kspec.argv) + "'" ] kspec.argv = argv return kspec - def find_kernel_specs(self, skip_base=True): - """ Returns a dict mapping kernel names to resource directories. - The update process also adds the resource dir for the SWAN - environments. - """ - kspecs = super(SwanKernelSpecManager, self).find_kernel_specs() - return kspecs - def get_kernel_spec(self, kernel_name): """ Returns a :class:`KernelSpec` instance for the given kernel_name. Also, SWAN kernelspecs are generated on the fly according to the detected environments. + + Parameters + ---------- + kernel_name : string + name of the kernel to check + Returns + ------- + kspec: dict + Spec for the given kernel_name """ kspec = super(SwanKernelSpecManager, self).get_kernel_spec(kernel_name) if self.project is None: return kspec else: - kspec = self.wrap_kernel_specs(self.project_name, kspec) - self.log.debug(f"ON get_kernel_spec = {kspec.argv}") + kspec = self.wrap_kernel_specs(self.project, kspec) + self.log.info(f"ON get_kernel_spec = {kspec.argv}") return kspec From 7ed1f845849fde52c9d62e458f3f064737f47b89 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Wed, 3 Nov 2021 15:20:37 +0100 Subject: [PATCH 24/35] -> removed stacks.json file, it is not needed anymore -> change create by isNewProject is ProjectDialog to be more clear --- SwanProjects/src/ProjectDialog.tsx | 8 +-- .../kernelmanager/kernelspecmanager.py | 2 +- SwanProjects/swanprojects/stacks.json | 50 ------------------- 3 files changed, 5 insertions(+), 55 deletions(-) delete mode 100644 SwanProjects/swanprojects/stacks.json diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 18d9fecd..cde4f3ed 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -54,7 +54,7 @@ export namespace ProjectDialog { * Create and show a modal dialog to create or modify projects. * * @param options - The dialog setup options. - * @param create - true for a new project, false to modify. + * @param isNewProject - true for a new project, false to modify. * @param commands - CommandRegistry object * @param theme - colors in the interface 'light' | 'dark'. * @returns A promise that resolves with the dialog results @@ -63,7 +63,7 @@ export namespace ProjectDialog { export async function OpenModal( options: ISWANOptions, stacks: ISWANStackOptions, - create: boolean, + isNewProject: boolean, commands: CommandRegistry, theme: 'light' | 'dark' ): Promise { @@ -106,7 +106,7 @@ export namespace ProjectDialog { options = dialogResult.newOptions; if (options.name?.trim() !== '') { //check if project already exists - if (create) { + if (isNewProject) { const content = await contentRequest( 'SWAN_projects/' + options.name ).catch((): void => { @@ -171,7 +171,7 @@ export namespace ProjectDialog { } while (!valid); if (dialogResult.changesSaved) { startSpinner(); - if (create) { + if (isNewProject) { await createProjectRequest(options) .then((res: ISWANReqResponse) => { if (res.status) { diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index f7730c56..d771daee 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -160,7 +160,7 @@ def get_kernel_spec(self, kernel_name): return kspec else: kspec = self.wrap_kernel_specs(self.project, kspec) - self.log.info(f"ON get_kernel_spec = {kspec.argv}") + self.log.debug(f"ON get_kernel_spec = {kspec.argv}") return kspec diff --git a/SwanProjects/swanprojects/stacks.json b/SwanProjects/swanprojects/stacks.json deleted file mode 100644 index bbe3a703..00000000 --- a/SwanProjects/swanprojects/stacks.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "LCG":{ - "LCG_99": [ - "x86_64-centos7-gcc8-opt", - "x86_64-centos7-gcc10-opt", - "x86_64-ubuntu2004-gcc9-opt" - ], - "LCG_99python2": [ - "x86_64-centos7-gcc8-opt", - "x86_64-centos7-gcc9-opt" - ], - "LCG_97a": [ - "x86_64-centos7-gcc8-opt" - ], - "LCG_97apython3": [ - "x86_64-centos7-gcc8-opt" - ], - "LCG_96": [ - "x86_64-centos7-gcc8-opt" - ], - "LCG_96python3": [ - "x86_64-centos7-gcc8-opt" - ], - "LCG_95a": [ - "x86_64-centos7-gcc8-opt" - ], - "LCG_95apython3": [ - "x86_64-centos7-gcc8-opt" - ], - "LCG_95apython3_nxcals": [ - "x86_64-centos7-gcc7-opt" - ]}, - "CMSSW":{ - "CMSSW_11_1_1": [ - "slc7_amd64_gcc820" - ], - "CMSSW_11_1_2": [ - "slc7_amd64_gcc820" - ], - "CMSSW_11_1_3": [ - "slc7_amd64_gcc820" - ], - "CMSSW_11_1_4": [ - "slc7_amd64_gcc820" - ], - "CMSSW_11_1_5": [ - "slc7_amd64_gcc820" - ] - } -} \ No newline at end of file From 3abc7cc1b9f342c391be74192dd4afa7d977f090 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Wed, 3 Nov 2021 16:18:18 +0100 Subject: [PATCH 25/35] -> removed duplicate code in ProjectDialog -> removed unneeded print in the kernel spec manager --- SwanProjects/src/ProjectDialog.tsx | 62 +++++++++---------- .../kernelmanager/kernelspecmanager.py | 1 - 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index cde4f3ed..a87b4f13 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -89,6 +89,32 @@ export namespace ProjectDialog { _spinner.hide(); } + /** + * Check if the name of the project is valid, + * to create a new one or when you want to edit the name. + */ + async function isValidProjectName(project_name: string): Promise { + let valid = false; + try { + // FIXME: requires full path to the project when porject can be anywhere + const content = await contentRequest('SWAN_projects/' + options.name); + if (content === undefined) { + valid = true; + } + } catch (error) { + // No message here, it is not needed, + //I am checking if the directory doesn't exist in order + //to make valid the creation of the project folder. + } + if (!valid) { + await showErrorMessage( + 'Invalid project name', + 'File or directory already exists with the same name.' + ); + } + return valid; + } + let valid = false; let dialogResult: { changesSaved: boolean; @@ -107,44 +133,15 @@ export namespace ProjectDialog { if (options.name?.trim() !== '') { //check if project already exists if (isNewProject) { - const content = await contentRequest( - 'SWAN_projects/' + options.name - ).catch((): void => { - // No message here, it is not needed, - //I am checking if the directory doesn't exist in order - //to make valid the creation of the project folder. - }); - if (content === undefined) { - valid = true; - } else { - await showErrorMessage( - 'Invalid project name', - 'Project already exists.' - ); - valid = false; - } + valid = await isValidProjectName(options.name); } else { //this is a special case for editing because I need to check that the new name of the project doesn't exists. if (old_options.name !== options.name) { - const content = await contentRequest( - 'SWAN_projects/' + options.name - ).catch(() => { - // No message here, it is not needed, - //I am checking if the directory doesn't exist in order - //to make valid the edition of the name of the project folder. - }); - if (content === undefined) { - valid = true; //folder doesn't exists, then I can to raname the project. - } else { - await showErrorMessage( - 'Invalid project name', - 'Project already exists.' - ); - valid = false; + valid = await isValidProjectName(options.name); + if (!valid) { continue; } } - if (options.corrupted) { valid = true; break; @@ -157,7 +154,6 @@ export namespace ProjectDialog { } } } - if (options.name?.trim() === '') { await showErrorMessage( 'Invalid project name', diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index d771daee..d05fbefc 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -130,7 +130,6 @@ def wrap_kernel_specs(self, project_path, kspec): kspec: dict wrapped kernel spec with the required information to run inside the enviroment of the project. """ - print(f"project = {self.project}") argv = self.swan_utils.get_env_isolated() argv += ["/bin/bash", "-c", "swan_env {} {} {} ".format( os.path.join( From f449a194cc10822c1f276d93f78a0365b8179bf9 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 4 Nov 2021 11:04:31 +0100 Subject: [PATCH 26/35] -> Fixed isValidProjectName function on the file ProjectDialog -> improved reuqest kernelsInfoRequest and simplified message for the user --- SwanProjects/src/ProjectDialog.tsx | 25 +++++++++++-------------- SwanProjects/src/index.ts | 18 +++++++++++++++--- SwanProjects/src/request.ts | 10 +++++++--- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index a87b4f13..8303053b 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -94,25 +94,23 @@ export namespace ProjectDialog { * to create a new one or when you want to edit the name. */ async function isValidProjectName(project_name: string): Promise { - let valid = false; + let content = undefined; try { // FIXME: requires full path to the project when porject can be anywhere - const content = await contentRequest('SWAN_projects/' + options.name); - if (content === undefined) { - valid = true; - } + content = await contentRequest('SWAN_projects/' + project_name); } catch (error) { // No message here, it is not needed, //I am checking if the directory doesn't exist in order //to make valid the creation of the project folder. } - if (!valid) { - await showErrorMessage( - 'Invalid project name', - 'File or directory already exists with the same name.' - ); + if (content === undefined) { + return true; } - return valid; + await showErrorMessage( + 'Invalid project name', + 'File or directory already exists with the same name.' + ); + return false; } let valid = false; @@ -134,6 +132,7 @@ export namespace ProjectDialog { //check if project already exists if (isNewProject) { valid = await isValidProjectName(options.name); + console.log(valid); } else { //this is a special case for editing because I need to check that the new name of the project doesn't exists. if (old_options.name !== options.name) { @@ -202,9 +201,7 @@ export namespace ProjectDialog { }) .catch((msg: any) => { stopSpinner(); - console.log( - 'Error moving from edited project ' + old_options.name - ); + console.log('Error opening project ' + old_options.name); console.log(msg); }); } else { diff --git a/SwanProjects/src/index.ts b/SwanProjects/src/index.ts index 2f331d0e..cf501b70 100644 --- a/SwanProjects/src/index.ts +++ b/SwanProjects/src/index.ts @@ -6,7 +6,11 @@ import { JupyterFrontEndPlugin, } from '@jupyterlab/application'; -import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils'; +import { + ICommandPalette, + IThemeManager, + showErrorMessage, +} from '@jupyterlab/apputils'; import { swanProjectIcon } from './icons'; const PALETTE_CATEGORY = 'Project'; @@ -67,6 +71,10 @@ const extension: JupyterFrontEndPlugin = { caption: 'New Project', execute: async (args) => { const stacks = await kernelsInfoRequest(); + if (!stacks.status) { + showErrorMessage("Error getting stacks information: ", stacks.msg); + return; + } ProjectDialog.OpenModal( { name: '', @@ -76,7 +84,7 @@ const extension: JupyterFrontEndPlugin = { user_script: '', }, { - stacks_options: stacks['stacks'], + stacks_options: stacks['content']['stacks'], }, true, commands, @@ -91,6 +99,10 @@ const extension: JupyterFrontEndPlugin = { caption: 'Edit', execute: async (args) => { const stacks = await kernelsInfoRequest(); + if (!stacks.status) { + showErrorMessage("Error getting stacks information: ", stacks.msg); + return; + } ProjectDialog.OpenModal( { name: args.name as string, @@ -101,7 +113,7 @@ const extension: JupyterFrontEndPlugin = { corrupted: args.corrupted as boolean, }, { - stacks_options: stacks['stacks'], + stacks_options: stacks['content']['stacks'], }, false, commands, diff --git a/SwanProjects/src/request.ts b/SwanProjects/src/request.ts index c60372a0..774b7e2d 100644 --- a/SwanProjects/src/request.ts +++ b/SwanProjects/src/request.ts @@ -107,12 +107,16 @@ export function editProjectRequest( * * @returns json with the software stack names, releases, platform, etc.. */ -export function kernelsInfoRequest(): any { +export async function kernelsInfoRequest(): Promise { try { - return request('swan/stacks/info', { + const content = await request('swan/stacks/info', { method: 'GET', }); + return { status: true, content: content }; } catch (reason) { - console.error(`Error on GET 'swan/stacks/info'.\n${reason}`); + const msg = "It was not possible to obtain the information of the stacks."; + const req = { status: false, reason: reason, param: {}, msg: msg }; + console.log(req); + return req; } } From 9683f5a91d221472d29c50dd3f555219510f4b3a Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 4 Nov 2021 15:30:36 +0100 Subject: [PATCH 27/35] -> simplified error messages for users in the requests -> removed unwanted console.log on ProjectDialog --- SwanProjects/src/ProjectDialog.tsx | 1 - SwanProjects/src/request.ts | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 8303053b..33fec8a5 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -132,7 +132,6 @@ export namespace ProjectDialog { //check if project already exists if (isNewProject) { valid = await isValidProjectName(options.name); - console.log(valid); } else { //this is a special case for editing because I need to check that the new name of the project doesn't exists. if (old_options.name !== options.name) { diff --git a/SwanProjects/src/request.ts b/SwanProjects/src/request.ts index 774b7e2d..9cba5b24 100644 --- a/SwanProjects/src/request.ts +++ b/SwanProjects/src/request.ts @@ -49,8 +49,10 @@ export function contentRequest(cwd: string): any { method: 'GET', }); } catch (reason) { - const msg = `Error on GET 'api/contents'+ ${cwd}.\n${reason}`; - return { status: 'error', reason: reason, param: cwd, msg: msg }; + const msg = `Error gettig information for ${cwd}`; + const req = { status: false, reason: reason, param: cwd, msg: msg }; + console.log(req); + return req; } } @@ -67,8 +69,10 @@ export function createProjectRequest(options: ProjectDialog.ISWANOptions): any { method: 'POST', }); } catch (reason) { - const msg = `Error on POST /swan/project/create ${options}.\n${reason}`; - return { status: 'error', reason: reason, param: options, msg: msg }; + const msg = "It was not possible to create the project."; + const req = { status: false, reason: reason, param: options, msg: msg }; + console.log(req); + return req; } } @@ -97,8 +101,10 @@ export function editProjectRequest( method: 'PUT', }); } catch (reason) { - const msg = `Error on PUT swan/project/edit ${options}.\n${reason}`; - return { status: 'error', reason: reason, param: options, msg: msg }; + const msg = "It was not possible to edit the project."; + const req = { status: false, reason: reason, param: options, msg: msg }; + console.log(req); + return req; } } From 6fc7185fc7835eba7e7bd7b1f853a10a17831fde Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Thu, 4 Nov 2021 16:21:33 +0100 Subject: [PATCH 28/35] the validation of the name of the project in in the ProjectDialog, was moved to ProjectWidget. The error message is displayed below the textbox --- SwanProjects/src/ProjectDialog.tsx | 86 ++++-------------------------- SwanProjects/src/ProjectWidget.tsx | 45 +++++++++++++--- 2 files changed, 49 insertions(+), 82 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 33fec8a5..5e83532e 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -12,11 +12,7 @@ import { showErrorMessage } from '@jupyterlab/apputils'; import { showDialog } from './dialog'; -import { - contentRequest, - createProjectRequest, - editProjectRequest, -} from './request'; +import { createProjectRequest, editProjectRequest } from './request'; import { Spinner } from '@jupyterlab/apputils'; import { CommandRegistry } from '@lumino/commands'; /** @@ -89,80 +85,20 @@ export namespace ProjectDialog { _spinner.hide(); } - /** - * Check if the name of the project is valid, - * to create a new one or when you want to edit the name. - */ - async function isValidProjectName(project_name: string): Promise { - let content = undefined; - try { - // FIXME: requires full path to the project when porject can be anywhere - content = await contentRequest('SWAN_projects/' + project_name); - } catch (error) { - // No message here, it is not needed, - //I am checking if the directory doesn't exist in order - //to make valid the creation of the project folder. - } - if (content === undefined) { - return true; - } - await showErrorMessage( - 'Invalid project name', - 'File or directory already exists with the same name.' - ); - return false; - } - - let valid = false; let dialogResult: { changesSaved: boolean; newOptions?: ISWANOptions; } | null = null; - do { - dialogResult = await showDialog( - { - ...options, - theme, - }, - { ...stacks } - ); - if (dialogResult?.changesSaved && dialogResult?.newOptions) { - options = dialogResult.newOptions; - if (options.name?.trim() !== '') { - //check if project already exists - if (isNewProject) { - valid = await isValidProjectName(options.name); - } else { - //this is a special case for editing because I need to check that the new name of the project doesn't exists. - if (old_options.name !== options.name) { - valid = await isValidProjectName(options.name); - if (!valid) { - continue; - } - } - if (options.corrupted) { - valid = true; - break; - } - // verifying that options changed, otherwise I will not send the request - if (JSON.stringify(old_options) !== JSON.stringify(options)) { - valid = true; - } else { - valid = false; - } - } - } - if (options.name?.trim() === '') { - await showErrorMessage( - 'Invalid project name', - 'Select a valid (non-empty) project name.' - ); - valid = false; - } - } else { - valid = true; - } - } while (!valid); + dialogResult = await showDialog( + { + ...options, + theme, + }, + { ...stacks } + ); + if (dialogResult?.changesSaved && dialogResult?.newOptions) { + options = dialogResult.newOptions; + } if (dialogResult.changesSaved) { startSpinner(); if (isNewProject) { diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 8213b045..630fa5ac 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -21,6 +21,8 @@ import { LabIcon } from '@jupyterlab/ui-components'; import { ProjectDialog } from './ProjectDialog'; +import { contentRequest } from './request'; + /** * Functio to create the widget required for the modal dialog, it is basically a form, * also it has the callbacks to handle the events. @@ -40,6 +42,7 @@ export const ProjectWidget: React.FunctionComponent<{ const options = props.options; const stacks = props.stacks; const [projectName, setProjectName] = React.useState(options.name || ''); + const [helperText, setHelperText] = React.useState(''); const availableStacks = Object.keys(stacks.stacks_options).filter((e) => { return e !== "path"; @@ -77,14 +80,41 @@ export const ProjectWidget: React.FunctionComponent<{ }); }); + async function checkProjectName(project_name: string): Promise { + let content = undefined; + try { + // FIXME: requires full path to the project when porject can be anywhere + content = await contentRequest('SWAN_projects/' + project_name); + } catch (error) { + // No message here, it is not needed, + //I am checking if the directory doesn't exist in order + //to make valid the creation of the project folder. + } + if (content === undefined) { + return true; + } + return false; + } + const onClickSubmit = () => { - props.onSubmit({ - name: projectName, - stack, - release, - platform, - user_script: userScript, - corrupted: options.corrupted, + if (projectName.trim() === '') { + setHelperText('Select a valid (non-empty) project name.'); + return; + } + checkProjectName(projectName).then((valid: boolean) => { + if (valid) { + props.onSubmit({ + name: projectName, + stack, + release, + platform, + user_script: userScript, + corrupted: options.corrupted, + }); + } else { + setHelperText('File or directory already exists with the same name.'); + return; + } }); }; @@ -129,6 +159,7 @@ export const ProjectWidget: React.FunctionComponent<{
Date: Fri, 5 Nov 2021 10:00:35 +0100 Subject: [PATCH 29/35] improved message error style when project name is not valid for ProjectWidget --- SwanProjects/src/ProjectWidget.tsx | 8 ++++---- SwanProjects/style/base.css | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 630fa5ac..f24372a9 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -42,7 +42,7 @@ export const ProjectWidget: React.FunctionComponent<{ const options = props.options; const stacks = props.stacks; const [projectName, setProjectName] = React.useState(options.name || ''); - const [helperText, setHelperText] = React.useState(''); + const [helperText, setHelperText] = React.useState({helperText:'',error:false}); const availableStacks = Object.keys(stacks.stacks_options).filter((e) => { return e !== "path"; @@ -98,7 +98,7 @@ export const ProjectWidget: React.FunctionComponent<{ const onClickSubmit = () => { if (projectName.trim() === '') { - setHelperText('Select a valid (non-empty) project name.'); + setHelperText({helperText:'Select a valid (non-empty) project name.', error:true}); return; } checkProjectName(projectName).then((valid: boolean) => { @@ -112,7 +112,7 @@ export const ProjectWidget: React.FunctionComponent<{ corrupted: options.corrupted, }); } else { - setHelperText('File or directory already exists with the same name.'); + setHelperText({helperText:'File or directory already exists with the same name.', error:true}); return; } }); @@ -159,7 +159,7 @@ export const ProjectWidget: React.FunctionComponent<{
Date: Fri, 5 Nov 2021 11:23:37 +0100 Subject: [PATCH 30/35] -> fixed edit project when checking if directory exists -> simplified error messages for users, the traceback is printed in the console for developers or advanced users. --- SwanProjects/src/ProjectDialog.tsx | 110 ++++++++++++++++++----------- SwanProjects/src/ProjectWidget.tsx | 54 +++++++++----- 2 files changed, 107 insertions(+), 57 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 5e83532e..d10011ee 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -85,6 +85,46 @@ export namespace ProjectDialog { _spinner.hide(); } + async function editProject( + old_options: ISWANOptions, + options: ISWANOptions + ): Promise { + await editProjectRequest(old_options, options) + .then(async (res: ISWANReqResponse) => { + if (res.status) { + await commands + .execute('filebrowser:go-to-path', { + path: res.project_dir, + showBrowser: false, + }) + .catch((msg: any) => { + stopSpinner(); + showErrorMessage( + 'Error opening project: ' + old_options.name, + 'It is not prossible go to the edited project folder.' + ); + console.log(msg); + }); + } else { + stopSpinner(); + showErrorMessage( + 'Error editing project: ' + old_options.name, + 'Internal error editing the project.' + ); + console.log(res); + } + return res; + }) + .catch((msg: any) => { + stopSpinner(); + showErrorMessage( + 'Error editing project' + old_options.name, + 'Request to edit the project failed.' + ); + console.log(msg); + }); + } + let dialogResult: { changesSaved: boolean; newOptions?: ISWANOptions; @@ -111,54 +151,44 @@ export namespace ProjectDialog { }); } else { stopSpinner(); - showErrorMessage('Error creating project', res.msg); + showErrorMessage( + 'Error creating project: ' + options.name, + 'Internal error creating the project.' + ); + console.log(res); } return res; }) .catch((msg: any): void => { stopSpinner(); - showErrorMessage('Error creating project', msg); - }); - } else { - await commands - .execute('filebrowser:go-to-path', { - path: '/SWAN_projects', - showBrowser: false, - }) - .then(async () => { - await editProjectRequest(old_options, options) - .then(async (res: ISWANReqResponse) => { - if (res.status) { - await commands - .execute('filebrowser:go-to-path', { - path: res.project_dir, - showBrowser: false, - }) - .catch((msg: any) => { - stopSpinner(); - console.log('Error opening project ' + old_options.name); - console.log(msg); - }); - } else { - stopSpinner(); - showErrorMessage('Error editing project', res.msg); - } - return res; - }) - .catch((msg: any) => { - stopSpinner(); - console.log('Error editing project: ' + old_options.name); - console.log(msg); - }); - }) - .catch((msg: any) => { - stopSpinner(); - console.log( - 'Error moving to /SWAN_projects to edit the project: ' + - old_options.name + showErrorMessage( + 'Error creating project', + 'Request to create the project failed.' ); console.log(msg); }); + } else { + if (old_options.name != options.name) { + await commands + .execute('filebrowser:go-to-path', { + path: '/SWAN_projects', + showBrowser: false, + }) + .then(async () => { + await editProject(old_options, options); + }) + .catch((msg: any) => { + stopSpinner(); + showErrorMessage( + 'Error moving to /SWAN_projects to edit the project: ' + + old_options.name, + msg + ); + console.log(msg); + }); + } else { + await editProject(old_options, options); + } } stopSpinner(); } diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index f24372a9..d60966a1 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -42,7 +42,10 @@ export const ProjectWidget: React.FunctionComponent<{ const options = props.options; const stacks = props.stacks; const [projectName, setProjectName] = React.useState(options.name || ''); - const [helperText, setHelperText] = React.useState({helperText:'',error:false}); + const [helperText, setHelperText] = React.useState({ + helperText: '', + error: false, + }); const availableStacks = Object.keys(stacks.stacks_options).filter((e) => { return e !== "path"; @@ -98,24 +101,41 @@ export const ProjectWidget: React.FunctionComponent<{ const onClickSubmit = () => { if (projectName.trim() === '') { - setHelperText({helperText:'Select a valid (non-empty) project name.', error:true}); + setHelperText({ + helperText: 'Select a valid (non-empty) project name.', + error: true, + }); return; } - checkProjectName(projectName).then((valid: boolean) => { - if (valid) { - props.onSubmit({ - name: projectName, - stack, - release, - platform, - user_script: userScript, - corrupted: options.corrupted, - }); - } else { - setHelperText({helperText:'File or directory already exists with the same name.', error:true}); - return; - } - }); + if (projectName != options.name) { + checkProjectName(projectName).then((valid: boolean) => { + if (valid) { + props.onSubmit({ + name: projectName, + stack, + release, + platform, + user_script: userScript, + corrupted: options.corrupted, + }); + } else { + setHelperText({ + helperText: 'File or directory already exists with the same name.', + error: true, + }); + return; + } + }); + } else { + props.onSubmit({ + name: projectName, + stack, + release, + platform, + user_script: userScript, + corrupted: options.corrupted, + }); + } }; const onClickCancel = () => { From 1cb39332b12a246d68f3f53820867d5a6ce25244 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Fri, 5 Nov 2021 13:57:17 +0100 Subject: [PATCH 31/35] -> fixed the validation of the form fields in the ProjectDialog, if nothing was chnaged the request is not send. -> theme handled as external paramether outside the project parameters in the dialog. --- SwanProjects/src/ProjectDialog.tsx | 8 +------- SwanProjects/src/ProjectWidget.tsx | 24 ++++++++++++++++-------- SwanProjects/src/dialog.tsx | 5 +++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index d10011ee..4a350a25 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -129,13 +129,7 @@ export namespace ProjectDialog { changesSaved: boolean; newOptions?: ISWANOptions; } | null = null; - dialogResult = await showDialog( - { - ...options, - theme, - }, - { ...stacks } - ); + dialogResult = await showDialog(options, theme, stacks); if (dialogResult?.changesSaved && dialogResult?.newOptions) { options = dialogResult.newOptions; } diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index d60966a1..39f775a9 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -100,6 +100,15 @@ export const ProjectWidget: React.FunctionComponent<{ } const onClickSubmit = () => { + const selectedOptions: ProjectDialog.ISWANOptions = { + name: projectName, + stack, + release, + platform, + user_script: userScript, + corrupted: options.corrupted, + }; + // validating is not an empty name if (projectName.trim() === '') { setHelperText({ helperText: 'Select a valid (non-empty) project name.', @@ -107,17 +116,16 @@ export const ProjectWidget: React.FunctionComponent<{ }); return; } + // validating changes were made in any field, to avoid send the request + if (JSON.stringify(selectedOptions) === JSON.stringify(options)) { + return; + } + + // if the name was changed, then I need to check it with the contents manager if (projectName != options.name) { checkProjectName(projectName).then((valid: boolean) => { if (valid) { - props.onSubmit({ - name: projectName, - stack, - release, - platform, - user_script: userScript, - corrupted: options.corrupted, - }); + props.onSubmit(selectedOptions); } else { setHelperText({ helperText: 'File or directory already exists with the same name.', diff --git a/SwanProjects/src/dialog.tsx b/SwanProjects/src/dialog.tsx index d1a9836d..7ce3c046 100644 --- a/SwanProjects/src/dialog.tsx +++ b/SwanProjects/src/dialog.tsx @@ -29,7 +29,8 @@ import { ProjectDialog } from './ProjectDialog'; */ export async function showDialog( - options: ProjectDialog.ISWANOptions & { theme: 'light' | 'dark' }, + options: ProjectDialog.ISWANOptions, + theme: 'light' | 'dark', stacks: ProjectDialog.ISWANStackOptions ): Promise<{ changesSaved: boolean; @@ -37,7 +38,7 @@ export async function showDialog( }> { return new Promise((resolve) => { const widget = ReactWidget.create( - + Date: Sat, 6 Nov 2021 20:09:56 +0100 Subject: [PATCH 32/35] ->fixed problems with project names with spaces ->fixed dialog edit when project is corrupted --- SwanProjects/bin/swan_bash | 12 ++++++------ SwanProjects/bin/swan_env | 19 +++++++------------ SwanProjects/src/ProjectWidget.tsx | 7 +++++-- .../kernelmanager/kernelspecmanager.py | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index 6b4093fc..b5de40ff 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -30,17 +30,17 @@ if [[ $# -gt 1 ]] ; then if [ -d "$PROJECT_PATH" ] then - STACK=`jq '.stack' $PROJECT_FILE` - RELEASE=`jq '.release' $PROJECT_FILE` - PLATFORM=`jq '.platform' $PROJECT_FILE` + STACK=`jq '.stack' "$PROJECT_FILE"` + RELEASE=`jq '.release' "$PROJECT_FILE"` + PLATFORM=`jq '.platform' "$PROJECT_FILE"` USER_SCRIPT="$PROJECT_PATH/.userscript" echo "Loading $RELEASE with plafortm $PLATFORM " # FIXME: this have to be removed when environment isolation is not needed anymore, it's only temporary env -i HOME=$HOME \ OAUTH2_TOKEN=$OAUTH2_TOKEN \ - PROJECT=$PROJECT_NAME \ - PROJECT_PATH=$PROJECT_PATH PS1="$PS1" \ - bash -c "swan_env $PROJECT_PATH $STACKS_PATH $PROJECT_PATH bash --rcfile <(echo 'PS1=\"($PROJECT_NAME) $PS1 \"') " + PROJECT="$PROJECT_NAME" \ + PROJECT_PATH="$PROJECT_PATH" PS1="$PS1" \ + bash -c "swan_env \"$PROJECT_PATH\" \"$STACKS_PATH\" \"$PROJECT_PATH\" bash --rcfile <(echo 'PS1=\"($PROJECT_NAME) $PS1 \"') " else echo "Error: project $PROJECT_PATH doesn't exist" >&2 # JupyterLab closes the terminal window immediately after the process ends diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index f73d7566..d195b1f9 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -14,8 +14,7 @@ # # This command is called from SwanKernelSpecManager to start the kernel and swan_bash to start a bash session inside the project environment. ## -PARAMETERS=($@) - +PARAMETERS=("$@") # Check if jq is installed if ! [ -x "$(command -v jq)" ]; then echo 'Error: jq is not installed.' >&2 @@ -44,9 +43,9 @@ fi PROJECT_PATH=$1 PROJECT_NAME=`IFS='/'; ARR=($PROJECT_PATH);echo "${ARR[-1]}"` PROJECT_FILE="$PROJECT_PATH/.swanproject" -STACK=`jq -r '.stack' $PROJECT_FILE` -RELEASE=`jq -r '.release' $PROJECT_FILE` -PLATFORM=`jq -r '.platform' $PROJECT_FILE` +STACK=`jq -r '.stack' "$PROJECT_FILE"` +RELEASE=`jq -r '.release' "$PROJECT_FILE"` +PLATFORM=`jq -r '.platform' "$PROJECT_FILE"` USER_SCRIPT="$PROJECT_PATH/.userscript" # Path to the stack where the setup.sh for this project is located. @@ -57,11 +56,7 @@ STACK_SETUP="$STACK_PATH/setup.sh" # Working directory parameter CWD="$3" -i=0 -if [ "$CWD" != "" ]; then - i=$((i+2)) -fi - +i=2 for j in $(seq 0 1 $((i)));do unset PARAMETERS[$j] @@ -88,10 +83,10 @@ export SWAN_PROJECT_NAME=$PROJECT_NAME export SWAN_PROJECT_PATH=$PROJECT_PATH if [ "$USER_SCRIPT" != "" ] && [ -f "$USER_SCRIPT" ]; then - . ${USER_SCRIPT} + . "${USER_SCRIPT}" fi # The command is executed inside the $CWD folder. # if the command produces output it will be there. -cd $CWD +cd "$CWD" $COMMAND diff --git a/SwanProjects/src/ProjectWidget.tsx b/SwanProjects/src/ProjectWidget.tsx index 39f775a9..9ad0c29d 100644 --- a/SwanProjects/src/ProjectWidget.tsx +++ b/SwanProjects/src/ProjectWidget.tsx @@ -101,7 +101,7 @@ export const ProjectWidget: React.FunctionComponent<{ const onClickSubmit = () => { const selectedOptions: ProjectDialog.ISWANOptions = { - name: projectName, + name: projectName.trim(), stack, release, platform, @@ -117,7 +117,10 @@ export const ProjectWidget: React.FunctionComponent<{ return; } // validating changes were made in any field, to avoid send the request - if (JSON.stringify(selectedOptions) === JSON.stringify(options)) { + if ( + JSON.stringify(selectedOptions) === JSON.stringify(options) && + !options.corrupted + ) { return; } diff --git a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py index d05fbefc..40748b00 100644 --- a/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py +++ b/SwanProjects/swanprojects/kernelmanager/kernelspecmanager.py @@ -131,7 +131,7 @@ def wrap_kernel_specs(self, project_path, kspec): wrapped kernel spec with the required information to run inside the enviroment of the project. """ argv = self.swan_utils.get_env_isolated() - argv += ["/bin/bash", "-c", "swan_env {} {} {} ".format( + argv += ["/bin/bash", "-c", "swan_env \"{}\" \"{}\" \"{}\" ".format( os.path.join( self.swan_utils.contents_manager.root_dir, project_path), self.swan_config.stacks_path, ".") + "'" + " ".join(kspec.argv) + "'" From fb6a88b200d46f194748c331e1a669920d1ccfc9 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Mon, 8 Nov 2021 16:35:59 +0100 Subject: [PATCH 33/35] fixed documentation for Card in the project components --- SwanProjects/src/Components.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SwanProjects/src/Components.tsx b/SwanProjects/src/Components.tsx index 1bf19348..40370eae 100644 --- a/SwanProjects/src/Components.tsx +++ b/SwanProjects/src/Components.tsx @@ -36,13 +36,13 @@ export function HelpTooltip(props: { } /** - * A pure tsx component for a launcher card. + * A pure tsx component for a ProjectDialog card. * * @param label - Text for the Card * @param icon - Icon for the Card * @param isSelected - Helps to know the selected stack. * @param updateCallback - Callback to update stacks on other components - * @returns a vdom `VirtualElement` for the launcher card. + * @returns a vdom `VirtualElement` for the ProjectDialog card. */ export function Card(props: { label: string; From 8086bc97791b4fd8f056cc6cd4f440f2bcd6b3a7 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Tue, 16 Nov 2021 15:14:47 +0100 Subject: [PATCH 34/35] fixed code comments and typos, thanks @etejedor for the review. --- SwanProjects/bin/swan_bash | 9 ++++----- SwanProjects/bin/swan_env | 10 +++++----- SwanProjects/bin/swan_kmspecs | 18 +++++++++--------- SwanProjects/setup.py | 1 - SwanProjects/src/Components.tsx | 4 ++-- SwanProjects/src/ProjectDialog.tsx | 8 ++++---- SwanProjects/swanprojects/handlers.py | 9 +++------ 7 files changed, 27 insertions(+), 32 deletions(-) diff --git a/SwanProjects/bin/swan_bash b/SwanProjects/bin/swan_bash index b5de40ff..0f35b40d 100755 --- a/SwanProjects/bin/swan_bash +++ b/SwanProjects/bin/swan_bash @@ -1,12 +1,11 @@ #!/bin/bash -i #Author Omar.Zapata@cern.ch 2021 ### -# This script allows to start a bash interpreter inside a project environment. -# This is called from the extension SwanTerminal and it requires like a parameter the project name to -# build the path to the project with $HOME/SWAN_projects/$PROJECT to load the project environment. +# This is called from the extension SwanTerminal and it requires the project path and stacks path as arguments. +# The project path is used to read the .swanproject file and the stacks path is required to load the proper enviroment. # When the bash session is started we modify the prompt indicating in which project the terminal is located. -# Aditionally bash run completed isolated from other possible environments then we pass like parameters to the isolated environment some -# required basic environment variables such as: +# Additionally, the bash session runs completely isolated from other possible environments, +# so we need to define some basic environment variables such as: # # * HOME with path to the user home # * PATH with default paths for the system diff --git a/SwanProjects/bin/swan_env b/SwanProjects/bin/swan_env index d195b1f9..61b85624 100755 --- a/SwanProjects/bin/swan_env +++ b/SwanProjects/bin/swan_env @@ -12,7 +12,7 @@ # With the project name we search the project located at $HOME/SWAN_projects/ # reading the configuration to load the environment and the bash script provided by the user. # -# This command is called from SwanKernelSpecManager to start the kernel and swan_bash to start a bash session inside the project environment. +# This script is called from SwanKernelSpecManager to start the kernel and from swan_bash to start a bash session inside the project environment. ## PARAMETERS=("$@") # Check if jq is installed @@ -36,7 +36,7 @@ if [[ $# -lt 3 ]] ; then fi ### -# In the next block I made the path to the project to read the information from .swanproject file such as +# In the next block I make the path to the project to read the information from .swanproject file such as # stack, release and platform. # I also create the path to use bash user script to source it. ### @@ -49,7 +49,7 @@ PLATFORM=`jq -r '.platform' "$PROJECT_FILE"` USER_SCRIPT="$PROJECT_PATH/.userscript" # Path to the stack where the setup.sh for this project is located. -# The available stackas are provided in the extension with --SwanProjects.stacks_path +# The available stacks are provided in the extension with --SwanProjects.stacks_path STACKS_PATH="$2" STACK_PATH="$STACKS_PATH/$STACK" STACK_SETUP="$STACK_PATH/setup.sh" @@ -61,7 +61,7 @@ i=2 for j in $(seq 0 1 $((i)));do unset PARAMETERS[$j] done -# After project name and working directory the rest of the options passed to this script is the command to be execute inside the environment. +# After project name and working directory the remaining option passed to the script is the command to be execute inside the environment. COMMAND=${PARAMETERS[@]} if [ ! -d $STACK_PATH ]; then @@ -77,7 +77,7 @@ else exit 1 fi -# The next variables allows the user to know in the environment basic information about the project such as stack, name and path. +# The next variables allow the user to know in the environment basic information about the project such as stack, name and path. export SWAN_STACK="$RELEASE($PLATFORM)" export SWAN_PROJECT_NAME=$PROJECT_NAME export SWAN_PROJECT_PATH=$PROJECT_PATH diff --git a/SwanProjects/bin/swan_kmspecs b/SwanProjects/bin/swan_kmspecs index 37799ddf..600415b0 100755 --- a/SwanProjects/bin/swan_kmspecs +++ b/SwanProjects/bin/swan_kmspecs @@ -5,10 +5,10 @@ """ This script allows to find the kernels for python2/3 and kernel spec paths for our kernel spec manager. -The script run a subprocess inside the project environment trying to find the package ipykernel +The script runs a subprocess inside the project environment trying to find the package ipykernel and run jupyter_path('kernels') to get the list of available paths for the differents kernels inside the environment as well. -This is execute in the Create/Edit project handlers and the stdout is captured, getting the json information printed in the function generate_ksminfo() +This is executed in the Create/Edit project handlers and the stdout is captured, getting the json information printed in the function generate_ksminfo() """ import argparse import json @@ -53,12 +53,12 @@ def check_native_kernel(): Checks if ipykernel is available for python2 and python3. This routine is called inside the project environment, - It checks if python2/3 are available to check if the package ipykernel is installed, - if the package was found the results is saved in the dictionary with the results. + It checks if python2/3 are available and, if they are, + it checks if the package ipykernel is installed for them. - Why? kernel.json is not always available in the software stacks but the package ipykernel is there, - then we can create the json file locally in the project to put it work, because our kernel spec manager requires - the kernel path with the kernel.json file. + Why? A kernel.json might not be available but, + if the package ipykernel is, it will allow us to create the json file locally in the project. + We need to do so because our kernel spec manager requires a kernel path with the kernel.json file. Returns ------- @@ -79,7 +79,7 @@ def check_native_kernel(): else: project_data["python2"]["found"] = False - # checking if python2 is found + # checking if python3 is found python3 = find_executable("python3") if python3 is not None: project_data["python3"] = {"found": True, "path": python3} @@ -121,7 +121,7 @@ def get_kernel_paths(): def generate_ksminfo(): """ - Function to generated all the kernel spec manager info, + Function to generate all the kernel spec manager info, calling the function check_native_kernel and get_kernel_paths. """ ksminfo = check_native_kernel() diff --git a/SwanProjects/setup.py b/SwanProjects/setup.py index 386f0e81..93582c55 100644 --- a/SwanProjects/setup.py +++ b/SwanProjects/setup.py @@ -81,7 +81,6 @@ setup_args['data_files'] = get_data_files(data_files_spec) except ImportError as e: print(e) - pass if __name__ == "__main__": setuptools.setup(**setup_args) diff --git a/SwanProjects/src/Components.tsx b/SwanProjects/src/Components.tsx index 40370eae..25358cf5 100644 --- a/SwanProjects/src/Components.tsx +++ b/SwanProjects/src/Components.tsx @@ -3,8 +3,8 @@ /** * Some components needed for the react Dialogs, - * such as ToolTip, to display information about the relase and platform in the project dialog - * and Card, that is based in the code from launcher's Card in JupyetrLab wich is not exported to reuse it. + * such as ToolTip, to display information about the release and platform in the project dialog + * and Card, that is based in the code from launcher's Card in JupyterLab wich is not exported to reuse it. */ import { classes, LabIcon } from '@jupyterlab/ui-components'; diff --git a/SwanProjects/src/ProjectDialog.tsx b/SwanProjects/src/ProjectDialog.tsx index 4a350a25..2ecc0f73 100644 --- a/SwanProjects/src/ProjectDialog.tsx +++ b/SwanProjects/src/ProjectDialog.tsx @@ -6,8 +6,8 @@ * the dialog allows to select the stack, release, platform and write in a textbox * a bash script to run inside the project. * - * If the .swanproject file is corrupted for any reason, this dialog will appear - * for the user in order to recover the project with the right information provide by he/she. + * If the .swanproject file is corrupted, this dialog will appear + * for the user to enter the right information about the project again. */ import { showErrorMessage } from '@jupyterlab/apputils'; @@ -68,7 +68,7 @@ export namespace ProjectDialog { function startSpinner(): void { /** - * Function to start the spiner in the SwanLauncer, embed in the html tag with id jp-main-dock-panel. + * Function to start the spinner in the SwanLauncer, embed in the html tag with id jp-main-dock-panel. */ const node = document.getElementById('jp-main-dock-panel'); node?.appendChild(_spinner.node); @@ -79,7 +79,7 @@ export namespace ProjectDialog { } /** - * hides the spiner from the component + * hides the spinner from the component */ function stopSpinner(): void { _spinner.hide(); diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 2db4ba45..55f67cdd 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -37,7 +37,7 @@ def initialize(self): def subprocess(self, command): """ - Method to call a sub process in a isolated environment + Method to call a sub process in an isolated environment Parameters ---------- @@ -63,7 +63,7 @@ def subprocess(self, command): def save_project_file(self, project_dir, content): """ - Method to save contents in the project file. + Method to save contents into the project file. Parameters ---------- @@ -104,7 +104,7 @@ def get_kmanager_info(self, project_dir): Returns ------- kinfo: dict - Kernel information such as kernei_dir and ipykernel for python2/3 in a dictionary. + Kernel information such as kernel_dir and ipykernel for python2/3 in a dictionary. """ name = project_dir.split(os.sep)[-1] swan_kmspecs = find_executable("swan_kmspecs") @@ -330,9 +330,6 @@ def put(self): "msg": f"edited project {name}"} self.finish(json.dumps(data)) -# URL to handler mappings - - def setup_handlers(web_app, url_path): host_pattern = ".*$" base_url = web_app.settings["base_url"] From cf79b5c9f435ea57c5da9fc47f848078f9a90e58 Mon Sep 17 00:00:00 2001 From: Omar Zapata Date: Wed, 17 Nov 2021 14:14:24 +0100 Subject: [PATCH 35/35] checking if old_name is not None to raname the project --- SwanProjects/swanprojects/handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwanProjects/swanprojects/handlers.py b/SwanProjects/swanprojects/handlers.py index 55f67cdd..ed17a66b 100644 --- a/SwanProjects/swanprojects/handlers.py +++ b/SwanProjects/swanprojects/handlers.py @@ -279,7 +279,7 @@ def put(self): project_relative_dir = os.path.join( self.swan_config.projects_folder_name, name) - if old_name != name: + if old_name is not None and old_name != name: try: old_project_dir = os.path.join( self.swan_config.projects_folder_name, old_name)