diff --git a/.circleci/config.yml b/.circleci/config.yml index 4eb7fa3c..80b0f55a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,82 +1,105 @@ version: 2 defaults: &defaults - docker: - - image: cimg/python:3.11.0-browsers + docker: + - image: cimg/python:3.12.1-browsers install_dependency: &install_dependency name: Installation of build and deployment dependencies. command: | - sudo apt update - sudo apt install jq - sudo apt install python3-pip - sudo pip3 install awscli --upgrade - sudo pip3 install docker-compose + pip3 install awscli --upgrade install_deploysuite: &install_deploysuite name: Installation of install_deploysuite. command: | - git clone --branch v1.4 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript + git clone --branch v1.4.17 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript cp ./../buildscript/master_deploy.sh . cp ./../buildscript/buildenv.sh . cp ./../buildscript/awsconfiguration.sh . + restore_cache_settings_for_build: &restore_cache_settings_for_build key: docker-node-modules-{{ checksum "yarn.lock" }} save_cache_settings: &save_cache_settings key: docker-node-modules-{{ checksum "yarn.lock" }} paths: - - node_modules + - node_modules builddeploy_steps: &builddeploy_steps - - checkout - - setup_remote_docker - - run: *install_dependency - - run: *install_deploysuite - - restore_cache: *restore_cache_settings_for_build - - run: ./build.sh ${APPNAME} - - save_cache: *save_cache_settings - - deploy: - name: Running MasterScript. - command: | - ./awsconfiguration.sh $DEPLOY_ENV - source awsenvconf - ./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-deployvar - source buildenvvar - ./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-global-appvar,${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME} - - +- checkout +- setup_remote_docker +- run: *install_dependency +- run: *install_deploysuite +- restore_cache: *restore_cache_settings_for_build +- run: + name: "Authenticate with CodeArtifact and build Docker image" + command: | + ./awsconfiguration.sh ${CODEARTIFACT_ENV} + source awsenvconf + aws codeartifact login --tool npm --repository topcoder-framework --domain topcoder --domain-owner $AWS_ACCOUNT_ID --region $AWS_REGION --namespace @topcoder-framework + cp ~/.npmrc . + rm -f awsenvconf + ./build.sh ${APPNAME} +- save_cache: *save_cache_settings +- deploy: + name: Running MasterScript. + command: | + ./awsconfiguration.sh $DEPLOY_ENV + source awsenvconf + ./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-deployvar + source buildenvvar + ./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-global-appvar,${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME} -p FARGATE jobs: # Build & Deploy against development backend "build-dev": - <<: *defaults + !!merge <<: *defaults environment: DEPLOY_ENV: "DEV" LOGICAL_ENV: "dev" - APPNAME: "challenge-api" + APPNAME: "challenge-api" + CODEARTIFACT_ENV: "PROD" + steps: *builddeploy_steps + + "build-qa": + !!merge <<: *defaults + environment: + DEPLOY_ENV: "QA" + LOGICAL_ENV: "qa" + APPNAME: "challenge-api" + CODEARTIFACT_ENV: "PROD" steps: *builddeploy_steps "build-prod": - <<: *defaults + !!merge <<: *defaults environment: DEPLOY_ENV: "PROD" - LOGICAL_ENV: "prod" - APPNAME: "challenge-api" + LOGICAL_ENV: "prod" + APPNAME: "challenge-api" + CODEARTIFACT_ENV: "PROD" steps: *builddeploy_steps workflows: version: 2 build: jobs: - # Development builds are executed on "develop" branch only. - - "build-dev": - context : org-global - filters: - branches: - only: - - dev + # Development builds are executed on "develop" branch only. + - "build-dev": + context: org-global + filters: + branches: + only: + - dev + - feature/top-262-projectid-non-mandatory + - TOP-2364 + + - "build-qa": + context: org-global + filters: + branches: + only: + - qa - # Production builds are exectuted only on tagged commits to the - # master branch. - - "build-prod": - context : org-global - filters: - branches: - only: master \ No newline at end of file + # Production builds are exectuted only on tagged commits to the + # master branch. + - "build-prod": + context: org-global + filters: + branches: + only: master diff --git a/.gitignore b/.gitignore index 493bab5b..4a2329e0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* +.yarn # Runtime data pids @@ -60,4 +61,6 @@ typings/ # next.js build output .next +ecr-login.sh .npmrc +test.js diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..436d5c5d --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.19.0 \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..2e47837e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 100 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f6a871c..4b667af1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { - "editor.defaultFormatter": "standard.vscode-standard", - "standard.autoFixOnSave": true -} \ No newline at end of file + "standard.autoFixOnSave": true, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/README.md b/README.md index ca02c8a4..8e55d566 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # Topcoder Challenge API This microservice provides access and interaction with all sorts of Challenge data. + ## Devlopment status + [![Total alerts](https://img.shields.io/lgtm/alerts/g/topcoder-platform/challenge-api.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/topcoder-platform/challenge-api/alerts/)[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/topcoder-platform/challenge-api.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/topcoder-platform/challenge-api/context:javascript) ### Deployment status + Dev: [![CircleCI](https://circleci.com/gh/topcoder-platform/challenge-api/tree/develop.svg?style=svg)](https://circleci.com/gh/topcoder-platform/challenge-api/tree/develop) Prod: [![CircleCI](https://circleci.com/gh/topcoder-platform/challenge-api/tree/master.svg?style=svg)](https://circleci.com/gh/topcoder-platform/challenge-api/tree/master) ## Swagger definition @@ -12,19 +15,18 @@ Dev: [![CircleCI](https://circleci.com/gh/topcoder-platform/challenge-api/tree/d - [Swagger](https://api.topcoder.com/v5/challenges/docs/) ## Intended use + - Production API ## Related repos - [Resources API](https://github.com/topcoder-platform/resources-api) - [ES Processor](https://github.com/topcoder-platform/challenge-processor-es) - Updates data in ElasticSearch -- [Legacy Processor](https://github.com/topcoder-platform/legacy-challenge-processor) - Moves data from DynamoDB back to Informix -- [Legacy Migration Script](https://github.com/topcoder-platform/legacy-challenge-migration-script) - Moves data from Informix to DynamoDB -- [Frontend App](https://github.com/topcoder-platform/challenge-engine-ui) +- [Domain Challenge](https://github.com/topcoder-platform/domain-challenge) - Domain Challenge ## Prerequisites -- [NodeJS](https://nodejs.org/en/) (v10) -- [DynamoDB](https://aws.amazon.com/dynamodb/) + +- [NodeJS](https://nodejs.org/en/) (v18+) - [AWS S3](https://aws.amazon.com/s3/) - [Elasticsearch v6](https://www.elastic.co/) - [Docker](https://www.docker.com/) @@ -58,9 +60,9 @@ The following parameters can be set in config files or in env variables: - ES.HOST: Elasticsearch host - ES.API_VERSION: Elasticsearch API version - ES.ES_INDEX: Elasticsearch index name -- ES.ES_TYPE: Elasticsearch index type - ES.ES_REFRESH: Elasticsearch refresh method. Default to string `true`(i.e. refresh immediately) - FILE_UPLOAD_SIZE_LIMIT: the file upload size limit in bytes +- OPENSEARCH: Flag to use Opensearch NPM instead of Elasticsearch - RESOURCES_API_URL: TC resources API base URL - GROUPS_API_URL: TC groups API base URL - PROJECTS_API_URL: TC projects API base URL @@ -75,6 +77,7 @@ The following parameters can be set in config files or in env variables: You can find sample `.env` files inside the `/docs` directory. ## Available commands + 1. Drop/delete tables: `npm run drop-tables` 2. Creating tables: `npm run create-tables` 3. Seed/Insert data to tables: `npm run seed-tables` @@ -88,11 +91,12 @@ You can find sample `.env` files inside the `/docs` directory. 11. Initialize the local environments: `npm run local:init` 12. Reset the local environments: `npm run local:reset` - ### Notes + - The seed data are located in `src/scripts/seed` ## Local Deployment + 0. Make sure to use Node v10+ by command `node -v`. We recommend using [NVM](https://github.com/nvm-sh/nvm) to quickly switch to the right version: ```bash @@ -102,34 +106,40 @@ You can find sample `.env` files inside the `/docs` directory. 1. 📦 Install npm dependencies ```bash + # export the production AWS credentials to access the topcoder-framework private repos in AWS codeartifact + aws codeartifact login --tool npm --repository topcoder-framework --domain topcoder --domain-owner 409275337247 --region us-east-1 --namespace @topcoder-framework + + # install dependencies yarn install ``` -2. ⚙ Local config - In the `challenge-api` root directory create `.env` file with the next environment variables. Values for **Auth0 config** should be shared with you on the forum.
- ```bash - # Auth0 config - AUTH0_URL= - AUTH0_PROXY_SERVER_URL= - AUTH0_AUDIENCE= - AUTH0_CLIENT_ID= - AUTH0_CLIENT_SECRET= +2. ⚙ Local config + In the `challenge-api` root directory create `.env` file with the next environment variables. Values for **Auth0 config** should be shared with you on the forum.
- # Locally deployed services (via docker-compose) - IS_LOCAL_DB=true - DYNAMODB_URL=http://localhost:8000 - ``` + ```bash + # Auth0 config + AUTH0_URL= + AUTH0_PROXY_SERVER_URL= + AUTH0_AUDIENCE= + AUTH0_CLIENT_ID= + AUTH0_CLIENT_SECRET= + + # Locally deployed services (via docker-compose) + IS_LOCAL_DB=true + DYNAMODB_URL=http://localhost:8000 + ``` - - Values from this file would be automatically used by many `npm` commands. - - ⚠️ Never commit this file or its copy to the repository! + - Values from this file would be automatically used by many `npm` commands. + - ⚠️ Never commit this file or its copy to the repository! 3. 🚢 Start docker-compose with services which are required to start Topcoder Challenges API locally ```bash npm run services:up ``` - + 4. ♻ Update following two parts: + - https://github.com/topcoder-platform/challenge-api/blob/develop/src/models/Challenge.js#L116 `throughput: 'ON_DEMAND',` should be updated to `throughput:{ read: 4, write: 2 },` - https://github.com/topcoder-platform/challenge-api/blob/develop/config/default.js#L27-L28 @@ -148,15 +158,17 @@ You can find sample `.env` files inside the `/docs` directory. ``` This command will do 3 things: - - create Elasticsearch indexes (drop if exists) - - Initialize the database by cleaning all the records. - - Import the data to the local database and index it to ElasticSearch + +- create Elasticsearch indexes (drop if exists) +- Initialize the database by cleaning all the records. +- Import the data to the local database and index it to ElasticSearch 7. 🚀 Start Topcoder Challenge API ```bash npm start ``` + The Topcoder Challenge API will be served on `http://localhost:3000` ## Production deployment @@ -181,6 +193,7 @@ The following test parameters can be set in config file or in env variables: - S3_ENDPOINT: endpoint of AWS S3 API, for unit and e2e test only; default to `localhost:9000` ### Prepare + - Start Local services in docker. - Create DynamoDB tables. - Initialize ES index. @@ -189,6 +202,7 @@ The following test parameters can be set in config file or in env variables: Seeding db data is not needed. ### Running unit tests + To run unit tests alone ```bash @@ -202,6 +216,7 @@ npm run test:cov ``` ### Running integration tests + To run integration tests alone ```bash @@ -215,6 +230,7 @@ npm run e2e:cov ``` ## Verification + Refer to the verification document `Verification.md` ## Notes diff --git a/app-bootstrap.js b/app-bootstrap.js index 6792c5f7..4516ae56 100644 --- a/app-bootstrap.js +++ b/app-bootstrap.js @@ -1,11 +1,11 @@ /** * App bootstrap */ -global.Promise = require('bluebird') -const Joi = require('joi') +global.Promise = require("bluebird"); +const Joi = require("joi"); -Joi.optionalId = () => Joi.string().uuid() -Joi.id = () => Joi.optionalId().required() -Joi.page = () => Joi.number().integer().min(1).default(1) -Joi.perPage = () => Joi.number().integer().min(1).max(100).default(20) -Joi.fileSize = () => Joi.number().integer().min(0).default(0) +Joi.optionalId = () => Joi.string().uuid(); +Joi.id = () => Joi.optionalId().required(); +Joi.page = () => Joi.number().integer().min(1).default(1); +Joi.perPage = () => Joi.number().integer().min(1).max(100).default(20); +Joi.fileSize = () => Joi.number().integer().min(0).default(0); diff --git a/app-constants.js b/app-constants.js index 7cad4648..718e2e9c 100644 --- a/app-constants.js +++ b/app-constants.js @@ -1,139 +1,148 @@ -const config = require('config') +const config = require("config"); /** * App constants */ const UserRoles = { - Admin: 'administrator', - Copilot: 'copilot', - Manager: 'Connect Manager', - User: 'Topcoder User', - SelfServiceCustomer: 'Self-Service Customer' -} + Admin: "administrator", + Copilot: "copilot", + Manager: "Connect Manager", + User: "Topcoder User", + SelfServiceCustomer: "Self-Service Customer", +}; const prizeSetTypes = { - ChallengePrizes: 'placement', - CopilotPayment: 'copilot', - ReviewerPayment: 'reviewer', - CheckpointPrizes: 'checkpoint' -} + ChallengePrizes: "placement", + CopilotPayment: "copilot", + ReviewerPayment: "reviewer", + CheckpointPrizes: "checkpoint", +}; + +const prizeTypes = { + USD: "USD", + POINT: "POINT", +}; const challengeStatuses = { - New: 'New', - Draft: 'Draft', - Approved: 'Approved', - Cancelled: 'Cancelled', - Active: 'Active', - Completed: 'Completed', - Deleted: 'Deleted', - CancelledFailedReview: 'Cancelled - Failed Review', - CancelledFailedScreening: 'Cancelled - Failed Screening', - CancelledZeroSubmissions: 'Cancelled - Zero Submissions', - CancelledWinnerUnresponsive: 'Cancelled - Winner Unresponsive', - CancelledClientRequest: 'Cancelled - Client Request', - CancelledRequirementsInfeasible: 'Cancelled - Requirements Infeasible', - CancelledZeroRegistrations: 'Cancelled - Zero Registrations', - CancelledPaymentFailed: 'Cancelled - Payment Failed' -} + New: "New", + Draft: "Draft", + Approved: "Approved", + Cancelled: "Cancelled", + Active: "Active", + Completed: "Completed", + Deleted: "Deleted", + CancelledFailedReview: "Cancelled - Failed Review", + CancelledFailedScreening: "Cancelled - Failed Screening", + CancelledZeroSubmissions: "Cancelled - Zero Submissions", + CancelledWinnerUnresponsive: "Cancelled - Winner Unresponsive", + CancelledClientRequest: "Cancelled - Client Request", + CancelledRequirementsInfeasible: "Cancelled - Requirements Infeasible", + CancelledZeroRegistrations: "Cancelled - Zero Registrations", + CancelledPaymentFailed: "Cancelled - Payment Failed", +}; const validChallengeParams = { - UpdatedBy: 'updatedBy', - Updated: 'updated', - CreatedBy: 'createdBy', - Created: 'created', - EndDate: 'endDate', - StartDate: 'startDate', - ProjectId: 'projectId', - Name: 'name', - Type: 'type', - NumOfSubmissions: 'numOfSubmissions', - NumOfRegistrants: 'numOfRegistrants', - Status: 'status', - TypeId: 'typeId', - Prizes: 'overview.totalPrizes' -} + UpdatedBy: "updatedBy", + Updated: "updated", + CreatedBy: "createdBy", + Created: "created", + EndDate: "endDate", + StartDate: "startDate", + ProjectId: "projectId", + Name: "name", + Type: "type", + NumOfSubmissions: "numOfSubmissions", + NumOfRegistrants: "numOfRegistrants", + Status: "status", + TypeId: "typeId", + Prizes: "overview.totalPrizes", +}; -const EVENT_ORIGINATOR = 'topcoder-challenges-api' +const EVENT_ORIGINATOR = "topcoder-challenges-api"; -const EVENT_MIME_TYPE = 'application/json' +const EVENT_MIME_TYPE = "application/json"; const DiscussionTypes = { - Challenge: 'challenge' -} + Challenge: "challenge", +}; // using a testing topc, should be changed to use real topics in comments when they are created const Topics = { - ChallengeCreated: 'challenge.notification.create', - ChallengeUpdated: 'challenge.notification.update', - ChallengeDeleted: 'challenge.notification.delete', - ChallengeTypeCreated: 'test.new.bus.events', // 'challenge.action.type.created', - ChallengeTypeUpdated: 'test.new.bus.events', // 'challenge.action.type.updated', - ChallengeTypeDeleted: 'test.new.bus.events', // 'challenge.action.type.deleted', - ChallengeTrackCreated: 'test.new.bus.events', // 'challenge.action.track.created', - ChallengeTrackUpdated: 'test.new.bus.events', // 'challenge.action.track.updated', - ChallengeTrackDeleted: 'test.new.bus.events', // 'challenge.action.track.deleted', - ChallengePhaseCreated: 'test.new.bus.events', // 'challenge.action.phase.created', - ChallengePhaseUpdated: 'test.new.bus.events', // 'challenge.action.phase.updated', - ChallengePhaseDeleted: 'test.new.bus.events', // 'challenge.action.phase.deleted', - TimelineTemplateCreated: 'test.new.bus.events', // 'challenge.action.timeline.template.created', - TimelineTemplateUpdated: 'test.new.bus.events', // 'challenge.action.timeline.template.updated', - TimelineTemplateDeleted: 'test.new.bus.events', // 'challenge.action.timeline.template.deleted', - ChallengeTypeTimelineTemplateCreated: 'test.new.bus.events', // 'challenge.action.type.timeline.template.created', - ChallengeTypeTimelineTemplateUpdated: 'test.new.bus.events', // 'challenge.action.type.timeline.template.updated', - ChallengeTypeTimelineTemplateDeleted: 'test.new.bus.events', // 'challenge.action.type.timeline.template.deleted' - ChallengeAttachmentCreated: 'test.new.bus.events', // 'challenge.action.attachment.created', - ChallengeAttachmentUpdated: 'test.new.bus.events', // 'challenge.action.attachment.updated', - ChallengeAttachmentDeleted: 'test.new.bus.events', // 'challenge.action.attachment.deleted', + ChallengeCreated: "challenge.notification.create", + ChallengeUpdated: "challenge.notification.update", + ChallengeDeleted: "challenge.notification.delete", + ChallengeTypeCreated: "test.new.bus.events", // 'challenge.action.type.created', + ChallengeTypeUpdated: "test.new.bus.events", // 'challenge.action.type.updated', + ChallengeTypeDeleted: "test.new.bus.events", // 'challenge.action.type.deleted', + ChallengeTrackCreated: "test.new.bus.events", // 'challenge.action.track.created', + ChallengeTrackUpdated: "test.new.bus.events", // 'challenge.action.track.updated', + ChallengeTrackDeleted: "test.new.bus.events", // 'challenge.action.track.deleted', + ChallengePhaseCreated: "test.new.bus.events", // 'challenge.action.phase.created', + ChallengePhaseUpdated: "test.new.bus.events", // 'challenge.action.phase.updated', + ChallengePhaseDeleted: "test.new.bus.events", // 'challenge.action.phase.deleted', + TimelineTemplateCreated: "test.new.bus.events", // 'challenge.action.timeline.template.created', + TimelineTemplateUpdated: "test.new.bus.events", // 'challenge.action.timeline.template.updated', + TimelineTemplateDeleted: "test.new.bus.events", // 'challenge.action.timeline.template.deleted', + ChallengeTypeTimelineTemplateCreated: "test.new.bus.events", // 'challenge.action.type.timeline.template.created', + ChallengeTypeTimelineTemplateUpdated: "test.new.bus.events", // 'challenge.action.type.timeline.template.updated', + ChallengeTypeTimelineTemplateDeleted: "test.new.bus.events", // 'challenge.action.type.timeline.template.deleted' + ChallengeAttachmentCreated: "test.new.bus.events", // 'challenge.action.attachment.created', + ChallengeAttachmentUpdated: "test.new.bus.events", // 'challenge.action.attachment.updated', + ChallengeAttachmentDeleted: "test.new.bus.events", // 'challenge.action.attachment.deleted', + ChallengeTimelineTemplateCreated: "challenge.action.challenge.timeline.created", + ChallengeTimelineTemplateUpdated: "challenge.action.challenge.timeline.updated", + ChallengeTimelineTemplateDeleted: "challenge.action.challenge.timeline.deleted", // Self Service topics - Notifications: 'notifications.action.create' -} + Notifications: "notifications.action.create", +}; const challengeTracks = { - DEVELOP: 'DEVELOP', - DESIGN: 'DESIGN', - DATA_SCIENCE: 'DATA_SCIENCE', - QA: 'QA' -} + DEVELOP: "DEVELOP", + DESIGN: "DESIGN", + DATA_SCIENCE: "DATA_SCIENCE", + QA: "QA", +}; const challengeTextSortField = { - Name: 'name', - TypeId: 'typeId' -} + Name: "name", + TypeId: "typeId", +}; const reviewTypes = { - Community: 'COMMUNITY', - Internal: 'INTERNAL' -} + Community: "COMMUNITY", + Internal: "INTERNAL", +}; const SelfServiceNotificationTypes = { - WORK_REQUEST_SUBMITTED: 'self-service.notifications.work-request-submitted', - WORK_REQUEST_STARTED: 'self-service.notifications.work-request-started', - WORK_REQUEST_REDIRECTED: 'self-service.notifications.work-request-redirected', - WORK_COMPLETED: 'self-service.notifications.work-completed' -} + WORK_REQUEST_SUBMITTED: "self-service.notifications.work-request-submitted", + WORK_REQUEST_STARTED: "self-service.notifications.work-request-started", + WORK_REQUEST_REDIRECTED: "self-service.notifications.work-request-redirected", + WORK_COMPLETED: "self-service.notifications.work-completed", +}; const SelfServiceNotificationSettings = { [SelfServiceNotificationTypes.WORK_REQUEST_SUBMITTED]: { sendgridTemplateId: config.SENDGRID_TEMPLATES.WORK_REQUEST_SUBMITTED, - cc: [] + cc: [], }, [SelfServiceNotificationTypes.WORK_REQUEST_STARTED]: { sendgridTemplateId: config.SENDGRID_TEMPLATES.WORK_REQUEST_STARTED, - cc: [] + cc: [], }, [SelfServiceNotificationTypes.WORK_REQUEST_REDIRECTED]: { sendgridTemplateId: config.SENDGRID_TEMPLATES.WORK_REQUEST_REDIRECTED, - cc: [...config.SELF_SERVICE_EMAIL_CC_ACCOUNTS] + cc: [...config.SELF_SERVICE_EMAIL_CC_ACCOUNTS], }, [SelfServiceNotificationTypes.WORK_COMPLETED]: { sendgridTemplateId: config.SENDGRID_TEMPLATES.WORK_COMPLETED, - cc: [] - } -} + cc: [], + }, +}; module.exports = { UserRoles, prizeSetTypes, + prizeTypes, challengeStatuses, validChallengeParams, EVENT_ORIGINATOR, @@ -144,5 +153,5 @@ module.exports = { DiscussionTypes, reviewTypes, SelfServiceNotificationTypes, - SelfServiceNotificationSettings -} + SelfServiceNotificationSettings, +}; diff --git a/app-routes.js b/app-routes.js index a77b491d..824b04d0 100644 --- a/app-routes.js +++ b/app-routes.js @@ -2,13 +2,15 @@ * Configure all routes for express app */ -const _ = require('lodash') -const config = require('config') -const HttpStatus = require('http-status-codes') -const helper = require('./src/common/helper') -const errors = require('./src/common/errors') -const routes = require('./src/routes') -const authenticator = require('tc-core-library-js').middleware.jwtAuthenticator +const _ = require("lodash"); +const config = require("config"); +const HttpStatus = require("http-status-codes"); +const uuid = require("uuid/v4"); +const helper = require("./src/common/helper"); +const errors = require("./src/common/errors"); +const logger = require("./src/common/logger"); +const routes = require("./src/routes"); +const authenticator = require("tc-core-library-js").middleware.jwtAuthenticator; /** * Configure all routes for express app @@ -18,97 +20,116 @@ module.exports = (app) => { // Load all routes _.each(routes, (verbs, path) => { _.each(verbs, (def, verb) => { - const controllerPath = `./src/controllers/${def.controller}` + const controllerPath = `./src/controllers/${def.controller}`; const method = require(controllerPath)[def.method]; // eslint-disable-line if (!method) { - throw new Error(`${def.method} is undefined`) + throw new Error(`${def.method} is undefined`); } - const actions = [] + const actions = []; actions.push((req, res, next) => { - req.signature = `${def.controller}#${def.method}` - next() - }) + if (def.method !== "checkHealth") { + req._id = uuid(); + req.signature = `${req._id}-${def.controller}#${def.method}`; + logger.info(`Started request handling, ${req.signature}`); + } + next(); + }); actions.push((req, res, next) => { - if (_.get(req, 'query.token')) { - _.set(req, 'headers.authorization', `Bearer ${_.trim(req.query.token)}`) + if (_.get(req, "query.token")) { + _.set(req, "headers.authorization", `Bearer ${_.trim(req.query.token)}`); } - next() - }) + next(); + }); if (def.auth) { // add Authenticator/Authorization check if route has auth actions.push((req, res, next) => { - authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, res, next) - }) + authenticator(_.pick(config, ["AUTH_SECRET", "VALID_ISSUERS"]))(req, res, next); + }); actions.push((req, res, next) => { if (req.authUser.isMachine) { // M2M if (!req.authUser.scopes || !helper.checkIfExists(def.scopes, req.authUser.scopes)) { - next(new errors.ForbiddenError('You are not allowed to perform this action!')) + next(new errors.ForbiddenError(`You are not allowed to perform this action, because the scopes are incorrect. \ + Required scopes: ${JSON.stringify(def.scopes)} \ + Provided scopes: ${JSON.stringify(req.authUser.scopes)}`)); } else { - req.authUser.handle = config.M2M_AUDIT_HANDLE - req.userToken = req.headers.authorization.split(' ')[1] - next() + req.authUser.handle = config.M2M_AUDIT_HANDLE; + req.authUser.userId = config.M2M_AUDIT_USERID; + req.userToken = req.headers.authorization.split(" ")[1]; + next(); } } else { - req.authUser.userId = String(req.authUser.userId) + req.authUser.userId = String(req.authUser.userId); // User roles authorization if (req.authUser.roles) { - if (def.access && !helper.checkIfExists(_.map(def.access, a => a.toLowerCase()), _.map(req.authUser.roles, r => r.toLowerCase()))) { - next(new errors.ForbiddenError('You are not allowed to perform this action!')) + if ( + def.access && + !helper.checkIfExists( + _.map(def.access, (a) => a.toLowerCase()), + _.map(req.authUser.roles, (r) => r.toLowerCase()) + ) + ) { + next(new errors.ForbiddenError(`You are not allowed to perform this action, because the roles are incorrect. \ + Required roles: ${JSON.stringify(def.access)} \ + Provided roles: ${JSON.stringify(req.authUser.roles)}`)); } else { // user token is used in create/update challenge to ensure user can create/update challenge under specific project - req.userToken = req.headers.authorization.split(' ')[1] - next() + req.userToken = req.headers.authorization.split(" ")[1]; + next(); } } else { - next(new errors.ForbiddenError('You are not authorized to perform this action')) + next(new errors.ForbiddenError("You are not authorized to perform this action, \ + because no roles were provided")); } } - }) + }); } else { // public API, but still try to authenticate token if provided, but allow missing/invalid token actions.push((req, res, next) => { - const interceptRes = {} - interceptRes.status = () => interceptRes - interceptRes.json = () => interceptRes - interceptRes.send = () => next() - authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, interceptRes, next) - }) + const interceptRes = {}; + interceptRes.status = () => interceptRes; + interceptRes.json = () => interceptRes; + interceptRes.send = () => next(); + authenticator(_.pick(config, ["AUTH_SECRET", "VALID_ISSUERS"]))(req, interceptRes, next); + }); actions.push((req, res, next) => { if (!req.authUser) { - next() + next(); } else if (req.authUser.isMachine) { - if (!def.scopes || !req.authUser.scopes || !helper.checkIfExists(def.scopes, req.authUser.scopes)) { - req.authUser = undefined + if ( + !def.scopes || + !req.authUser.scopes || + !helper.checkIfExists(def.scopes, req.authUser.scopes) + ) { + req.authUser = undefined; } - next() + next(); } else { - req.authUser.userId = String(req.authUser.userId) - next() + req.authUser.userId = String(req.authUser.userId); + next(); } - }) + }); } - - actions.push(method) - app[verb](`/${config.API_VERSION}${path}`, helper.autoWrapExpress(actions)) - }) - }) + actions.push(method); + app[verb](`/${config.API_VERSION}${path}`, helper.autoWrapExpress(actions)); + }); + }); // Check if the route is not found or HTTP method is not supported - app.use('*', (req, res) => { + app.use("*", (req, res) => { if (routes[req.baseUrl]) { res.status(HttpStatus.METHOD_NOT_ALLOWED).json({ - message: 'The requested HTTP method is not supported.' - }) + message: "The requested HTTP method is not supported.", + }); } else { res.status(HttpStatus.NOT_FOUND).json({ - message: 'The requested resource cannot be found.' - }) + message: "The requested resource cannot be found.", + }); } - }) -} + }); +}; diff --git a/app.js b/app.js index 7f98945e..c72672f2 100644 --- a/app.js +++ b/app.js @@ -2,120 +2,144 @@ * The application entry point */ -require('./app-bootstrap') - -const _ = require('lodash') -const config = require('config') -const express = require('express') -const bodyParser = require('body-parser') -const cors = require('cors') -const HttpStatus = require('http-status-codes') -const logger = require('./src/common/logger') -const interceptor = require('express-interceptor') -const fileUpload = require('express-fileupload') -const YAML = require('yamljs') -const swaggerUi = require('swagger-ui-express') -const challengeAPISwaggerDoc = YAML.load('./docs/swagger.yaml') -const { ForbiddenError } = require('./src/common/errors') +require("./app-bootstrap"); + +const _ = require("lodash"); +const config = require("config"); +const express = require("express"); +const bodyParser = require("body-parser"); +const cors = require("cors"); +const HttpStatus = require("http-status-codes"); +const logger = require("./src/common/logger"); +const interceptor = require("express-interceptor"); +const fileUpload = require("express-fileupload"); +const YAML = require("yamljs"); +const swaggerUi = require("swagger-ui-express"); +const challengeAPISwaggerDoc = YAML.load("./docs/swagger.yaml"); +const { ForbiddenError } = require("./src/common/errors"); // setup express app -const app = express() +const app = express(); // Disable POST, PUT, PATCH, DELETE operations if READONLY is set to true app.use((req, res, next) => { - if (config.READONLY === true && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) { - throw new ForbiddenError('Action is temporarely not allowed!') + if (config.READONLY === true && ["POST", "PUT", "PATCH", "DELETE"].includes(req.method)) { + throw new ForbiddenError("Action is temporarely not allowed!"); } - next() -}) + next(); +}); // serve challenge V5 API swagger definition -app.use('/v5/challenges/docs', swaggerUi.serve, swaggerUi.setup(challengeAPISwaggerDoc)) - -app.use(cors({ - exposedHeaders: [ - 'X-Prev-Page', - 'X-Next-Page', - 'X-Page', - 'X-Per-Page', - 'X-Total', - 'X-Total-Pages', - 'Link' - ] -})) -app.use(fileUpload({ - limits: { fileSize: config.FILE_UPLOAD_SIZE_LIMIT } -})) -app.use(bodyParser.json()) -app.use(bodyParser.urlencoded({ extended: true })) -app.set('port', config.PORT) - -// intercept the response body from jwtAuthenticator -app.use(interceptor((req, res) => { - return { - isInterceptable: () => { - return res.statusCode === 403 - }, +app.use("/v5/challenges/docs", swaggerUi.serve, swaggerUi.setup(challengeAPISwaggerDoc)); - intercept: (body, send) => { - let obj - try { - obj = JSON.parse(body) - } catch (e) { - logger.error('Invalid response body.') - } - if (obj && obj.result && obj.result.content && obj.result.content.message) { - const ret = { message: obj.result.content.message } - res.statusCode = 401 - send(JSON.stringify(ret)) +app.use( + cors({ + origin: (origin, callback) => { + if (!origin) { + // disable cors if service to service request + callback(null, false); } else { - send(body) + callback(null, new RegExp(/topcoder(-dev|-qa)?\.com$/)); } - } - } -})) + }, + exposedHeaders: [ + "X-Prev-Page", + "X-Next-Page", + "X-Page", + "X-Per-Page", + "X-Total", + "X-Total-Pages", + "Link", + ], + }) +); +app.use( + fileUpload({ + limits: { fileSize: config.FILE_UPLOAD_SIZE_LIMIT }, + }) +); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); +app.set("port", config.PORT); + +// intercept the response body from jwtAuthenticator +app.use( + interceptor((req, res) => { + return { + isInterceptable: () => { + return res.statusCode === 403; + }, + + intercept: (body, send) => { + let obj; + try { + obj = JSON.parse(body); + } catch (e) { + logger.error("Invalid response body."); + } + if (obj && obj.result && obj.result.content && obj.result.content.message) { + const ret = { message: obj.result.content.message }; + res.statusCode = 401; + send(JSON.stringify(ret)); + } else { + send(body); + } + }, + }; + }) +); // Register routes -require('./app-routes')(app) +require("./app-routes")(app); // The error handler // eslint-disable-next-line no-unused-vars app.use((err, req, res, next) => { - logger.logFullError(err, req.signature || `${req.method} ${req.url}`) - const errorResponse = {} - const status = err.isJoi ? HttpStatus.BAD_REQUEST : (err.httpStatus || _.get(err, 'response.status') || HttpStatus.INTERNAL_SERVER_ERROR) + logger.logFullError(err, req.signature || `${req.method} ${req.url}`); + const errorResponse = {}; + let status = err.isJoi + ? HttpStatus.BAD_REQUEST + : err.httpStatus || _.get(err, "response.status") || HttpStatus.INTERNAL_SERVER_ERROR; + + // Check if err is a GrpcError + if (err.details != null && err.code != null) { + status = err.code == 5 ? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST; // TODO: Use @topcoder-framework/lib-common to map GrpcError codes to HTTP codes + errorResponse.code = err.code; + errorResponse.message = err.details; + } if (_.isArray(err.details)) { if (err.isJoi) { _.map(err.details, (e) => { if (e.message) { if (_.isUndefined(errorResponse.message)) { - errorResponse.message = e.message + errorResponse.message = e.message; } else { - errorResponse.message += `, ${e.message}` + errorResponse.message += `, ${e.message}`; } } - }) + }); } } - if (_.get(err, 'response.status')) { + if (_.get(err, "response.status")) { // extra error message from axios http response(v4 and v5 tc api) - errorResponse.message = _.get(err, 'response.data.result.content.message') || _.get(err, 'response.data.message') + errorResponse.message = + _.get(err, "response.data.result.content.message") || _.get(err, "response.data.message"); } if (_.isUndefined(errorResponse.message)) { if (err.message && status !== HttpStatus.INTERNAL_SERVER_ERROR) { - errorResponse.message = err.message + errorResponse.message = err.message; } else { - errorResponse.message = 'Internal server error' + errorResponse.message = "Internal server error"; } } - res.status(status).json(errorResponse) -}) + res.status(status).json(errorResponse); +}); -app.listen(app.get('port'), () => { - logger.info(`Express server listening on port ${app.get('port')}`) -}) +app.listen(app.get("port"), () => { + logger.info(`Express server listening on port ${app.get("port")}`); +}); -module.exports = app +module.exports = app; diff --git a/config/default.js b/config/default.js index 70c0d29f..4727e58e 100644 --- a/config/default.js +++ b/config/default.js @@ -1,112 +1,137 @@ /** * The configuration file. */ -const _ = require('lodash') -require('dotenv').config() +const _ = require("lodash"); +require("dotenv").config(); module.exports = { - READONLY: process.env.READONLY === 'true' || false, - LOG_LEVEL: process.env.LOG_LEVEL || 'debug', + READONLY: process.env.READONLY === "true" || false, + LOG_LEVEL: process.env.LOG_LEVEL || "debug", PORT: process.env.PORT || 3000, // used to properly set the header response to api calls for services behind a load balancer API_BASE_URL: process.env.API_BASE_URL || `http://localhost:3000`, - API_VERSION: process.env.API_VERSION || 'v5', - AUTH_SECRET: process.env.AUTH_SECRET || 'mysecret', - VALID_ISSUERS: process.env.VALID_ISSUERS || '["https://api.topcoder-dev.com", "https://api.topcoder.com", "https://topcoder-dev.auth0.com/"]', + API_VERSION: process.env.API_VERSION || "v5", + AUTH_SECRET: process.env.AUTH_SECRET || "mysecret", + VALID_ISSUERS: + process.env.VALID_ISSUERS || + '["https://api.topcoder-dev.com", "https://api.topcoder.com", "https://topcoder-dev.auth0.com/"]', // used to get M2M token AUTH0_URL: process.env.AUTH0_URL, AUTH0_PROXY_SERVER_URL: process.env.AUTH0_PROXY_SERVER_URL, - AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || 'https://www.topcoder-dev.com', + AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || "https://www.topcoder-dev.com", TOKEN_CACHE_TIME: process.env.TOKEN_CACHE_TIME, AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET: process.env.AUTH0_CLIENT_SECRET, // bus API config params - BUSAPI_URL: process.env.BUSAPI_URL || 'https://api.topcoder-dev.com/v5', - KAFKA_ERROR_TOPIC: process.env.KAFKA_ERROR_TOPIC || 'common.error.reporting', - SCHEDULING_TOPIC: process.env.SCHEDULING_TOPIC || 'challenge.notification.schedule.update', + BUSAPI_URL: process.env.BUSAPI_URL || "https://api.topcoder-dev.com/v5", + KAFKA_ERROR_TOPIC: process.env.KAFKA_ERROR_TOPIC || "common.error.reporting", + SCHEDULING_TOPIC: process.env.SCHEDULING_TOPIC || "challenge.notification.schedule.update", AMAZON: { // AWS_ACCESS_KEY_ID: process.env.AWS_FAKE_ID || 'FAKE_ACCESS_KEY', // AWS_SECRET_ACCESS_KEY: process.env.AWS_FAKE_KEY || 'FAKE_SECRET_ACCESS_KEY', - AWS_REGION: process.env.AWS_REGION || 'ap-northeast-1', + AWS_REGION: process.env.AWS_REGION || "ap-northeast-1", IS_LOCAL_DB: process.env.IS_LOCAL_DB || true, - DYNAMODB_URL: process.env.DYNAMODB_URL || 'http://localhost:7777', - S3_API_VERSION: process.env.S3_API_VERSION || '2006-03-01', - BUCKET_WHITELIST: process.env.BUCKET_WHITELIST || 'topcoder_01, topcoder_02' + DYNAMODB_URL: process.env.DYNAMODB_URL || "http://localhost:7777", + S3_API_VERSION: process.env.S3_API_VERSION || "2006-03-01", + BUCKET_WHITELIST: process.env.BUCKET_WHITELIST || "topcoder_01, topcoder_02", }, ES: { // above AWS_REGION is used if we use AWS ES - HOST: process.env.ES_HOST || 'localhost:9200', - API_VERSION: process.env.ES_API_VERSION || '6.8', - ES_INDEX: process.env.ES_INDEX || 'challenge', - ES_TYPE: process.env.ES_TYPE || '_doc', // ES 6.x accepts only 1 Type per index and it's mandatory to define it - ES_REFRESH: process.env.ES_REFRESH || 'true', - TEMP_REINDEXING: process.env.TEMP_REINDEXING || true // if true, it won't delete the existing index when reindexing data + HOST: process.env.ES_HOST || "localhost:9200", + API_VERSION: process.env.ES_API_VERSION || "6.8", + OPENSEARCH: process.env.OPENSEARCH || "false", + ES_INDEX: process.env.ES_INDEX || "challenge", + ES_TYPE: process.env.ES_TYPE || "_doc", + ES_REFRESH: process.env.ES_REFRESH || "true", + TEMP_REINDEXING: process.env.TEMP_REINDEXING || true, // if true, it won't delete the existing index when reindexing data }, // in bytes FILE_UPLOAD_SIZE_LIMIT: process.env.FILE_UPLOAD_SIZE_LIMIT - ? Number(process.env.FILE_UPLOAD_SIZE_LIMIT) : 50 * 1024 * 1024, // 50M + ? Number(process.env.FILE_UPLOAD_SIZE_LIMIT) + : 50 * 1024 * 1024, // 50M // TODO: change this to localhost - SUBMISSIONS_API_URL: process.env.SUBMISSIONS_API_URL || 'https://api.topcoder-dev.com/v5/submissions', - MEMBERS_API_URL: process.env.MEMBERS_API_URL || 'https://api.topcoder-dev.com/v5/members', - RESOURCES_API_URL: process.env.RESOURCES_API_URL || 'http://localhost:4000/v5/resources', + SUBMISSIONS_API_URL: + process.env.SUBMISSIONS_API_URL || "https://api.topcoder-dev.com/v5/submissions", + MEMBERS_API_URL: process.env.MEMBERS_API_URL || "https://api.topcoder-dev.com/v5/members", + RESOURCES_API_URL: process.env.RESOURCES_API_URL || "http://localhost:4000/v5/resources", // TODO: change this to localhost - RESOURCE_ROLES_API_URL: process.env.RESOURCE_ROLES_API_URL || 'http://api.topcoder-dev.com/v5/resource-roles', - GROUPS_API_URL: process.env.GROUPS_API_URL || 'http://localhost:4000/v5/groups', - PROJECTS_API_URL: process.env.PROJECTS_API_URL || 'http://localhost:4000/v5/projects', - TERMS_API_URL: process.env.TERMS_API_URL || 'http://localhost:4000/v5/terms', - CUSTOMER_PAYMENTS_URL: process.env.CUSTOMER_PAYMENTS_URL || 'https://api.topcoder-dev.com/v5/customer-payments', - CHALLENGE_MIGRATION_APP_URL: process.env.CHALLENGE_MIGRATION_APP_URL || 'https://api.topcoder.com/v5/challenge-migration', + RESOURCE_ROLES_API_URL: + process.env.RESOURCE_ROLES_API_URL || "http://api.topcoder-dev.com/v5/resource-roles", + GROUPS_API_URL: process.env.GROUPS_API_URL || "http://localhost:4000/v5/groups", + PROJECTS_API_URL: process.env.PROJECTS_API_URL || "http://localhost:4000/v5/projects", + TERMS_API_URL: process.env.TERMS_API_URL || "http://localhost:4000/v5/terms", + CUSTOMER_PAYMENTS_URL: + process.env.CUSTOMER_PAYMENTS_URL || "https://api.topcoder-dev.com/v5/customer-payments", + CHALLENGE_MIGRATION_APP_URL: + process.env.CHALLENGE_MIGRATION_APP_URL || "https://api.topcoder.com/v5/challenge-migration", // copilot resource role ids allowed to upload attachment COPILOT_RESOURCE_ROLE_IDS: process.env.COPILOT_RESOURCE_ROLE_IDS - ? process.env.COPILOT_RESOURCE_ROLE_IDS.split(',') : ['10ba038e-48da-487b-96e8-8d3b99b6d18b'], - SUBMITTER_ROLE_ID: process.env.SUBMITTER_ROLE_ID || '732339e7-8e30-49d7-9198-cccf9451e221', + ? process.env.COPILOT_RESOURCE_ROLE_IDS.split(",") + : ["10ba038e-48da-487b-96e8-8d3b99b6d18b"], + SUBMITTER_ROLE_ID: process.env.SUBMITTER_ROLE_ID || "732339e7-8e30-49d7-9198-cccf9451e221", - MANAGER_ROLE_ID: process.env.MANAGER_ROLE_ID || '0e9c6879-39e4-4eb6-b8df-92407890faf1', - OBSERVER_ROLE_ID: process.env.OBSERVER_ROLE_ID || '2a4dc376-a31c-4d00-b173-13934d89e286', - CLIENT_MANAGER_ROLE_ID: process.env.OBSERVER_ROLE_ID || '9b2f1905-8128-42da-85df-ed64410f4781', + MANAGER_ROLE_ID: process.env.MANAGER_ROLE_ID || "0e9c6879-39e4-4eb6-b8df-92407890faf1", + OBSERVER_ROLE_ID: process.env.OBSERVER_ROLE_ID || "2a4dc376-a31c-4d00-b173-13934d89e286", + CLIENT_MANAGER_ROLE_ID: process.env.OBSERVER_ROLE_ID || "9b2f1905-8128-42da-85df-ed64410f4781", // topgear billing accounts - TOPGEAR_BILLING_ACCOUNTS_ID: process.env.TOPGEAR_BILLING_ACCOUNTS_ID ? process.env.TOPGEAR_BILLING_ACCOUNTS_ID.split(',') : [], + TOPGEAR_BILLING_ACCOUNTS_ID: process.env.TOPGEAR_BILLING_ACCOUNTS_ID + ? process.env.TOPGEAR_BILLING_ACCOUNTS_ID.split(",") + : [], // health check timeout in milliseconds HEALTH_CHECK_TIMEOUT: process.env.HEALTH_CHECK_TIMEOUT || 3000, SCOPES: { - READ: process.env.SCOPE_CHALLENGES_READ || 'read:challenges', - CREATE: process.env.SCOPE_CHALLENGES_CREATE || 'create:challenges', - UPDATE: process.env.SCOPE_CHALLENGES_UPDATE || 'update:challenges', - DELETE: process.env.SCOPE_CHALLENGES_DELETE || 'delete:challenges', - ALL: process.env.SCOPE_CHALLENGES_ALL || 'all:challenges' + READ: process.env.SCOPE_CHALLENGES_READ || "read:challenges", + CREATE: process.env.SCOPE_CHALLENGES_CREATE || "create:challenges", + UPDATE: process.env.SCOPE_CHALLENGES_UPDATE || "update:challenges", + DELETE: process.env.SCOPE_CHALLENGES_DELETE || "delete:challenges", + ALL: process.env.SCOPE_CHALLENGES_ALL || "all:challenges", + PAYMENT: process.env.SCOPE_PAYMENT || "create:payments", }, - DEFAULT_CONFIDENTIALITY_TYPE: process.env.DEFAULT_CONFIDENTIALITY_TYPE || 'public', + DEFAULT_CONFIDENTIALITY_TYPE: process.env.DEFAULT_CONFIDENTIALITY_TYPE || "public", - M2M_AUDIT_HANDLE: process.env.M2M_AUDIT_HANDLE || 'tcwebservice', + M2M_AUDIT_HANDLE: process.env.M2M_AUDIT_HANDLE || "tcwebservice", + M2M_AUDIT_USERID: process.env.M2M_AUDIT_USERID || 22838965, FORUM_TITLE_LENGTH_LIMIT: process.env.FORUM_TITLE_LENGTH_LIMIT || 90, - NEW_SELF_SERVICE_PROJECT_TYPE: process.env.NEW_SELF_SERVICE_PROJECT_TYPE || 'self-service', + NEW_SELF_SERVICE_PROJECT_TYPE: process.env.NEW_SELF_SERVICE_PROJECT_TYPE || "self-service", AXIOS_RETRIES: process.env.AXIOS_RETRIES || 3, SENDGRID_TEMPLATES: { - WORK_REQUEST_SUBMITTED: process.env.WORK_REQUEST_SUBMITTED || '', - WORK_REQUEST_STARTED: process.env.WORK_REQUEST_STARTED || '', - WORK_REQUEST_REDIRECTED: process.env.WORK_REQUEST_REDIRECTED || '', - WORK_COMPLETED: process.env.WORK_COMPLETED || '' + WORK_REQUEST_SUBMITTED: process.env.WORK_REQUEST_SUBMITTED || "", + WORK_REQUEST_STARTED: process.env.WORK_REQUEST_STARTED || "", + WORK_REQUEST_REDIRECTED: process.env.WORK_REQUEST_REDIRECTED || "", + WORK_COMPLETED: process.env.WORK_COMPLETED || "", }, - EMAIL_FROM: process.env.EMAIL_FROM || 'no-reply@topcoder.com', - SELF_SERVICE_EMAIL_CC_ACCOUNTS: process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS ? _.map(process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS.split(','), email => ({ email })) : [{ email: 'sathya.jayabal@gmail.com' }], - SELF_SERVICE_WHITELIST_HANDLES: process.env.SELF_SERVICE_WHITELIST_HANDLES ? process.env.SELF_SERVICE_WHITELIST_HANDLES.split(',') : ['TCConnCopilot', 'sstestcopilot'], - SELF_SERVICE_APP_URL: process.env.SELF_SERVICE_APP_URL || 'https://platform.topcoder-dev.com/self-service', - ZENDESK_API_TOKEN: process.env.ZENDESK_API_TOKEN || '', - ZENDESK_API_URL: process.env.ZENDESK_API_URL || '', + EMAIL_FROM: process.env.EMAIL_FROM || "no-reply@topcoder.com", + SELF_SERVICE_EMAIL_CC_ACCOUNTS: process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS + ? _.map(process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS.split(","), (email) => ({ email })) + : [{ email: "sathya.jayabal@gmail.com" }], + SELF_SERVICE_WHITELIST_HANDLES: process.env.SELF_SERVICE_WHITELIST_HANDLES + ? process.env.SELF_SERVICE_WHITELIST_HANDLES.split(",") + : ["TCConnCopilot", "sstestcopilot"], + SELF_SERVICE_APP_URL: + process.env.SELF_SERVICE_APP_URL || "https://platform.topcoder-dev.com/self-service", + ZENDESK_API_TOKEN: process.env.ZENDESK_API_TOKEN || "", + ZENDESK_API_URL: process.env.ZENDESK_API_URL || "", ZENDESK_CUSTOM_FIELD_TAG_ID: process.env.ZENDESK_CUSTOM_FIELD_TAG_ID, - ZENDESK_DEFAULT_PRIORITY: process.env.ZENDESK_DEFAULT_PRIORITY || 'high', - INTERNAL_CACHE_TTL: process.env.INTERNAL_CACHE_TTL || 1800 -} + ZENDESK_DEFAULT_PRIORITY: process.env.ZENDESK_DEFAULT_PRIORITY || "high", + INTERNAL_CACHE_TTL: process.env.INTERNAL_CACHE_TTL || 1800, + GRPC_CHALLENGE_SERVER_HOST: process.env.GRPC_DOMAIN_CHALLENGE_SERVER_HOST || "localhost", + GRPC_CHALLENGE_SERVER_PORT: process.env.GRPC_DOMAIN_CHALLENGE_SERVER_PORT || 8888, + GRPC_ACL_SERVER_HOST: process.env.GRPC_ACL_SERVER_HOST || "localhost", + GRPC_ACL_SERVER_PORT: process.env.GRPC_ACL_SERVER_PORT || 40020, + + SKIP_PROJECT_ID_BY_TIMLINE_TEMPLATE_ID: + process.env.SKIP_PROJECT_ID_BY_TIMLINE_TEMPLATE_ID || "517e76b0-8824-4e72-9b48-a1ebde1793a8", +}; diff --git a/docker/Dockerfile b/docker/Dockerfile index 2a9b952c..7edc492e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # Use the base image with Node.js -FROM node:14.21.2-bullseye +FROM node:18.14.1-alpine3.17 # Copy the current directory into the Docker image COPY . /challenge-api @@ -10,4 +10,4 @@ WORKDIR /challenge-api # Install the dependencies from package.json RUN yarn install -CMD yarn start \ No newline at end of file +CMD node /challenge-api/app.js diff --git a/docs/prod.env b/docs/prod.env index 3393a3c8..7c022cd8 100644 --- a/docs/prod.env +++ b/docs/prod.env @@ -14,7 +14,6 @@ S3_API_VERSION= ES_HOST= ES_API_VERSION= ES_INDEX= -ES_TYPE= ES_REFRESH=true RESOURCES_API_URL= GROUPS_API_URL= diff --git a/docs/swagger.yaml b/docs/swagger.yaml index e261a2ed..3e4edb8d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -58,6 +58,7 @@ paths: produces: - application/json parameters: + - $ref: "#/parameters/app-version" - $ref: "#/parameters/page" - $ref: "#/parameters/perPage" - name: id @@ -472,6 +473,7 @@ paths: produces: - application/json parameters: + - $ref: "#/parameters/app-version" - in: body name: body required: true @@ -512,6 +514,7 @@ paths: produces: - application/json parameters: + - $ref: "#/parameters/app-version" - name: challengeId in: path required: true @@ -548,6 +551,7 @@ paths: produces: - application/json parameters: + - $ref: "#/parameters/app-version" - name: challengeId in: path required: true @@ -593,6 +597,7 @@ paths: produces: - application/json parameters: + - $ref: "#/parameters/app-version" - name: challengeId in: path required: true @@ -2398,6 +2403,19 @@ parameters: minimum: 1 default: 100 maximum: 100 + app-version: + name: app-version + in: header + description: > + The app version: + + - 1.1.0: With 1.1.0 or later version, challenge-api supports create/update challenge with "skills" data in request payload, + and with "skills" data in response body when get/search/create/update challenges. + + - 2.0.0: With 2.0.0 or later version, challenge-api supports "payments" data in response body when get/search/create/update challenges for admin/m2m user. + required: false + type: string + default: 1.0.0 definitions: Challenge: type: object @@ -2643,6 +2661,39 @@ definitions: type: string description: Auto-generated example: Development + payments: + type: array + description: The payments of the challenge, only applicable when "app-version" header >= 2.0.0 for admin/m2m user + items: + type: object + properties: + handle: + type: string + type: + type: string + amount: + type: number + userId: + type: number + skills: + type: array + description: The skills of the challenge, only applicable when "app-version" header >= 1.1.0 + items: + type: object + properties: + id: + type: string + format: UUID + name: + type: string + category: + type: object + properties: + id: + type: string + format: UUID + name: + type: string required: - typeId - trackId @@ -2792,6 +2843,17 @@ definitions: required: - id - roleId + skills: + type: array + description: The ids of the skills to add for the challenge, only applicable when "app-version" header >= 1.1.0 + items: + type: object + properties: + id: + type: string + format: UUID + required: + - id required: - typeId - trackId @@ -2975,6 +3037,17 @@ definitions: required: - id - roleId + skills: + type: array + description: The ids of the skills to add for the challenge, only applicable when "app-version" header >= 1.1.0 + items: + type: object + properties: + id: + type: string + format: UUID + required: + - id required: - typeId - trackId @@ -3159,6 +3232,17 @@ definitions: required: - id - roleId + skills: + type: array + description: The ids of the skills to add for the challenge, only applicable when "app-version" header >= 1.1.0 + items: + type: object + properties: + id: + type: string + format: UUID + required: + - id SearchChallengeRequestBody: type: object properties: diff --git a/local/docker-compose.yml b/local/docker-compose.yml index 88b4db1c..b5e97b5f 100644 --- a/local/docker-compose.yml +++ b/local/docker-compose.yml @@ -1,33 +1,33 @@ version: '3' services: - dynamodb: - image: tray/dynamodb-local - ports: - - "8000:8000" - command: "-inMemory -port 8000" + # dynamodb: + # image: amazon/dynamodb-local + # ports: + # - "8000:8000" + # command: "-inMemory -port 8000" esearch: image: "docker.elastic.co/elasticsearch/elasticsearch:6.8.0" ports: - "9200:9200" environment: - "discovery.type=single-node" - minio1: - image: minio/minio - ports: - - "9000:9000" - environment: - MINIO_ACCESS_KEY: "FAKE_ACCESS_KEY" - MINIO_SECRET_KEY: "FAKE_SECRET_ACCESS_KEY" - command: "server /data" - mock-api: - image: mock-api:latest - build: - context: ../ - dockerfile: mock-api/Dockerfile - volumes: - - ../mock-api:/challenge-api/mock-api - environment: - DYNAMODB_URL: http://dynamodb:7777 - IS_LOCAL_DB: "true" - ports: - - "4000:4000" + # minio1: + # image: minio/minio + # ports: + # - "9000:9000" + # environment: + # MINIO_ACCESS_KEY: "FAKE_ACCESS_KEY" + # MINIO_SECRET_KEY: "FAKE_SECRET_ACCESS_KEY" + # command: "server /data" + # mock-api: + # image: mock-api:latest + # build: + # context: ../ + # dockerfile: mock-api/Dockerfile + # volumes: + # - ../mock-api:/challenge-api/mock-api + # environment: + # DYNAMODB_URL: http://dynamodb:7777 + # IS_LOCAL_DB: "true" + # ports: + # - "4000:4000" diff --git a/local/start-xray-daemon.sh b/local/start-xray-daemon.sh new file mode 100755 index 00000000..a54dbc9b --- /dev/null +++ b/local/start-xray-daemon.sh @@ -0,0 +1,7 @@ +docker run --attach STDOUT \ + -e AWS_REGION=${AWS_REGION} \ + -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ + -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ + -e AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN} \ + --publish 2000:2000/udp \ + public.ecr.aws/xray/aws-xray-daemon -o \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8c0bc8cd..00000000 --- a/package-lock.json +++ /dev/null @@ -1,5351 +0,0 @@ -{ - "name": "topcoder-challenges-api", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/generator": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", - "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", - "dev": true, - "requires": { - "@babel/types": "^7.18.2", - "@jridgewell/gen-mapping": "^0.3.0", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz", - "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", - "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", - "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", - "dev": true - }, - "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz", - "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.2", - "@babel/helper-environment-visitor": "^7.18.2", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.18.0", - "@babel/types": "^7.18.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz", - "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" - }, - "@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", - "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", - "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/chai": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", - "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", - "dev": true - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-jwt": { - "version": "0.0.42", - "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", - "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", - "requires": { - "@types/express": "*", - "@types/express-unless": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/express-unless": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.3.tgz", - "integrity": "sha512-TyPLQaF6w8UlWdv4gj8i46B+INBVzURBNRahCozCSXfsK2VTlL1wNyTlMKw817VHygBtlcl5jfnPadlydr06Yw==", - "requires": { - "@types/express": "*" - } - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "@types/node": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.0.tgz", - "integrity": "sha512-D5Rt+HXgEywr3RQJcGlZUCTCx1qVbCZpVk3/tOOA6spLNZdGm8BU+zRgdRYDoF1pO3RuXLxADzMrF903JlQXqg==" - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/superagent": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.8.7.tgz", - "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", - "dev": true, - "requires": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "array-includes": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", - "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - } - }, - "array.prototype.reduce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", - "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - } - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "aws-sdk": { - "version": "2.1146.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1146.0.tgz", - "integrity": "sha512-lg83hvrK2oiJVnklUVMXIJkeYX2nlqhvxIFlZ2wfoaJyvdGsEcOUdZ/EMDgiS0V2jwGS8CtTUypcW/t2S6gdcQ==", - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "8.0.0", - "xml2js": "0.4.19" - }, - "dependencies": { - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" - } - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, - "axios-retry": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", - "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", - "requires": { - "@babel/runtime": "^7.15.4", - "is-retry-allowed": "^2.2.0" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", - "dev": true - } - } - }, - "babel-runtime": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", - "integrity": "sha512-5pdhO3jaxqh9L42oBfbrqy58swDhciM47sRGoODURdRxwfiqttEvK87LX27W/PYY6f4cJt2mEdyoLcr/+cM/iw==", - "requires": { - "core-js": "^2.1.0" - } - }, - "backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", - "requires": { - "precond": "0.2" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "bunyan": { - "version": "1.8.15", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", - "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", - "requires": { - "dtrace-provider": "~0.8", - "moment": "^2.19.3", - "mv": "~2", - "safe-json-stringify": "~1" - } - }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { - "streamsearch": "^1.1.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "dev": true, - "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g==", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "chai-http": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.3.0.tgz", - "integrity": "sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg==", - "dev": true, - "requires": { - "@types/chai": "4", - "@types/superagent": "^3.8.3", - "cookiejar": "^2.1.1", - "is-ip": "^2.0.0", - "methods": "^1.1.2", - "qs": "^6.5.1", - "superagent": "^3.7.0" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" - }, - "codependency": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/codependency/-/codependency-0.1.4.tgz", - "integrity": "sha512-26yAvd3+17xSfDADtnzpnL5GK+8+x4QeZ3DegekkHyno6LWeHqXuSU7q8w/IrAur7SY6ISPApOWtWTfuIF0Xpg==", - "requires": { - "semver": "5.0.1" - }, - "dependencies": { - "semver": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.1.tgz", - "integrity": "sha512-Ne6/HdGZvvpXBdjW3o8J0pvxC2jnmVNBK7MKkMgsOBfrsIdTXfA5x+H9DUbQ2xzyvnLv0A0v9x8R4B40xNZIRQ==" - } - } - }, - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "requires": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "config": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/config/-/config-3.3.7.tgz", - "integrity": "sha512-mX/n7GKDYZMqvvkY6e6oBY49W8wxdmQt+ho/5lhwFDXqQW9gI+Ahp8EKp8VAbISPnmf2+Bv5uZK7lKXZ6pf1aA==", - "requires": { - "json5": "^2.1.1" - } - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - } - }, - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "cssfilter": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", - "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha512-gV/pe1YIaKNgLYnd1g9VNW80tcb7oV5qvNUxG7NM8rbDpnl6RGunzlAtlGSb0wEs3nesu2vHNiX9TSsZ+Y+RjA==", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==" - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "deglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", - "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", - "dev": true, - "requires": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^3.0.9", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" - }, - "dtrace-provider": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", - "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", - "optional": true, - "requires": { - "nan": "^2.14.0" - } - }, - "dynamoose": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/dynamoose/-/dynamoose-1.11.1.tgz", - "integrity": "sha512-73T0GOeSh2FQDdK5V+cN9yowFU4uVt2OsOvknCB4Xdb+nSL/hBaecPY/meJaWZApVx73Hl1DcBSCGp/VWMvfRA==", - "requires": { - "@types/node": "11.11.0", - "aws-sdk": "2.395.0", - "debug": "4.1.1", - "deep-equal": "1.0.1", - "hooks": "0.3.2", - "object-path": "0.11.4", - "q": "1.5.1" - }, - "dependencies": { - "aws-sdk": { - "version": "2.395.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.395.0.tgz", - "integrity": "sha512-ldTTjctniZT4E2lq2z3D8Y2u+vpkp+laoEnDkXgjKXTKbiJ0QEtfWsUdx/IQ7awCt8stoxyqZK47DJOxIbRNoA==", - "requires": { - "buffer": "4.9.1", - "events": "1.1.1", - "ieee754": "1.1.8", - "jmespath": "0.15.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.3.2", - "xml2js": "0.4.19" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha512-DNK4ruAqtyHaN8Zne7PkBTO+dD1Lr0YfTduMqlIyjvQIoztBkUxrvL+hKeLW8NXFKHOq/2upkxuoS9znQ9bW9A==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha512-/aoyv2Nt7mGLnCAWzE0C1WH9Xd8ZsqR0f4Pjwxputi1JNm01+InyAYQotF4N+ulEIjbEsJo22NOHr+U/XEZ1Pw==" - }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "elasticsearch": { - "version": "16.7.3", - "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-16.7.3.tgz", - "integrity": "sha512-e9kUNhwnIlu47fGAr4W6yZJbkpsgQJB0TqNK8rCANe1J4P65B1sGnbCFTgcKY3/dRgCWnuP1AJ4obvzW604xEQ==", - "requires": { - "agentkeepalive": "^3.4.1", - "chalk": "^1.0.0", - "lodash": "^4.17.10" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - } - } - }, - "es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - }, - "dependencies": { - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - } - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "eslint": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", - "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", - "dev": true, - "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.2", - "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "eslint-config-standard": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", - "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz", - "integrity": "sha512-D+YWAoXw+2GIdbMBRAzWwr1ZtvnSf4n4yL0gKGg7ShUOGXkSOLerI17K4F6LdQMJPNMoWYqepzQD/fKY+tXNSg==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "find-up": "^2.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - } - } - }, - "eslint-plugin-es": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz", - "integrity": "sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA==", - "dev": true, - "requires": { - "eslint-utils": "^1.4.2", - "regexpp": "^2.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", - "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", - "dev": true, - "requires": { - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.2.0", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0", - "resolve": "^1.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", - "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^4.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - } - }, - "eslint-plugin-promise": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", - "integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==", - "dev": true - }, - "eslint-plugin-react": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", - "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.2" - } - }, - "eslint-plugin-standard": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.2.tgz", - "integrity": "sha512-nKptN8l7jksXkwFk++PhJB3cCDTcXOEyhISIN86Ue2feJ1LFyY3PrY3/xT2keXlJSY5bpmbiTG0f885/YKAvTA==", - "dev": true - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", - "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", - "dev": true, - "requires": { - "acorn": "^6.0.2", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" - }, - "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.0", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.10.3", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "express-fileupload": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.0.tgz", - "integrity": "sha512-RjzLCHxkv3umDeZKeFeMg8w7qe0V09w3B7oGZprr/oO2H/ISCgNzuqzn7gV3HRWb37GjRk429CCpSLS2KNTqMQ==", - "requires": { - "busboy": "^1.6.0" - } - }, - "express-interceptor": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/express-interceptor/-/express-interceptor-1.2.0.tgz", - "integrity": "sha512-fCbcJv8ZwabDg0M/3PmHUxfr/WKHGMpAicR9TfGdhANV4M1GBDSrBTenHIK3aegyRN5S6eDwlvyNFiLynnc19w==", - "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha512-uXP/zGzxxFvFfcZGgBIwotm+Tdc55ddPAzF7iHshP4YGaXMww7rSF9peD9D1sui5ebONg5UobsZv+FfgEpGv/w==", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } - }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - } - }, - "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha512-3TOY+4TKV0Ml83PXJQY+JFQaHNV38lzQDIzzXYg1kWdBLenGgoZhAs0CKgzI31vi2pWEpQMq/Yi4bpKwCPkw7g==", - "dev": true, - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-parameter-names": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/get-parameter-names/-/get-parameter-names-0.3.0.tgz", - "integrity": "sha512-KkR1dX7U1TynXFkqveVE/XoRn9qRAsM2q4Eu2WsGTFzoaSdnNQEfxbcK+LMv8DcFoQQT9BFjNL+bf9ZyTLkWpg==" - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", - "optional": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha512-w0Kz8lJFBoyaurBiNrIvxPqr/gJ6fOfSkpAPOepN3oECqGJag37xPbOv57izi/KP8auHgNYxn5fXtAb+1LsJ6w==", - "dev": true, - "requires": { - "is-stream": "^1.0.1" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true - } - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" - }, - "hooks": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/hooks/-/hooks-0.3.2.tgz", - "integrity": "sha512-TqeFzUf12rSzcbm5lUls81jimUC8TmXZ4ANPxxeeMou09hrjBcHYhAQ0WgyN5YqNCXOzz7L6xVNl/+ctFuSeOw==" - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-aws-es": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/http-aws-es/-/http-aws-es-6.0.0.tgz", - "integrity": "sha512-g+qp7J110/m4aHrR3iit4akAlnW0UljZ6oTq/rCcbsI8KP9x+95vqUtx49M2XQ2JMpwJio3B6gDYx+E8WDxqiA==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "http-status-codes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz", - "integrity": "sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==" - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "is-ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", - "integrity": "sha512-9MTn0dteHETtyUx8pxqMwg5hMBi3pvlyglJ+b79KOCca0po23337LbVV2Hl4xmMvfw++ljnO0/+5G6G+0Szh6g==", - "dev": true, - "requires": { - "ip-regex": "^2.0.0" - } - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0" - } - }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" - }, - "joi": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", - "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", - "requires": { - "hoek": "6.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "jsx-ast-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", - "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "object.assign": "^4.1.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jwks-rsa": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.12.3.tgz", - "integrity": "sha512-cFipFDeYYaO9FhhYJcZWX/IyZgc0+g316rcHnDpT2dNRNIE/lMOmWKKqp09TkJoYlNFzrEVODsR4GgXJMgWhnA==", - "requires": { - "@types/express-jwt": "0.0.42", - "axios": "^0.21.1", - "debug": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "jsonwebtoken": "^8.5.1", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.2", - "ms": "^2.1.2", - "proxy-from-env": "^1.1.0" - }, - "dependencies": { - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - } - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "logform": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz", - "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==", - "requires": { - "@colors/colors": "1.5.0", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, - "lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", - "requires": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - }, - "lru-memoizer": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", - "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", - "requires": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "millisecond": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/millisecond/-/millisecond-0.1.2.tgz", - "integrity": "sha512-BJ8XtxY+woL+5TkP6uS6XvOArm0JVrX2otkgtWZseHpIax0oOOPW3cnwhOjRqbEJg7YRO/BDF7fO/PTWNT3T9Q==" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "mocha": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", - "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.4", - "ms": "2.1.1", - "node-environment-flags": "1.0.5", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", - "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "mocha-prepare": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/mocha-prepare/-/mocha-prepare-0.1.0.tgz", - "integrity": "sha512-ZSa/Zq+CQv9ZS7kfgsB3iRbfM4WBLn+Oy3EHDp3IXxbUVmVrcu+cs2YOELH77Gj3amkHjhCpM5CAZHV6jz6scg==", - "dev": true - }, - "moment": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", - "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", - "dev": true - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "optional": true, - "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - } - }, - "nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", - "optional": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "optional": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "nested-error-stacks": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", - "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "requires": { - "clone": "2.x" - } - }, - "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-path": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", - "integrity": "sha512-ICbQN+aw/eAASDtaC7+SJXSAruz7fvvNjxMFfS3mTdvZaaiuuw81XXYu+9CSJeUVrS3YpRhTr862YGywMQUOWg==" - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", - "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", - "dev": true, - "requires": { - "array.prototype.reduce": "^1.0.4", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.1" - } - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - } - } - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - } - } - }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw==", - "dev": true, - "requires": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "r7insight_node": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/r7insight_node/-/r7insight_node-1.8.4.tgz", - "integrity": "sha512-6cQrzLkaOxdv/SRFXWRJjgFr8a3nXUOT/4IMFuBv+mWzBnu5DJl+HzONAsWYvclrlZnvfa54PaIPqPuPRSlbrQ==", - "requires": { - "babel-runtime": "6.6.1", - "codependency": "0.1.4", - "json-stringify-safe": "5.0.1", - "lodash": "4.17.15", - "reconnect-core": "1.3.0", - "semver": "5.1.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "semver": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", - "integrity": "sha512-sfKXKhcz5XVyfUZa2V4RbjK0xjOJCMLNF9H4p4v0UCo9wNHM/lH9RDuyDbGEtxWLMDlPBc8xI7AbbVLKXty+rQ==" - } - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "reconnect-core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", - "integrity": "sha512-+gLKwmyRf2tjl6bLR03DoeWELzyN6LW9Xgr3vh7NXHHwPi0JC0N2TwPyf90oUEBkCRcD+bgQ+s3HORoG3nwHDg==", - "requires": { - "backoff": "~2.5.0" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", - "dev": true - } - } - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", - "optional": true, - "requires": { - "glob": "^6.0.1" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-json-stringify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", - "optional": true - }, - "safe-stable-stringify": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", - "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", - "dev": true, - "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" - }, - "standard": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/standard/-/standard-12.0.1.tgz", - "integrity": "sha512-UqdHjh87OG2gUrNCSM4QRLF5n9h3TFPwrCNyVlkqu31Hej0L/rc8hzKqVvkb2W3x0WMq7PzZdkLfEcBhVOR6lg==", - "dev": true, - "requires": { - "eslint": "~5.4.0", - "eslint-config-standard": "12.0.0", - "eslint-config-standard-jsx": "6.0.2", - "eslint-plugin-import": "~2.14.0", - "eslint-plugin-node": "~7.0.1", - "eslint-plugin-promise": "~4.0.0", - "eslint-plugin-react": "~7.11.1", - "eslint-plugin-standard": "~4.0.0", - "standard-engine": "~9.0.0" - } - }, - "standard-engine": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-9.0.0.tgz", - "integrity": "sha512-ZfNfCWZ2Xq67VNvKMPiVMKHnMdvxYzvZkf1AH8/cw2NLDBm5LRsxMqvEJpsjLI/dUosZ3Z1d6JlHDp5rAvvk2w==", - "dev": true, - "requires": { - "deglob": "^2.1.0", - "get-stdin": "^6.0.0", - "minimist": "^1.1.0", - "pkg-conf": "^2.0.0" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - }, - "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "swagger-ui-dist": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.11.1.tgz", - "integrity": "sha512-pf3kfSTYdF9mYFY2VnfJ51wnXlSVhEGdtymhpHzfbFw2jTbiEWgBoVz5EB9aW2EaJvUGTM1YHAXYZX7Jk4RdAQ==" - }, - "swagger-ui-express": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz", - "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==", - "requires": { - "swagger-ui-dist": ">=4.11.0" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", - "dev": true - }, - "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "tc-core-library-js": { - "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", - "from": "github:appirio-tech/tc-core-library-js#v2.6.4", - "requires": { - "axios": "^0.19.0", - "bunyan": "^1.8.12", - "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^1.6.0", - "lodash": "^4.17.15", - "millisecond": "^0.1.2", - "r7insight_node": "^1.8.4", - "request": "^2.88.0" - } - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "topcoder-bus-api-wrapper": { - "version": "github:topcoder-platform/tc-bus-api-wrapper#f8cbd335a0e0b4d6edd7cae859473593271fd97f", - "from": "github:topcoder-platform/tc-bus-api-wrapper", - "requires": { - "joi": "^13.4.0", - "lodash": "^4.17.15", - "superagent": "^3.8.3", - "tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6.4" - }, - "dependencies": { - "hoek": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", - "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" - }, - "joi": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", - "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", - "requires": { - "hoek": "5.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - } - } - }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "requires": { - "hoek": "6.x.x" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } - } - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - } - } - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "winston": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", - "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", - "requires": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" - }, - "xss": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz", - "integrity": "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==", - "requires": { - "commander": "^2.20.3", - "cssfilter": "0.0.10" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, - "yamljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", - "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", - "requires": { - "argparse": "^1.0.7", - "glob": "^7.0.5" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - } - } -} diff --git a/package.json b/package.json index 40dd562f..9de7e6b2 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,11 @@ "main": "app.js", "scripts": { "start": "node app.js", - "lint": "standard", - "lint:fix": "standard --fix", + "dev": "nodemon app.js", + "lint": "prettier src/**/*.js", + "lint:fix": "prettier --write src/**/*.js", + "standard-lint": "standard", + "standard-lint:fix": "standard --fix", "init-es": "node src/init-es.js", "init-db": "node src/init-db.js", "sync-es": "node src/scripts/sync-es.js", @@ -27,27 +30,37 @@ "local:reset": "npm run init-es force && npm run init-db force" }, "author": "TCSCODER", - "license": "none", + "license": "MIT", "repository": "https://github.com/topcoder-platform/challenge-api", "devDependencies": { "chai": "^4.2.0", "chai-http": "^4.2.1", "mocha": "^6.1.4", "mocha-prepare": "^0.1.0", + "nodemon": "^2.0.20", "nyc": "^14.0.0", - "standard": "^12.0.1" + "prettier": "^2.8.1", + "standard": "^17.1.0" }, "dependencies": { + "@grpc/grpc-js": "^1.8.12", + "@opensearch-project/opensearch": "^2.2.0", + "@topcoder-framework/domain-acl": "^0.24.0", + "@topcoder-framework/domain-challenge": "^0.24.1", + "@topcoder-framework/lib-common": "^0.24.1", "aws-sdk": "^2.1145.0", "axios": "^0.19.0", - "axios-retry": "^3.3.1", + "axios-retry": "^3.4.0", "bluebird": "^3.5.1", "body-parser": "^1.15.1", + "compare-versions": "^6.1.0", "config": "^3.0.1", - "cors": "^2.7.1", + "cors": "^2.8.5", + "decimal.js": "^10.4.3", + "deep-equal": "^2.2.0", + "dompurify": "^3.0.2", "dotenv": "^8.2.0", - "dynamoose": "^1.11.1", - "elasticsearch": "^16.1.1", + "elasticsearch": "^16.7.3", "express": "^4.15.4", "express-fileupload": "^1.1.6", "express-interceptor": "^1.2.0", @@ -55,15 +68,18 @@ "http-aws-es": "^6.0.0", "http-status-codes": "^1.3.0", "joi": "^14.0.0", + "jsdom": "^21.1.2", + "json-rules-engine": "^6.1.2", "jsonwebtoken": "^8.3.0", "lodash": "^4.17.19", + "markdown-it": "^13.0.1", "moment": "^2.24.0", "node-cache": "^5.1.2", "swagger-ui-express": "^4.1.3", "tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.6.4", "topcoder-bus-api-wrapper": "topcoder-platform/tc-bus-api-wrapper.git", "uuid": "^3.3.2", - "winston": "^3.1.0", + "winston": "^3.8.2", "xss": "^1.0.8", "yamljs": "^0.3.0" }, @@ -76,9 +92,10 @@ ] }, "engines": { - "node": "14.x" + "node": "18.x" }, "volta": { - "node": "14.21.2" + "node": "18.12.1", + "yarn": "1.22.19" } } diff --git a/src/common/challenge-helper.js b/src/common/challenge-helper.js new file mode 100644 index 00000000..34e8f8c8 --- /dev/null +++ b/src/common/challenge-helper.js @@ -0,0 +1,539 @@ +const challengeTypeService = require("../services/ChallengeTypeService"); +const challengeTrackService = require("../services/ChallengeTrackService"); +const timelineTemplateService = require("../services/TimelineTemplateService"); +const HttpStatus = require("http-status-codes"); +const _ = require("lodash"); +const errors = require("./errors"); +const config = require("config"); +const helper = require("./helper"); +const constants = require("../../app-constants"); +const axios = require("axios"); +const Decimal = require("decimal.js"); +const { getM2MToken } = require("./m2m-helper"); +const { hasAdminRole } = require("./role-helper"); +const { ensureAcessibilityToModifiedGroups } = require("./group-helper"); + +class ChallengeHelper { + /** + * @param {Object} challenge the challenge object + * @returns {Promise<{trackId, typeId}>} the challenge track and type ids + */ + async validateAndGetChallengeTypeAndTrack({ typeId, trackId, timelineTemplateId }) { + let challengeType; + if (typeId) { + challengeType = await challengeTypeService.getChallengeType(typeId); + } + + let challengeTrack; + if (trackId) { + challengeTrack = await challengeTrackService.getChallengeTrack(trackId); + } + + if (timelineTemplateId) { + const template = await timelineTemplateService.getTimelineTemplate(timelineTemplateId); + + if (!template.isActive) { + throw new errors.BadRequestError( + `The timeline template with id: ${timelineTemplateId} is inactive` + ); + } + } + + return { type: challengeType, track: challengeTrack }; + } + + /** + * Ensure project exist + * @param {String} projectId the project id + * @param {String} currentUser the user + */ + static async ensureProjectExist(projectId, currentUser) { + let token = await getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + try { + const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }); + if (currentUser.isMachine || hasAdminRole(currentUser)) { + return res.data; + } + if ( + _.get(res, "data.type") === "self-service" && + _.includes(config.SELF_SERVICE_WHITELIST_HANDLES, currentUser.handle.toLowerCase()) + ) { + return res.data; + } + if ( + !_.find( + _.get(res, "data.members", []), + (m) => _.toString(m.userId) === _.toString(currentUser.userId) + ) + ) { + throw new errors.ForbiddenError(`You don't have access to project with ID: ${projectId}`); + } + return res.data; + } catch (err) { + if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`); + } else { + // re-throw other error + throw err; + } + } + } + + /** + * Validate Challenge groups. + * @param {Object} groups the group of a challenge + */ + async validateGroups(groups) { + const promises = []; + _.each(groups, (g) => { + promises.push( + (async () => { + const group = await helper.getGroupById(g); + if (!group || group.status !== "active") { + throw new errors.BadRequestError("The groups provided are invalid " + g); + } + })() + ); + }); + await Promise.all(promises); + } + + validatePrizeSetsAndGetPrizeType(prizeSets) { + if (_.isEmpty(prizeSets)) return null; + + const firstType = _.get(prizeSets, "[0].prizes[0].type", null); + if (!firstType) return null; + + const isConsistent = _.every(prizeSets, (prizeSet) => + _.every(prizeSet.prizes, (prize) => prize.type === firstType) + ); + + if (!isConsistent) { + throw new errors.BadRequestError("All prizes must be of the same type"); + } + + return firstType; + } + + /** + * Validate Challenge skills. + * @param {Object} challenge the challenge + * @param {oldChallenge} challenge the old challenge data + */ + async validateSkills(challenge, oldChallenge) { + if (!challenge.skills) { + return; + } + + const ids = _.uniq(_.map(challenge.skills, "id")); + + if (oldChallenge && oldChallenge.status === constants.challengeStatuses.Completed) { + // Don't allow edit skills for Completed challenges + if (!_.isEqual(ids, _.uniq(_.map(oldChallenge.skills, "id")))) { + throw new errors.BadRequestError( + "Cannot update skills for challenges with Completed status" + ); + } + } + + if (!ids.length) { + return; + } + + const standSkills = await helper.getStandSkills(ids); + + const skills = []; + for (const id of ids) { + const found = _.find(standSkills, (item) => item.id === id); + if (!found) { + throw new errors.BadRequestError("The skill id is invalid " + id); + } + + const skill = { + id, + name: found.name, + }; + + if (found.category) { + skill.category = { + id: found.category.id, + name: found.category.name, + }; + } + + skills.push(skill); + } + challenge.skills = skills; + } + + async validateCreateChallengeRequest(currentUser, challenge) { + // projectId is required for non self-service challenges + if ( + challenge.legacy.selfService == null && + challenge.projectId == null && + this.isProjectIdRequired(challenge.timelineTemplateId) + ) { + throw new errors.BadRequestError("projectId is required for non self-service challenges."); + } + + if (challenge.status === constants.challengeStatuses.Active) { + throw new errors.BadRequestError( + "You cannot create an Active challenge. Please create a Draft challenge and then change the status to Active." + ); + } + + helper.ensureNoDuplicateOrNullElements(challenge.tags, "tags"); + helper.ensureNoDuplicateOrNullElements(challenge.groups, "groups"); + // helper.ensureNoDuplicateOrNullElements(challenge.terms, 'terms') + // helper.ensureNoDuplicateOrNullElements(challenge.events, 'events') + + // check groups authorization + if (challenge.groups && challenge.groups.length > 0) { + if (currentUser.isMachine || hasAdminRole(currentUser)) { + await this.validateGroups(challenge.groups); + } else { + await helper.ensureAccessibleByGroupsAccess(currentUser, challenge); + } + } + + // check skills + await this.validateSkills(challenge); + + if (challenge.constraints) { + await ChallengeHelper.validateChallengeConstraints(challenge.constraints); + } + } + + async validateChallengeUpdateRequest(currentUser, challenge, data, challengeResources) { + if (process.env.LOCAL != "true") { + await helper.ensureUserCanModifyChallenge(currentUser, challenge, challengeResources); + } + + helper.ensureNoDuplicateOrNullElements(data.tags, "tags"); + helper.ensureNoDuplicateOrNullElements(data.groups, "groups"); + + if (data.projectId) { + await ChallengeHelper.ensureProjectExist(data.projectId, currentUser); + } + + // check groups access to be updated group values + if (data.groups && data.groups.length > 0) { + if (currentUser.isMachine || hasAdminRole(currentUser)) { + await this.validateGroups(data.groups); + } else { + await ensureAcessibilityToModifiedGroups(currentUser, data, challenge); + } + } + + // check skills + await this.validateSkills(data, challenge); + + // Ensure descriptionFormat is either 'markdown' or 'html' + if (data.descriptionFormat && !_.includes(["markdown", "html"], data.descriptionFormat)) { + throw new errors.BadRequestError( + "The property 'descriptionFormat' must be either 'markdown' or 'html'" + ); + } + + // Ensure unchangeable fields are not changed + if ( + _.get(challenge, "legacy.track") && + _.get(data, "legacy.track") && + _.get(challenge, "legacy.track") !== _.get(data, "legacy.track") + ) { + throw new errors.ForbiddenError("Cannot change legacy.track"); + } + + if ( + _.get(challenge, "trackId") && + _.get(data, "trackId") && + _.get(challenge, "trackId") !== _.get(data, "trackId") + ) { + throw new errors.ForbiddenError("Cannot change trackId"); + } + + if ( + _.get(challenge, "typeId") && + _.get(data, "typeId") && + _.get(challenge, "typeId") !== _.get(data, "typeId") + ) { + throw new errors.ForbiddenError("Cannot change typeId"); + } + + if ( + _.get(challenge, "legacy.pureV5Task") && + _.get(data, "legacy.pureV5Task") && + _.get(challenge, "legacy.pureV5Task") !== _.get(data, "legacy.pureV5Task") + ) { + throw new errors.ForbiddenError("Cannot change legacy.pureV5Task"); + } + + if ( + _.get(challenge, "legacy.pureV5") && + _.get(data, "legacy.pureV5") && + _.get(challenge, "legacy.pureV5") !== _.get(data, "legacy.pureV5") + ) { + throw new errors.ForbiddenError("Cannot change legacy.pureV5"); + } + + if ( + _.get(challenge, "legacy.selfService") && + _.get(data, "legacy.selfService") && + _.get(challenge, "legacy.selfService") !== _.get(data, "legacy.selfService") + ) { + throw new errors.ForbiddenError("Cannot change legacy.selfService"); + } + + if ( + (challenge.status === constants.challengeStatuses.Completed || + challenge.status === constants.challengeStatuses.Cancelled) && + data.status && + data.status !== challenge.status && + data.status !== constants.challengeStatuses.CancelledClientRequest + ) { + throw new errors.BadRequestError( + `Cannot change ${challenge.status} challenge status to ${data.status} status` + ); + } + + if ( + data.winners && + data.winners.length > 0 && + challenge.status !== constants.challengeStatuses.Completed && + data.status !== constants.challengeStatuses.Completed + ) { + throw new errors.BadRequestError( + `Cannot set winners for challenge with non-completed ${challenge.status} status` + ); + } + + if (data.constraints) { + await ChallengeHelper.validateChallengeConstraints(data.constraints); + } + } + + static async validateChallengeConstraints(constraints) { + if (!_.isEmpty(constraints.allowedRegistrants)) { + await ChallengeHelper.validateAllowedRegistrants(constraints.allowedRegistrants); + } + } + + static async validateAllowedRegistrants(allowedRegistrants) { + const members = await helper.getMembersByHandles(allowedRegistrants); + const incorrectHandles = _.difference( + allowedRegistrants, + _.map(members, (m) => _.toLower(m.handle)) + ); + if (incorrectHandles.length > 0) { + throw new errors.BadRequestError( + `Cannot create challenge with invalid handle in constraints. [${_.join( + incorrectHandles, + "," + )}]` + ); + } + } + + sanitizeRepeatedFieldsInUpdateRequest(data) { + if (data.winners != null) { + data.winnerUpdate = { + winners: data.winners, + }; + delete data.winners; + } + + if (data.discussions != null) { + data.discussionUpdate = { + discussions: data.discussions, + }; + delete data.discussions; + } + + if (data.metadata != null) { + data.metadataUpdate = { + metadata: data.metadata, + }; + delete data.metadata; + } + + if (data.phases != null) { + data.phaseUpdate = { + phases: data.phases, + }; + delete data.phases; + } + + if (data.events != null) { + data.eventUpdate = { + events: data.events, + }; + delete data.events; + } + + if (data.terms != null) { + data.termUpdate = { + terms: data.terms, + }; + delete data.terms; + } + + if (data.prizeSets != null) { + const type = data.prizeSets[0]?.prizes[0]?.type; + if (type === constants.prizeTypes.USD) { + ChallengeHelper.convertPSValuesToCents(data.prizeSets); + } + + data.prizeSetUpdate = { + prizeSets: [...data.prizeSets], + }; + delete data.prizeSets; + } + + if (data.tags != null) { + data.tagUpdate = { + tags: data.tags, + }; + delete data.tags; + } + + if (data.attachments != null) { + data.attachmentUpdate = { + attachments: data.attachments, + }; + delete data.attachments; + } + + if (data.groups != null) { + data.groupUpdate = { + groups: data.groups, + }; + delete data.groups; + } + + if (data.skills != null) { + data.skillUpdate = { + skills: data.skills, + }; + delete data.skills; + } + + return data; + } + + enrichChallengeForResponse(challenge, track, type) { + if (challenge.phases && challenge.phases.length > 0) { + const registrationPhase = _.find(challenge.phases, (p) => p.name === "Registration"); + const submissionPhase = _.find(challenge.phases, (p) => p.name === "Submission"); + + challenge.currentPhase = challenge.phases + .slice() + .reverse() + .find((phase) => phase.isOpen); + + challenge.currentPhaseNames = _.map( + _.filter(challenge.phases, (p) => p.isOpen === true), + "name" + ); + + if (registrationPhase) { + challenge.registrationStartDate = + registrationPhase.actualStartDate || registrationPhase.scheduledStartDate; + challenge.registrationEndDate = + registrationPhase.actualEndDate || registrationPhase.scheduledEndDate; + } + if (submissionPhase) { + challenge.submissionStartDate = + submissionPhase.actualStartDate || submissionPhase.scheduledStartDate; + + challenge.submissionEndDate = + submissionPhase.actualEndDate || submissionPhase.scheduledEndDate; + } + } + + if (challenge.created) + challenge.created = ChallengeHelper.convertDateToISOString(challenge.created); + if (challenge.updated) + challenge.updated = ChallengeHelper.convertDateToISOString(challenge.updated); + if (challenge.startDate) + challenge.startDate = ChallengeHelper.convertDateToISOString(challenge.startDate); + if (challenge.endDate) + challenge.endDate = ChallengeHelper.convertDateToISOString(challenge.endDate); + + if (track) { + challenge.track = track.name; + } + + if (type) { + challenge.type = type.name; + } + + challenge.metadata = challenge.metadata.map((m) => { + try { + m.value = JSON.stringify(JSON.parse(m.value)); // when we update how we index data, make this a JSON field + } catch (err) { + // do nothing + } + return m; + }); + } + + static convertDateToISOString(startDate) { + if (startDate instanceof Date) { + return startDate.toISOString(); + } + if (typeof startDate === "string" && !isNaN(startDate)) { + startDate = parseInt(startDate); + } + if (typeof startDate === "number") { + const date = new Date(startDate); + return date.toISOString(); + } else { + return startDate; + } + } + + static convertPSValuesToCents(prizeSets) { + prizeSets.forEach((prizeSet) => { + prizeSet.prizes.forEach((prize) => { + prize.amountInCents = new Decimal(prize.value).mul(100).toNumber(); + delete prize.value; + }); + }); + } + + convertToISOString(startDate) { + return ChallengeHelper.convertDateToISOString(startDate); + } + + convertPrizeSetValuesToCents(prizeSets) { + return ChallengeHelper.convertPSValuesToCents(prizeSets); + } + + convertPrizeSetValuesToDollars(prizeSets, overview) { + prizeSets.forEach((prizeSet) => { + prizeSet.prizes.forEach((prize) => { + if (prize.amountInCents != null) { + prize.value = parseFloat(new Decimal(prize.amountInCents).div(100).toFixed(2)); + + delete prize.amountInCents; + } + }); + }); + if (overview && !_.isUndefined(overview.totalPrizesInCents)) { + overview.totalPrizes = parseFloat( + new Decimal(overview.totalPrizesInCents).div(100).toFixed(2) + ); + + delete overview.totalPrizesInCents; + } + } + + isProjectIdRequired(timelineTemplateId) { + const template = _.get(config, "SKIP_PROJECT_ID_BY_TIMLINE_TEMPLATE_ID", '517e76b0-8824-4e72-9b48-a1ebde1793a8'); + + return template !== timelineTemplateId; + } +} + +module.exports = new ChallengeHelper(); diff --git a/src/common/errors.js b/src/common/errors.js index 3b345883..e3b2040f 100644 --- a/src/common/errors.js +++ b/src/common/errors.js @@ -1,7 +1,7 @@ /** * This file defines application errors */ -const util = require('util') +const util = require("util"); /** * Helper function to create generic error object with http status code @@ -10,31 +10,31 @@ const util = require('util') * @returns {Function} the error constructor * @private */ -function createError (name, statusCode) { +function createError(name, statusCode) { /** * The error constructor * @param {String} message the error message * @param {String} [cause] the error cause * @constructor */ - function ErrorCtor (message, cause) { - Error.call(this) - Error.captureStackTrace(this) - this.message = message || name - this.cause = cause - this.httpStatus = statusCode + function ErrorCtor(message, cause) { + Error.call(this); + Error.captureStackTrace(this); + this.message = message || name; + this.cause = cause; + this.httpStatus = statusCode; } - util.inherits(ErrorCtor, Error) - ErrorCtor.prototype.name = name - return ErrorCtor + util.inherits(ErrorCtor, Error); + ErrorCtor.prototype.name = name; + return ErrorCtor; } module.exports = { - BadRequestError: createError('BadRequestError', 400), - UnauthorizedError: createError('UnauthorizedError', 401), - ForbiddenError: createError('ForbiddenError', 403), - NotFoundError: createError('NotFoundError', 404), - ConflictError: createError('ConflictError', 409), - ServiceUnavailableError: createError('ServiceUnavailableError', 503) -} + BadRequestError: createError("BadRequestError", 400), + UnauthorizedError: createError("UnauthorizedError", 401), + ForbiddenError: createError("ForbiddenError", 403), + NotFoundError: createError("NotFoundError", 404), + ConflictError: createError("ConflictError", 409), + ServiceUnavailableError: createError("ServiceUnavailableError", 503), +}; diff --git a/src/common/group-helper.js b/src/common/group-helper.js new file mode 100644 index 00000000..7ef2911d --- /dev/null +++ b/src/common/group-helper.js @@ -0,0 +1,36 @@ +const _ = require("lodash"); +const errors = require("./errors"); +const helper = require("./helper"); + +const { hasAdminRole } = require("./role-helper"); + +class GroupHelper { + /** + * Ensure the user can access the groups being updated to + * @param {Object} currentUser the user who perform operation + * @param {Object} data the challenge data to be updated + * @param {String} challenge the original challenge data + */ + async ensureAcessibilityToModifiedGroups(currentUser, data, challenge) { + const needToCheckForGroupAccess = !currentUser + ? true + : !currentUser.isMachine && !hasAdminRole(currentUser); + if (!needToCheckForGroupAccess) { + return; + } + const userGroups = await helper.getUserGroups(currentUser.userId); + const userGroupsIds = _.map(userGroups, (group) => group.id); + const updatedGroups = _.difference( + _.union(challenge.groups, data.groups), + _.intersection(challenge.groups, data.groups) + ); + const filtered = updatedGroups.filter((g) => !userGroupsIds.includes(g)); + if (filtered.length > 0) { + throw new errors.ForbiddenError( + "ensureAcessibilityToModifiedGroups :: You don't have access to this group!" + ); + } + } +} + +module.exports = new GroupHelper(); diff --git a/src/common/helper.js b/src/common/helper.js index cc8cdddd..eaaa0579 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -1,76 +1,79 @@ /** * This file defines helper methods */ -const Joi = require('joi') -const _ = require('lodash') -const querystring = require('querystring') -const constants = require('../../app-constants') -const models = require('../models') -const errors = require('./errors') -const util = require('util') -const AWS = require('aws-sdk') -const config = require('config') -const m2mAuth = require('tc-core-library-js').auth.m2m -const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME'])) -const axios = require('axios') -const axiosRetry = require('axios-retry') -const busApi = require('topcoder-bus-api-wrapper') -const elasticsearch = require('elasticsearch') -const NodeCache = require('node-cache') -const HttpStatus = require('http-status-codes') -const xss = require('xss') -const logger = require('./logger') +const Joi = require("joi"); +const _ = require("lodash"); +const querystring = require("querystring"); +const constants = require("../../app-constants"); +const errors = require("./errors"); +const util = require("util"); +const AWS = require("aws-sdk"); +const config = require("config"); +const axios = require("axios"); +const axiosRetry = require("axios-retry"); +const busApi = require("topcoder-bus-api-wrapper"); +const NodeCache = require("node-cache"); +const HttpStatus = require("http-status-codes"); +const xss = require("xss"); +const logger = require("./logger"); + +const { Client: OSClient } = require("@opensearch-project/opensearch"); +const elasticsearch = require("elasticsearch"); + +const projectHelper = require("./project-helper"); +const m2mHelper = require("./m2m-helper"); +const { hasAdminRole } = require("./role-helper"); // Bus API Client -let busApiClient +let busApiClient; // Elasticsearch client -let esClient +let esClient; // validate ES refresh method -validateESRefreshMethod(config.ES.ES_REFRESH) +validateESRefreshMethod(config.ES.ES_REFRESH); AWS.config.update({ s3: config.AMAZON.S3_API_VERSION, // accessKeyId: config.AMAZON.AWS_ACCESS_KEY_ID, // secretAccessKey: config.AMAZON.AWS_SECRET_ACCESS_KEY, - region: config.AMAZON.AWS_REGION -}) -const s3 = new AWS.S3() + region: config.AMAZON.AWS_REGION, +}); +const s3 = new AWS.S3(); /** * Wrap async function to standard express function * @param {Function} fn the async function * @returns {Function} the wrapped function */ -function wrapExpress (fn) { +function wrapExpress(fn) { return function (req, res, next) { - fn(req, res, next).catch(next) - } + fn(req, res, next).catch(next); + }; } // Internal cache -const internalCache = new NodeCache({ stdTTL: config.INTERNAL_CACHE_TTL }) +const internalCache = new NodeCache({ stdTTL: config.INTERNAL_CACHE_TTL }); /** * Wrap all functions from object * @param obj the object (controller exports) * @returns {Object|Array} the wrapped object */ -function autoWrapExpress (obj) { +function autoWrapExpress(obj) { if (_.isArray(obj)) { - return obj.map(autoWrapExpress) + return obj.map(autoWrapExpress); } if (_.isFunction(obj)) { - if (obj.constructor.name === 'AsyncFunction') { - return wrapExpress(obj) + if (obj.constructor.name === "AsyncFunction") { + return wrapExpress(obj); } - return obj + return obj; } _.each(obj, (value, key) => { - obj[key] = autoWrapExpress(value) - }) - return obj + obj[key] = autoWrapExpress(value); + }); + return obj; } /** @@ -79,9 +82,9 @@ function autoWrapExpress (obj) { * @param {Number} page the page number * @returns {String} link for the page */ -function getPageLink (req, page) { - const q = _.assignIn({}, req.query, { page }) - return `${config.API_BASE_URL}${req.path}?${querystring.stringify(q)}` +function getPageLink(req, page) { + const q = _.assignIn({}, req.query, { page }); + return `${config.API_BASE_URL}${req.path}?${querystring.stringify(q)}`; } /** @@ -90,44 +93,32 @@ function getPageLink (req, page) { * @param {Object} res the HTTP response * @param {Object} result the operation result */ -function setResHeaders (req, res, result) { - const totalPages = Math.ceil(result.total / result.perPage) +function setResHeaders(req, res, result) { + const totalPages = Math.ceil(result.total / result.perPage); if (parseInt(result.page, 10) > 1) { - res.set('X-Prev-Page', parseInt(result.page, 10) - 1) + res.set("X-Prev-Page", parseInt(result.page, 10) - 1); } if (parseInt(result.page, 10) < totalPages) { - res.set('X-Next-Page', parseInt(result.page, 10) + 1) + res.set("X-Next-Page", parseInt(result.page, 10) + 1); } - res.set('X-Page', parseInt(result.page, 10)) - res.set('X-Per-Page', result.perPage) - res.set('X-Total', result.total) - res.set('X-Total-Pages', totalPages) + res.set("X-Page", parseInt(result.page, 10)); + res.set("X-Per-Page", result.perPage); + res.set("X-Total", result.total); + res.set("X-Total-Pages", totalPages); // set Link header if (totalPages > 0) { - let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink(req, totalPages)}>; rel="last"` + let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink( + req, + totalPages + )}>; rel="last"`; if (parseInt(result.page, 10) > 1) { - link += `, <${getPageLink(req, parseInt(result.page, 10) - 1)}>; rel="prev"` + link += `, <${getPageLink(req, parseInt(result.page, 10) - 1)}>; rel="prev"`; } if (parseInt(result.page, 10) < totalPages) { - link += `, <${getPageLink(req, parseInt(result.page, 10) + 1)}>; rel="next"` - } - res.set('Link', link) - } -} - -/** - * Check if the user has admin role - * @param {Object} authUser the user - */ -function hasAdminRole (authUser) { - if (authUser && authUser.roles) { - for (let i = 0; i < authUser.roles.length; i++) { - if (authUser.roles[i].toLowerCase() === constants.UserRoles.Admin.toLowerCase()) { - return true - } + link += `, <${getPageLink(req, parseInt(result.page, 10) + 1)}>; rel="next"`; } + res.set("Link", link); } - return false } /** @@ -136,16 +127,18 @@ function hasAdminRole (authUser) { * @returns {Object} the new object with removed properties * @private */ -function _sanitizeObject (obj) { +function _sanitizeObject(obj) { try { - return JSON.parse(JSON.stringify(obj, (name, value) => { - if (_.isArray(value) && value.length > 30) { - return `Array(${value.length})` - } - return value - })) + return JSON.parse( + JSON.stringify(obj, (name, value) => { + if (_.isArray(value) && value.length > 30) { + return `Array(${value.length})`; + } + return value; + }) + ); } catch (e) { - return obj + return obj; } } @@ -154,8 +147,8 @@ function _sanitizeObject (obj) { * @param {Object} obj the object * @returns {String} the string value */ -function toString (obj) { - return util.inspect(_sanitizeObject(obj), { breakLength: Infinity }) +function toString(obj) { + return util.inspect(_sanitizeObject(obj), { breakLength: Infinity }); } /** @@ -164,176 +157,30 @@ function toString (obj) { * @param {Array} source the array in which to search for the term * @param {Array | String} term the term to search */ -function checkIfExists (source, term) { - let terms +function checkIfExists(source, term) { + let terms; if (!_.isArray(source)) { - throw new Error('Source argument should be an array') + throw new Error("Source argument should be an array"); } - source = source.map(s => s.toLowerCase()) + source = source.map((s) => s.toLowerCase()); if (_.isString(term)) { - terms = term.toLowerCase().split(' ') + terms = term.toLowerCase().split(" "); } else if (_.isArray(term)) { - terms = term.map(t => t.toLowerCase()) + terms = term.map((t) => t.toLowerCase()); } else { - throw new Error('Term argument should be either a string or an array') + throw new Error("Term argument should be either a string or an array"); } for (let i = 0; i < terms.length; i++) { if (source.includes(terms[i])) { - return true + return true; } } - return false -} - -/** - * Get Data by model id - * @param {String} modelName The dynamoose model name - * @param {String} id The id value - * @returns {Promise} - */ -async function getById (modelName, id) { - return new Promise((resolve, reject) => { - models[modelName].query('id').eq(id).exec((err, result) => { - if (err) { - return reject(err) - } - if (result.length > 0) { - return resolve(result[0]) - } else { - return reject(new errors.NotFoundError(`${modelName} with id: ${id} doesn't exist`)) - } - }) - }) -} - -/** - * Get Data by model ids - * @param {String} modelName The dynamoose model name - * @param {Array} ids The ids - * @returns {Promise} the found entities - */ -async function getByIds (modelName, ids) { - const entities = [] - const theIds = ids || [] - for (const id of theIds) { - entities.push(await getById(modelName, id)) - } - return entities -} - -/** - * Validate the data to ensure no duplication - * @param {Object} modelName The dynamoose model name - * @param {String} name The attribute name of dynamoose model - * @param {String} value The attribute value to be validated - * @returns {Promise} - */ -async function validateDuplicate (modelName, name, value) { - const list = await scan(modelName) - for (let i = 0; i < list.length; i++) { - if (list[i][name] && String(list[i][name]).toLowerCase() === String(value).toLowerCase()) { - throw new errors.ConflictError(`${modelName} with ${name}: ${value} already exist`) - } - } -} - -/** - * Create item in database - * @param {Object} modelName The dynamoose model name - * @param {Object} data The create data object - * @returns {Promise} - */ -async function create (modelName, data) { - return new Promise((resolve, reject) => { - const dbItem = new models[modelName](data) - dbItem.save((err) => { - if (err) { - return reject(err) - } else { - return resolve(dbItem) - } - }) - }) -} - -/** - * Update item in database - * @param {Object} dbItem The Dynamo database item - * @param {Object} data The updated data object - * @returns {Promise} - */ -async function update (dbItem, data) { - Object.keys(data).forEach((key) => { - dbItem[key] = data[key] - }) - return new Promise((resolve, reject) => { - dbItem.save((err) => { - if (err) { - return reject(err) - } else { - return resolve(dbItem) - } - }) - }) -} - -/** - * Get data collection by scan parameters - * @param {Object} modelName The dynamoose model name - * @param {Object} scanParams The scan parameters object - * @returns {Promise} - */ -async function scan (modelName, scanParams) { - return new Promise((resolve, reject) => { - models[modelName].scan(scanParams).exec((err, result) => { - if (err) { - return reject(err) - } else { - return resolve(result.count === 0 ? [] : result) - } - }) - }) -} - -/** - * Get all data collection (avoid default page limit of DynamoDB) by scan parameters - * @param {Object} modelName The dynamoose model name - * @param {Object} scanParams The scan parameters object - * @returns {Array} - */ -async function scanAll (modelName, scanParams) { - let results = await models[modelName].scan(scanParams).exec() - let lastKey = results.lastKey - while (!_.isUndefined(results.lastKey)) { - const newResult = await models[modelName].scan(scanParams).startAt(lastKey).exec() - results = [...results, ...newResult] - lastKey = newResult.lastKey - } - return results -} - -/** - * Test whether the given value is partially match the filter. - * @param {String} filter the filter - * @param {String} value the value to test - * @returns {Boolean} the match result - */ -function partialMatch (filter, value) { - if (filter) { - if (value) { - const filtered = xss(filter) - return _.toLower(value).includes(_.toLower(filtered)) - } else { - return false - } - } else { - return true - } + return false; } /** @@ -342,12 +189,12 @@ function partialMatch (filter, value) { * @param {String} key the key name * @return {Promise} promise resolved to downloaded data */ -async function downloadFromFileStack (url) { - const res = await axios.get(url) +async function downloadFromFileStack(url) { + const res = await axios.get(url); return { data: res.data, - mimetype: _.get(res, `headers['content-type']`, 'application/json') - } + mimetype: _.get(res, `headers['content-type']`, "application/json"), + }; } /** @@ -356,12 +203,12 @@ async function downloadFromFileStack (url) { * @param {String} key the key name * @return {Promise} promise resolved to downloaded data */ -async function downloadFromS3 (bucket, key) { - const file = await s3.getObject({ Bucket: bucket, Key: key }).promise() +async function downloadFromS3(bucket, key) { + const file = await s3.getObject({ Bucket: bucket, Key: key }).promise(); return { data: file.Body, - mimetype: file.ContentType - } + mimetype: file.ContentType, + }; } /** @@ -370,16 +217,8 @@ async function downloadFromS3 (bucket, key) { * @param {String} key the key name * @return {Promise} promise resolved to deleted data */ -async function deleteFromS3 (bucket, key) { - return s3.deleteObject({ Bucket: bucket, Key: key }).promise() -} - -/** - * Get M2M token. - * @returns {Promise} the M2M token - */ -async function getM2MToken () { - return m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) +async function deleteFromS3(bucket, key) { + return s3.deleteObject({ Bucket: bucket, Key: key }).promise(); } /** @@ -387,24 +226,47 @@ async function getM2MToken () { * @param {String} challengeId the challenge id * @returns {Promise} the challenge resources */ -async function getChallengeResources (challengeId) { - const token = await getM2MToken() - const perPage = 100 - let page = 1 - let result = [] +async function getChallengeResources(challengeId, roleId = null) { + const token = await m2mHelper.getM2MToken(); + const perPage = 100; + let page = 1; + let result = []; while (true) { - const url = `${config.RESOURCES_API_URL}?challengeId=${challengeId}&perPage=${perPage}&page=${page}` - const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }) + const url = `${ + config.RESOURCES_API_URL + }?challengeId=${challengeId}&perPage=${perPage}&page=${page}${ + roleId ? `&roleId=${roleId}` : "" + }`; + + const res = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + }); if (!res.data || res.data.length === 0) { - break + break; } - result = result.concat(res.data) - page += 1 - if (res.headers['x-total-pages'] && page > Number(res.headers['x-total-pages'])) { - break + result = result.concat(res.data); + page += 1; + if (res.headers["x-total-pages"] && page > Number(res.headers["x-total-pages"])) { + break; } } - return result + return result; +} + +/** + * Get challenge resources count + * @param {String} challengeId the challenge id + * @returns {Promise} the challenge resources count + */ +async function getChallengeResourcesCount(challengeId, roleId = null) { + const token = await m2mHelper.getM2MToken(); + const url = `${config.RESOURCES_API_URL}/count?challengeId=${challengeId}${ + roleId ? `&roleId=${roleId}` : "" + }`; + const res = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data; } /** @@ -413,35 +275,42 @@ async function getChallengeResources (challengeId) { * @param {String} memberHandle the user's member handle * @param {String} roleId the resource role ID to assign */ -async function createResource (challengeId, memberHandle, roleId) { - const token = await getM2MToken() +async function createResource(challengeId, memberHandle, roleId) { + const token = await m2mHelper.getM2MToken(); const userObj = { challengeId, memberHandle, - roleId - } - const url = `${config.RESOURCES_API_URL}` - const res = await axios.post(url, userObj, { headers: { Authorization: `Bearer ${token}` } }) - return res || false + roleId, + }; + const url = `${config.RESOURCES_API_URL}`; + const res = await axios.post(url, userObj, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res || false; } -function exponentialDelay (retryNumber = 0) { - const delay = Math.pow(2, retryNumber) * 200 - const randomSum = delay * 0.2 * Math.random() // 0-20% of the delay - return delay + randomSum +function exponentialDelay(retryNumber = 0) { + const delay = Math.pow(2, retryNumber) * 200; + const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay + return delay + randomSum; } axiosRetry(axios, { retries: `${config.AXIOS_RETRIES}`, // number of retries retryCondition: (e) => { - return e.config.url.indexOf('v5/projects') > 0 && - (axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === HttpStatus.TOO_MANY_REQUESTS) + return ( + e.config.url.indexOf("v5/projects") > 0 && + (axiosRetry.isNetworkOrIdempotentRequestError(e) || + e.response.status === HttpStatus.TOO_MANY_REQUESTS) + ); }, onRetry: (retryCount, error, requestConfig) => - logger.info(`${error.message} while calling: ${requestConfig.url} - retry count: ${retryCount}`), - retryDelay: exponentialDelay -}) + logger.info( + `${error.message} while calling: ${requestConfig.url} - retry count: ${retryCount}` + ), + retryDelay: exponentialDelay, +}); /** * Create Project @@ -451,73 +320,79 @@ axiosRetry(axios, { * @param {String} token The token * @returns */ -async function createSelfServiceProject (name, description, type, token) { +async function createSelfServiceProject(name, description, type, token) { const projectObj = { name, description, - type + type, + }; + if (!token) { + token = await m2mHelper.getM2MToken(); } - const url = `${config.PROJECTS_API_URL}` - const res = await axios.post(url, projectObj, { headers: { Authorization: `Bearer ${token}` } }) - return _.get(res, 'data.id') + const url = `${config.PROJECTS_API_URL}`; + const res = await axios.post(url, projectObj, { + headers: { Authorization: `Bearer ${token}` }, + }); + + return _.get(res, "data.id"); } /** * Get project id by roundId * @param {String} roundId the round id */ -async function getProjectIdByRoundId (roundId) { - const url = `${config.CHALLENGE_MIGRATION_APP_URL}/getChallengeProjectId/${roundId}` - const res = await axios.get(url) - return _.get(res, 'data.projectId') +async function getProjectIdByRoundId(roundId) { + const url = `${config.CHALLENGE_MIGRATION_APP_URL}/getChallengeProjectId/${roundId}`; + const res = await axios.get(url); + return _.get(res, "data.projectId"); } /** * Get project payment * @param {String} projectId the project id */ -async function getProjectPayment (projectId) { - const token = await getM2MToken() - const url = `${config.CUSTOMER_PAYMENTS_URL}` +async function getProjectPayment(projectId) { + const token = await m2mHelper.getM2MToken(); + const url = `${config.CUSTOMER_PAYMENTS_URL}`; const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` }, params: { referenceId: projectId, - reference: 'project' - } - }) - const [payment] = res.data - return payment + reference: "project", + }, + }); + const [payment] = res.data; + return payment; } /** * Charge payment * @param {String} paymentId the payment ID */ -async function capturePayment (paymentId) { - const token = await getM2MToken() - const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/charge` - logger.info(`Calling: ${url} to capture payment`) - const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }) - logger.debug(`Payment API Response: ${JSON.stringify(res.data, null, 2)}`) - if (res.data.status !== 'succeeded') { - throw new Error(`Failed to charge payment. Current status: ${res.data.status}`) +async function capturePayment(paymentId) { + const token = await m2mHelper.getM2MToken(); + const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/charge`; + logger.info(`Calling: ${url} to capture payment`); + const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }); + logger.debug(`Payment API Response: ${JSON.stringify(res.data)}`); + if (res.data.status !== "succeeded") { + throw new Error(`Failed to charge payment. Current status: ${res.data.status}`); } - return res.data + return res.data; } /** * Cancel payment * @param {String} paymentId the payment ID */ -async function cancelPayment (paymentId) { - const token = await getM2MToken() - const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/cancel` - const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }) - if (res.data.status !== 'canceled') { - throw new Error(`Failed to cancel payment. Current status: ${res.data.status}`) +async function cancelPayment(paymentId) { + const token = await m2mHelper.getM2MToken(); + const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/cancel`; + const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }); + if (res.data.status !== "canceled") { + throw new Error(`Failed to cancel payment. Current status: ${res.data.status}`); } - return res.data + return res.data; } /** @@ -526,30 +401,34 @@ async function cancelPayment (paymentId) { * @param {String} cancelReason the cancel reasonn * @param {Object} currentUser the current user */ -async function cancelProject (projectId, cancelReason, currentUser) { - let payment = await getProjectPayment(projectId) - const project = await ensureProjectExist(projectId, currentUser) - if (project.status === 'cancelled') return // already canceled +async function cancelProject(projectId, cancelReason, currentUser) { + let payment = await getProjectPayment(projectId); + const project = await projectHelper.getProject(projectId, currentUser); + if (project.status === "cancelled") return; // already canceled try { - payment = await cancelPayment(payment.id) + payment = await cancelPayment(payment.id); } catch (e) { - logger.debug(`Failed to cancel payment with error: ${e.message}`) + logger.debug(`Failed to cancel payment with error: ${e.message}`); } - const token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - await axios.patch(url, { - cancelReason, - status: 'cancelled', - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status - } - }, { headers: { Authorization: `Bearer ${token}` } }) + const token = await m2mHelper.getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + await axios.patch( + url, + { + cancelReason, + status: "cancelled", + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + }, + }, + { headers: { Authorization: `Bearer ${token}` } } + ); } /** @@ -557,41 +436,49 @@ async function cancelProject (projectId, cancelReason, currentUser) { * @param {String} projectId the project id * @param {Object} currentUser the current user */ -async function activateProject (projectId, currentUser, name, description) { - let payment - let project +async function activateProject(projectId, currentUser, name, description) { + let payment; + let project; try { - payment = await getProjectPayment(projectId) - project = await ensureProjectExist(projectId, currentUser) - if (payment.status !== 'succeeded') { - payment = await capturePayment(payment.id) + payment = await getProjectPayment(projectId); + project = await projectHelper.getProject(projectId, currentUser); + if (payment.status !== "succeeded") { + payment = await capturePayment(payment.id); } } catch (e) { - logger.debug(e) - logger.debug(`Failed to charge payment ${payment.id} with error: ${e.message}`) - await cancelProject(projectId, `Failed to charge payment ${payment.id} with error: ${e.message}`, currentUser) - throw new Error(`Failed to charge payment ${payment.id} with error: ${e.message}`) + logger.debug(e); + logger.debug(`Failed to charge payment ${payment.id} with error: ${e.message}`); + await cancelProject( + projectId, + `Failed to charge payment ${payment.id} with error: ${e.message}`, + currentUser + ); + throw new Error(`Failed to charge payment ${payment.id} with error: ${e.message}`); } - const token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - const res = await axios.patch(url, { - name, - description, - status: 'active', - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status - } - }, { headers: { Authorization: `Bearer ${token}` } }) - - if (res.data && res.data.status === 'reviewed') { + const token = await m2mHelper.getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + const res = await axios.patch( + url, + { + name, + description, + status: "active", + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + }, + }, + { headers: { Authorization: `Bearer ${token}` } } + ); + + if (res.data && res.data.status === "reviewed") { // auto activate if the project goes in reviewed state - await activateProject(projectId, currentUser, name, description) + await activateProject(projectId, currentUser, name, description); } } @@ -601,45 +488,64 @@ async function activateProject (projectId, currentUser, name, description) { * @param {*} workItemPlannedEndDate the planned end date of the work item * @param {*} currentUser the current user */ -async function updateSelfServiceProjectInfo (projectId, workItemPlannedEndDate, currentUser) { - const project = await ensureProjectExist(projectId, currentUser) - const payment = await getProjectPayment(projectId) - const token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - await axios.patch(url, { - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status, - workItemPlannedEndDate - } - }, { headers: { Authorization: `Bearer ${token}` } }) +async function updateSelfServiceProjectInfo(projectId, workItemPlannedEndDate, currentUser) { + const project = await projectHelper.getProject(projectId, currentUser); + const payment = await getProjectPayment(projectId); + const token = await m2mHelper.getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + + await axios.patch( + url, + { + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + workItemPlannedEndDate, + }, + }, + { headers: { Authorization: `Bearer ${token}` } } + ); } /** * Get resource roles * @returns {Promise} the challenge resources */ -async function getResourceRoles () { - const token = await getM2MToken() - const res = await axios.get(config.RESOURCE_ROLES_API_URL, { headers: { Authorization: `Bearer ${token}` } }) - return res.data || [] +async function getResourceRoles() { + const token = await m2mHelper.getM2MToken(); + const res = await axios.get(config.RESOURCE_ROLES_API_URL, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data || []; } /** * Check if a user has full access on a challenge * @param {String} challengeId the challenge UUID * @param {String} userId the user ID + * @param {Array} challengeResources the challenge resources */ -async function userHasFullAccess (challengeId, userId) { - const resourceRoles = await getResourceRoles() - const rolesWithFullAccess = _.map(_.filter(resourceRoles, r => r.fullWriteAccess), 'id') - const challengeResources = await getChallengeResources(challengeId) - return _.filter(challengeResources, r => _.toString(r.memberId) === _.toString(userId) && _.includes(rolesWithFullAccess, r.roleId)).length > 0 +async function userHasFullAccess(challengeId, userId, challengeResources) { + const resourceRoles = await getResourceRoles(); + const rolesWithFullAccess = _.map( + _.filter(resourceRoles, (r) => r.fullWriteAccess), + "id" + ); + if (!challengeResources) { + challengeResources = await getChallengeResources(challengeId); + } + return ( + _.filter( + challengeResources, + (r) => + _.toString(r.memberId) === _.toString(userId) && _.includes(rolesWithFullAccess, r.roleId) + ).length > 0 + ); } /** @@ -647,11 +553,11 @@ async function userHasFullAccess (challengeId, userId) { * @param {String} userId the user id * @returns {Promise} the user groups */ -async function getUserGroups (userId) { - const token = await getM2MToken() - let allGroups = [] +async function getUserGroups(userId) { + const token = await m2mHelper.getM2MToken(); + let allGroups = []; // get search is paginated, we need to get all pages' data - let page = 1 + let page = 1; while (true) { const result = await axios.get(config.GROUPS_API_URL, { headers: { Authorization: `Bearer ${token}` }, @@ -659,20 +565,20 @@ async function getUserGroups (userId) { page, perPage: 5000, memberId: userId, - membershipType: 'user' - } - }) - const groups = result.data || [] + membershipType: "user", + }, + }); + const groups = result.data || []; if (groups.length === 0) { - break + break; } - allGroups = allGroups.concat(groups) - page += 1 - if (result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { - break + allGroups = allGroups.concat(groups); + page += 1; + if (result.headers["x-total-pages"] && page > Number(result.headers["x-total-pages"])) { + break; } } - return allGroups + return allGroups; } /** @@ -680,16 +586,16 @@ async function getUserGroups (userId) { * @param {String} userId the user id * @returns {Promise} the user groups */ -async function getCompleteUserGroupTreeIds (userId) { - const token = await getM2MToken() +async function getCompleteUserGroupTreeIds(userId) { + const token = await m2mHelper.getM2MToken(); const result = await axios.get(`${config.GROUPS_API_URL}/memberGroups/${userId}`, { headers: { Authorization: `Bearer ${token}` }, params: { - uuid: true - } - }) + uuid: true, + }, + }); - return result.data || [] + return result.data || []; } /** @@ -697,16 +603,16 @@ async function getCompleteUserGroupTreeIds (userId) { * @param {String} groupId the group ID * @returns {Array} an array with the groups ID and the IDs of all subGroups */ -async function expandWithSubGroups (groupId) { - const token = await getM2MToken() +async function expandWithSubGroups(groupId) { + const token = await m2mHelper.getM2MToken(); const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { headers: { Authorization: `Bearer ${token}` }, params: { - includeSubGroups: true - } - }) - const groups = result.data || {} - return [groupId, ..._.map(_.get(groups, 'subGroups', []), 'id')] + includeSubGroups: true, + }, + }); + const groups = result.data || {}; + return [groupId, ..._.map(_.get(groups, "subGroups", []), "id")]; } /** @@ -714,26 +620,26 @@ async function expandWithSubGroups (groupId) { * @param {String} groupId the group ID * @returns {Array} an array with the group ID and the IDs of all parent groups up the chain */ -async function expandWithParentGroups (groupId) { - const token = await getM2MToken() +async function expandWithParentGroups(groupId) { + const token = await m2mHelper.getM2MToken(); const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { headers: { Authorization: `Bearer ${token}` }, params: { includeParentGroup: true, - oneLevel: false - } - }) + oneLevel: false, + }, + }); - const ids = [] + const ids = []; const extractIds = (group) => { - ids.push(group.id) - _.each(_.get(group, 'parentGroups', []), (parent) => { - extractIds(parent) - }) - } + ids.push(group.id); + _.each(_.get(group, "parentGroups", []), (parent) => { + extractIds(parent); + }); + }; - extractIds(result.data || {}) - return ids + extractIds(result.data || {}); + return ids; } /** @@ -741,15 +647,15 @@ async function expandWithParentGroups (groupId) { * @param {Array} arr the array to check * @param {String} name the array name */ -function ensureNoDuplicateOrNullElements (arr, name) { - const a = arr || [] +function ensureNoDuplicateOrNullElements(arr, name) { + const a = arr || []; for (let i = 0; i < a.length; i += 1) { if (_.isNil(a[i])) { - throw new errors.BadRequestError(`There is null element for ${name}.`) + throw new errors.BadRequestError(`There is null element for ${name}.`); } for (let j = i + 1; j < a.length; j += 1) { if (a[i] === a[j]) { - throw new errors.BadRequestError(`There are duplicate elements (${a[i]}) for ${name}.`) + throw new errors.BadRequestError(`There are duplicate elements (${a[i]}) for ${name}.`); } } } @@ -759,16 +665,24 @@ function ensureNoDuplicateOrNullElements (arr, name) { * Get Bus API Client * @return {Object} Bus API Client Instance */ -function getBusApiClient () { +function getBusApiClient() { // if there is no bus API client instance, then create a new instance if (!busApiClient) { - busApiClient = busApi(_.pick(config, - ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', - 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET', 'BUSAPI_URL', - 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) + busApiClient = busApi( + _.pick(config, [ + "AUTH0_URL", + "AUTH0_AUDIENCE", + "TOKEN_CACHE_TIME", + "AUTH0_CLIENT_ID", + "AUTH0_CLIENT_SECRET", + "BUSAPI_URL", + "KAFKA_ERROR_TOPIC", + "AUTH0_PROXY_SERVER_URL", + ]) + ); } - return busApiClient + return busApiClient; } /** @@ -777,90 +691,70 @@ function getBusApiClient () { * @param {Object} payload the event payload * @param {Object} options the extra options to the message */ -async function postBusEvent (topic, payload, options = {}) { - const client = getBusApiClient() +async function postBusEvent(topic, payload, options = {}) { + const client = getBusApiClient(); const message = { topic, originator: constants.EVENT_ORIGINATOR, timestamp: new Date().toISOString(), - 'mime-type': constants.EVENT_MIME_TYPE, - payload - } + "mime-type": constants.EVENT_MIME_TYPE, + payload, + }; if (options.key) { - message.key = options.key + message.key = options.key; } - await client.postEvent(message) + await client.postEvent(message); } /** * Get ES Client * @return {Object} Elasticsearch Client Instance */ -function getESClient () { +function getESClient() { if (esClient) { - return esClient - } - const esHost = config.get('ES.HOST') - // AWS ES configuration is different from other providers - if (/.*amazonaws.*/.test(esHost)) { - esClient = elasticsearch.Client({ - apiVersion: config.get('ES.API_VERSION'), - hosts: esHost, - connectionClass: require('http-aws-es'), // eslint-disable-line global-require - amazonES: { - region: config.get('AMAZON.AWS_REGION'), - credentials: new AWS.EnvironmentCredentials('AWS') - } - }) - } else { - esClient = new elasticsearch.Client({ - apiVersion: config.get('ES.API_VERSION'), - hosts: esHost - }) + return esClient; } - return esClient -} - -/** - * Ensure project exist - * @param {String} projectId the project id - * @param {String} currentUser the user - */ -async function ensureProjectExist (projectId, currentUser) { - let token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - try { - const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }) - if (currentUser.isMachine || hasAdminRole(currentUser)) { - return res.data - } - if (_.get(res, 'data.type') === 'self-service' && _.includes(config.SELF_SERVICE_WHITELIST_HANDLES, currentUser.handle.toLowerCase())) { - return res.data - } - if (!_.find(_.get(res, 'data.members', []), m => _.toString(m.userId) === _.toString(currentUser.userId))) { - throw new errors.ForbiddenError(`You don't have access to project with ID: ${projectId}`) - } - return res.data - } catch (err) { - if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`) + const esHost = config.get("ES.HOST"); + + if (config.get("ES.OPENSEARCH") == "false") { + if (/.*amazonaws.*/.test(esHost)) { + esClient = elasticsearch.Client({ + apiVersion: config.get("ES.API_VERSION"), + hosts: esHost, + connectionClass: require("http-aws-es"), // eslint-disable-line global-require + amazonES: { + region: config.get("AMAZON.AWS_REGION"), + credentials: new AWS.EnvironmentCredentials("AWS"), + }, + }); } else { - // re-throw other error - throw err + esClient = new elasticsearch.Client({ + apiVersion: config.get("ES.API_VERSION"), + hosts: esHost, + }); } + } else { + esClient = new OSClient({ + node: esHost, + ssl: { + rejectUnauthorized: false, + }, + }); } + + return esClient; } /** * Calculates challenge end date based on its phases * @param {any} challenge */ -function calculateChallengeEndDate (challenge, data) { +function calculateChallengeEndDate(challenge, data) { if (!data) { - data = challenge + data = challenge; } - let lastPhase = data.phases[data.phases.length - 1] - return lastPhase.actualEndDate || lastPhase.scheduledEndDate + let lastPhase = data.phases[data.phases.length - 1]; + return lastPhase.actualEndDate || lastPhase.scheduledEndDate; // let phase = data.phases[data.phases.length - 1] // if (!phase || (!data.startDate && !challenge.startDate)) { // return data.startDate || challenge.startDate @@ -882,36 +776,25 @@ function calculateChallengeEndDate (challenge, data) { * @param {Number} memberId the member id * @returns {Promise} an array of challenge ids represents challenges that given member has access to. */ -async function listChallengesByMember (memberId) { - const token = await getM2MToken() - let allIds = [] - // get search is paginated, we need to get all pages' data - let page = 1 - while (true) { - let result = {} - try { - result = await axios.get(`${config.RESOURCES_API_URL}/${memberId}/challenges`, { - headers: { Authorization: `Bearer ${token}` }, - params: { - page, - perPage: 10000 - } - }) - } catch (e) { - // only log the error but don't throw it, so the following logic can still be executed. - logger.debug(`Failed to get challenges that accessible to the memberId ${memberId}`, e) - } - const ids = result.data || [] - if (ids.length === 0) { - break - } - allIds = allIds.concat(ids) - page += 1 - if (result.headers && result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { - break - } +async function listChallengesByMember(memberId) { + const token = await m2mHelper.getM2MToken(); + let allIds = []; + + try { + const result = await axios.get(`${config.RESOURCES_API_URL}/${memberId}/challenges`, { + headers: { Authorization: `Bearer ${token}` }, + params: { + useScroll: true, + }, + }); + + allIds = result.data || []; + } catch (e) { + // only log the error but don't throw it, so the following logic can still be executed. + logger.debug(`Failed to get challenges that accessible to the memberId ${memberId}`, e); } - return allIds + + return allIds; } /** @@ -920,22 +803,25 @@ async function listChallengesByMember (memberId) { * @param {String} id the challenge id * @returns {Promise} an array of resources. */ -async function listResourcesByMemberAndChallenge (memberId, challengeId) { - const token = await getM2MToken() - let response = {} +async function listResourcesByMemberAndChallenge(memberId, challengeId) { + const token = await m2mHelper.getM2MToken(); + let response = {}; try { response = await axios.get(config.RESOURCES_API_URL, { headers: { Authorization: `Bearer ${token}` }, params: { memberId, - challengeId - } - }) + challengeId, + }, + }); } catch (e) { - logger.debug(`Failed to get resources on challenge ${challengeId} that memberId ${memberId} has`, e) + logger.debug( + `Failed to get resources on challenge ${challengeId} that memberId ${memberId} has`, + e + ); } - const result = response.data || [] - return result + const result = response.data || []; + return result; } /** @@ -944,9 +830,9 @@ async function listResourcesByMemberAndChallenge (memberId, challengeId) { * @param {String} method method to be tested * @returns {String} method valid method */ -async function validateESRefreshMethod (method) { - Joi.attempt(method, Joi.string().label('ES_REFRESH').valid(['true', 'false', 'wait_for'])) - return method +async function validateESRefreshMethod(method) { + Joi.attempt(method, Joi.string().label("ES_REFRESH").valid(["true", "false", "wait_for"])); + return method; } /** @@ -955,51 +841,20 @@ async function validateESRefreshMethod (method) { * @param {Number} projectId The id of the project for which to get the default terms of use * @returns {Promise>} An array containing the ids of the default project terms of use */ -async function getProjectDefaultTerms (projectId) { - const token = await getM2MToken() - const projectUrl = `${config.PROJECTS_API_URL}/${projectId}` - try { - const res = await axios.get(projectUrl, { headers: { Authorization: `Bearer ${token}` } }) - return res.data.terms || [] - } catch (err) { - if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`) - } else { - // re-throw other error - throw err - } - } -} - -/** - * This functions gets the default billing account for a given project id - * - * @param {Number} projectId The id of the project for which to get the default terms of use - * @returns {Promise} The billing account ID - */ -async function getProjectBillingInformation (projectId) { - const token = await getM2MToken() - const projectUrl = `${config.PROJECTS_API_URL}/${projectId}/billingAccount` +async function getProjectDefaultTerms(projectId) { + const token = await m2mHelper.getM2MToken(); + const projectUrl = `${config.PROJECTS_API_URL}/${projectId}`; try { - const res = await axios.get(projectUrl, { headers: { Authorization: `Bearer ${token}` } }) - let markup = _.get(res, 'data.markup', null) ? _.toNumber(_.get(res, 'data.markup', null)) : null - if (markup && markup > 0) { - // TODO - Hack to change int returned from api to decimal - markup = markup / 100 - } - return { - billingAccountId: _.get(res, 'data.tcBillingAccountId', null), - markup - } + const res = await axios.get(projectUrl, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data.terms || []; } catch (err) { - if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { - return { - billingAccountId: null, - markup: null - } + if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`); } else { // re-throw other error - throw err + throw err; } } } @@ -1010,25 +865,29 @@ async function getProjectBillingInformation (projectId) { * * @param {Array} terms The array of terms {id, roleId} to retrieve from terms API */ -async function validateChallengeTerms (terms = []) { - const listOfTerms = [] - const token = await getM2MToken() +async function validateChallengeTerms(terms = []) { + const listOfTerms = []; + const token = await m2mHelper.getM2MToken(); for (let term of terms) { // Get the terms details from the API try { - await axios.get(`${config.TERMS_API_URL}/${term.id}`, { headers: { Authorization: `Bearer ${token}` } }) - listOfTerms.push(term) + await axios.get(`${config.TERMS_API_URL}/${term.id}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + listOfTerms.push(term); } catch (e) { - if (_.get(e, 'response.status') === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError(`Terms of use identified by the id ${term.id} does not exist`) + if (_.get(e, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError( + `Terms of use identified by the id ${term.id} does not exist` + ); } else { // re-throw other error - throw e + throw e; } } } - return listOfTerms + return listOfTerms; } /** @@ -1037,28 +896,37 @@ async function validateChallengeTerms (terms = []) { * @param {Array} challenges the challenges to filter * @returns {Array} the challenges that can be accessed by current user */ -async function _filterChallengesByGroupsAccess (currentUser, challenges) { - const res = [] - const needToCheckForGroupAccess = !currentUser ? true : !currentUser.isMachine && !hasAdminRole(currentUser) - if (!needToCheckForGroupAccess) return challenges +async function _filterChallengesByGroupsAccess(currentUser, challenges) { + const res = []; + const needToCheckForGroupAccess = !currentUser + ? true + : !currentUser.isMachine && !hasAdminRole(currentUser); + if (!needToCheckForGroupAccess) return challenges; - let userGroups + let userGroups; for (const challenge of challenges) { - challenge.groups = _.filter(challenge.groups, g => !_.includes(['null', 'undefined'], _.toString(g).toLowerCase())) - if (!challenge.groups || _.get(challenge, 'groups.length', 0) === 0 || !needToCheckForGroupAccess) { - res.push(challenge) + challenge.groups = _.filter( + challenge.groups, + (g) => !_.includes(["null", "undefined"], _.toString(g).toLowerCase()) + ); + if ( + !challenge.groups || + _.get(challenge, "groups.length", 0) === 0 || + !needToCheckForGroupAccess + ) { + res.push(challenge); } else if (currentUser) { if (_.isNil(userGroups)) { - userGroups = await getCompleteUserGroupTreeIds(currentUser.userId) + userGroups = await getCompleteUserGroupTreeIds(currentUser.userId); } // get user groups if not yet if (_.find(challenge.groups, (group) => !!_.find(userGroups, (ug) => ug === group))) { - res.push(challenge) + res.push(challenge); } } } - return res + return res; } /** @@ -1066,10 +934,12 @@ async function _filterChallengesByGroupsAccess (currentUser, challenges) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function ensureAccessibleByGroupsAccess (currentUser, challenge) { - const filtered = await _filterChallengesByGroupsAccess(currentUser, [challenge]) +async function ensureAccessibleByGroupsAccess(currentUser, challenge) { + const filtered = await _filterChallengesByGroupsAccess(currentUser, [challenge]); if (filtered.length === 0) { - throw new errors.ForbiddenError("helper ensureAcessibilityToModifiedGroups :: You don't have access to this group!") + throw new errors.ForbiddenError( + "helper ensureAcessibilityToModifiedGroups :: You don't have access to this group!" + ); } } @@ -1079,18 +949,20 @@ async function ensureAccessibleByGroupsAccess (currentUser, challenge) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function _ensureAccessibleForTaskChallenge (currentUser, challenge) { - let memberResources +async function _ensureAccessibleForTaskChallenge(currentUser, challenge) { + let memberResources; // Check if challenge is task and apply security rules - if (_.get(challenge, 'task.isTask', false) && _.get(challenge, 'task.isAssigned', false)) { + if (_.get(challenge, "task.isTask", false) && _.get(challenge, "task.isAssigned", false)) { if (currentUser) { if (!currentUser.isMachine) { - memberResources = await listResourcesByMemberAndChallenge(currentUser.userId, challenge.id) + memberResources = await listResourcesByMemberAndChallenge(currentUser.userId, challenge.id); } } - const canAccesChallenge = _.isUndefined(currentUser) ? false : currentUser.isMachine || hasAdminRole(currentUser) || !_.isEmpty(memberResources) + const canAccesChallenge = _.isUndefined(currentUser) + ? false + : currentUser.isMachine || hasAdminRole(currentUser) || !_.isEmpty(memberResources); if (!canAccesChallenge) { - throw new errors.ForbiddenError(`You don't have access to view this challenge`) + throw new errors.ForbiddenError(`You don't have access to view this challenge`); } } } @@ -1101,11 +973,11 @@ async function _ensureAccessibleForTaskChallenge (currentUser, challenge) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function ensureUserCanViewChallenge (currentUser, challenge) { +async function ensureUserCanViewChallenge(currentUser, challenge) { // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge) + await ensureAccessibleByGroupsAccess(currentUser, challenge); // check if user can access a challenge that is a task - await _ensureAccessibleForTaskChallenge(currentUser, challenge) + await _ensureAccessibleForTaskChallenge(currentUser, challenge); } /** @@ -1113,84 +985,109 @@ async function ensureUserCanViewChallenge (currentUser, challenge) { * * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check - * @returns {undefined} + * @param {Array} challengeResources the challenge resources + * @returns {Promise} */ -async function ensureUserCanModifyChallenge (currentUser, challenge) { +async function ensureUserCanModifyChallenge(currentUser, challenge, challengeResources) { // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge) + await ensureAccessibleByGroupsAccess(currentUser, challenge); // check full access - const isUserHasFullAccess = await userHasFullAccess(challenge.id, currentUser.userId) - if (!currentUser.isMachine && !hasAdminRole(currentUser) && challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && !isUserHasFullAccess) { - throw new errors.ForbiddenError(`Only M2M, admin, challenge's copilot or users with full access can perform modification.`) + const isUserHasFullAccess = await userHasFullAccess( + challenge.id, + currentUser.userId, + challengeResources + ); + if ( + !currentUser.isMachine && + !hasAdminRole(currentUser) && + challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && + !isUserHasFullAccess + ) { + throw new errors.ForbiddenError( + `Only M2M, admin, challenge's copilot or users with full access can perform modification.` + ); } } /** - * Calculate the sum of prizes. - * - * @param {Array} prizes the list of prize - * @returns {Number} the result prize - */ -function sumOfPrizes (prizes) { - let sum = 0 + * Calculate the sum of prizes. + * + * @param {Array} prizes the list of prize + * @returns {Number} the result prize + */ +function sumOfPrizes(prizes) { + let sum = 0; if (!prizes.length) { - return sum + return sum; } for (const prize of prizes) { - sum += prize.value + sum += prize.value; } - return sum + return sum; } /** - * Get group by id - * @param {String} groupId the group id - * @returns {Promise} the group - */ -async function getGroupById (groupId) { - const token = await getM2MToken() + * Get group by id + * @param {String} groupId the group id + * @returns {Promise} the group + */ +async function getGroupById(groupId) { + const token = await m2mHelper.getM2MToken(); try { const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { - headers: { Authorization: `Bearer ${token}` } - }) - return result.data + headers: { Authorization: `Bearer ${token}` }, + }); + return result.data; } catch (err) { if (err.response.status === HttpStatus.NOT_FOUND) { - return + return; } - throw err + throw err; } } /** * Get challenge submissions * @param {String} challengeId the challenge id - * @returns {Array} the submission + * @returns {Promise} the submission */ -async function getChallengeSubmissions (challengeId) { - const token = await getM2MToken() - let allSubmissions = [] +async function getChallengeSubmissions(challengeId) { + const token = await m2mHelper.getM2MToken(); + let allSubmissions = []; // get search is paginated, we need to get all pages' data - let page = 1 + let page = 1; while (true) { const result = await axios.get(`${config.SUBMISSIONS_API_URL}?challengeId=${challengeId}`, { headers: { Authorization: `Bearer ${token}` }, params: { page, - perPage: 100 - } - }) - const ids = result.data || [] + perPage: 100, + }, + }); + const ids = result.data || []; if (ids.length === 0) { - break + break; } - allSubmissions = allSubmissions.concat(ids) - page += 1 - if (result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { - break + allSubmissions = allSubmissions.concat(ids); + page += 1; + if (result.headers["x-total-pages"] && page > Number(result.headers["x-total-pages"])) { + break; } } - return allSubmissions + return allSubmissions; +} + +/** + * Get challenge submissions count + * @param {String} challengeId the challenge id + * @returns {Promise} the submission counts + */ +async function getChallengeSubmissionsCount(challengeId) { + const token = await m2mHelper.getM2MToken(); + const res = await axios.get(`${config.SUBMISSIONS_API_URL}/${challengeId}/count`, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data; } /** @@ -1198,13 +1095,13 @@ async function getChallengeSubmissions (challengeId) { * @param {String} userId the user ID * @returns {Object} */ -async function getMemberById (userId) { - const token = await getM2MToken() +async function getMemberById(userId) { + const token = await m2mHelper.getM2MToken(); const res = await axios.get(`${config.MEMBERS_API_URL}?userId=${userId}`, { - headers: { Authorization: `Bearer ${token}` } - }) - if (res.data.length > 0) return res.data[0] - return {} + headers: { Authorization: `Bearer ${token}` }, + }); + if (res.data.length > 0) return res.data[0]; + return {}; } /** @@ -1212,12 +1109,66 @@ async function getMemberById (userId) { * @param {String} handle the user handle * @returns {Object} */ -async function getMemberByHandle (handle) { - const token = await getM2MToken() +async function getMemberByHandle(handle) { + const token = await m2mHelper.getM2MToken(); const res = await axios.get(`${config.MEMBERS_API_URL}/${handle}`, { - headers: { Authorization: `Bearer ${token}` } - }) - return res.data || {} + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data || {}; +} + +/** + * Get members by handles + * @param {Array} handles the user handle + * @returns {Object} + */ +async function getMembersByHandles(handles) { + const token = await m2mHelper.getM2MToken(); + const res = await axios.get( + `${config.MEMBERS_API_URL}/?fields=handle&handlesLower=["${_.join(handles, '","')}"]`, + { + headers: { Authorization: `Bearer ${token}` }, + } + ); + return res.data; +} + +/** + * Get standard skills by ids + * @param {Array} ids the skills ids + * @returns {Object} + */ +async function getStandSkills(ids) { + const queryBatches = []; + const skillIdArg = "&skillId="; + let queryString = "disablePagination=true"; + + for (const id of ids) { + const enid = encodeURIComponent(id); + // When many skill ids, the query string will exceed 2048 limit + if (queryString.length + skillIdArg.length + enid.length < 2048) { + queryString += skillIdArg + enid; + } else { + queryBatches.push(queryString); + queryString = "disablePagination=true" + skillIdArg + enid; + } + } + queryBatches.push(queryString); + + const skillDataPromises = []; + for (const batch of queryBatches) { + skillDataPromises.push( + (async () => { + const res = await axios.get( + `${config.API_BASE_URL}/v5/standardized-skills/skills?${batch}` + ); + return res.data; + })() + ); + } + + const data = await Promise.all(skillDataPromises); + return _.concat(...data); } /** @@ -1226,12 +1177,12 @@ async function getMemberByHandle (handle) { * @param {Array} recipients the array of recipients in { userId || email || handle } format * @param {Object} data the data */ -async function sendSelfServiceNotification (type, recipients, data) { +async function sendSelfServiceNotification(type, recipients, data) { try { await postBusEvent(constants.Topics.Notifications, { notifications: [ { - serviceId: 'email', + serviceId: "email", type, details: { from: config.EMAIL_FROM, @@ -1239,16 +1190,16 @@ async function sendSelfServiceNotification (type, recipients, data) { cc: [...constants.SelfServiceNotificationSettings[type].cc], data: { ...data, - supportUrl: `${config.SELF_SERVICE_APP_URL}/support` + supportUrl: `${config.SELF_SERVICE_APP_URL}/support`, }, sendgridTemplateId: constants.SelfServiceNotificationSettings[type].sendgridTemplateId, - version: 'v3' - } - } - ] - }) + version: "v3", + }, + }, + ], + }); } catch (e) { - logger.debug(`Failed to post notification ${type}: ${e.message}`) + logger.debug(`Failed to post notification ${type}: ${e.message}`); } } @@ -1256,31 +1207,45 @@ async function sendSelfServiceNotification (type, recipients, data) { * Submit a request to zendesk * @param {Object} request the request */ -async function submitZendeskRequest (request) { +async function submitZendeskRequest(request) { try { - const res = await axios.post(`${config.ZENDESK_API_URL}/api/v2/requests`, { - request: { - ...request + const res = await axios.post( + `${config.ZENDESK_API_URL}/api/v2/requests`, + { + request: { + ...request, + }, + }, + { + auth: { + username: `${request.requester.email}/token`, + password: config.ZENDESK_API_TOKEN, + }, } - }, { - auth: { - username: `${request.requester.email}/token`, - password: config.ZENDESK_API_TOKEN - } - }) - return res.data || {} + ); + return res.data || {}; } catch (e) { - logger.debug(`Failed to submit request: ${e.message}`) - throw e + logger.debug(`Failed to submit request: ${e.message}`); + throw e; } } -function getFromInternalCache (key) { - return internalCache.get(key) +function getFromInternalCache(key) { + return internalCache.get(key); +} + +function setToInternalCache(key, value) { + internalCache.set(key, value); } -function setToInternalCache (key, value) { - internalCache.set(key, value) +function flushInternalCache() { + internalCache.flushAll(); +} + +function grpcErrorToHTTPCode(grpcErrorCode) { + if (grpcErrorCode == 2) return HttpStatus.NOT_FOUND; + + return HttpStatus.INTERNAL_SERVER_ERROR; } module.exports = { @@ -1288,33 +1253,23 @@ module.exports = { autoWrapExpress, setResHeaders, checkIfExists, - hasAdminRole, toString, - getById, - getByIds, - create, - update, - scan, - scanAll, - validateDuplicate, - partialMatch, downloadFromFileStack, downloadFromS3, deleteFromS3, getChallengeResources, + getChallengeResourcesCount, createResource, getUserGroups, ensureNoDuplicateOrNullElements, postBusEvent, getESClient, - ensureProjectExist, calculateChallengeEndDate, listChallengesByMember, listResourcesByMemberAndChallenge, validateESRefreshMethod, getProjectDefaultTerms, validateChallengeTerms, - getProjectBillingInformation, expandWithSubGroups, getCompleteUserGroupTreeIds, expandWithParentGroups, @@ -1327,6 +1282,7 @@ module.exports = { getProjectIdByRoundId, getGroupById, getChallengeSubmissions, + getChallengeSubmissionsCount, getMemberById, createSelfServiceProject, activateProject, @@ -1336,8 +1292,14 @@ module.exports = { cancelPayment, sendSelfServiceNotification, getMemberByHandle, + getMembersByHandles, + getStandSkills, submitZendeskRequest, updateSelfServiceProjectInfo, getFromInternalCache, - setToInternalCache -} + setToInternalCache, + grpcErrorToHTTPCode, + flushInternalCache, +}; + +logger.buildService(module.exports); diff --git a/src/common/logger.js b/src/common/logger.js index 8217199f..478983a6 100644 --- a/src/common/logger.js +++ b/src/common/logger.js @@ -2,24 +2,21 @@ * This module contains the winston logger configuration. */ -const _ = require('lodash') -const Joi = require('joi') -const util = require('util') -const config = require('config') -const getParams = require('get-parameter-names') -const { createLogger, format, transports } = require('winston') +const _ = require("lodash"); +const Joi = require("joi"); +const util = require("util"); +const config = require("config"); +const getParams = require("get-parameter-names"); +const { createLogger, format, transports } = require("winston"); const logger = createLogger({ level: config.LOG_LEVEL, transports: [ new transports.Console({ - format: format.combine( - format.colorize(), - format.simple() - ) - }) - ] -}) + format: format.combine(format.colorize(), format.simple()), + }), + ], +}); /** * Log error details with signature @@ -28,17 +25,25 @@ const logger = createLogger({ */ logger.logFullError = (err, signature) => { if (!err) { - return + return; } if (signature) { - logger.error(`Error happened in ${signature}`) + logger.error(`Error happened in ${signature}`); } - logger.error(util.inspect(err)) - if (!err.logged) { - logger.error(err.stack) - err.logged = true + if (err.isJoi) { + logger.error( + `${err.name} details: ${JSON.stringify(err.details)} input:${JSON.stringify(err._object)}` + ); + } else if (err.isAxiosError) { + logger.error(`${err.message} - ${JSON.stringify(err.response.data)}`); + } else if (err.httpStatus) { + logger.error(err.message); + } else if (!_.isUndefined(err.code) && err.details && err.metadata) { + logger.error(JSON.stringify(err)); + } else { + logger.error(util.inspect(err)); } -} +}; /** * Remove invalid properties from the object and hide long arrays @@ -48,22 +53,24 @@ logger.logFullError = (err, signature) => { */ const _sanitizeObject = (obj) => { try { - return JSON.parse(JSON.stringify(obj, (name, value) => { - // Array of field names that should not be logged - // add field if necessary (password, tokens etc) - const removeFields = ['userToken'] - if (_.includes(removeFields, name)) { - return '' - } - if (_.isArray(value) && value.length > 30) { - return `Array(${value.length})` - } - return value - })) + return JSON.parse( + JSON.stringify(obj, (name, value) => { + // Array of field names that should not be logged + // add field if necessary (password, tokens etc) + const removeFields = ["userToken"]; + if (_.includes(removeFields, name)) { + return ""; + } + if (_.isArray(value) && value.length > 30) { + return `Array(${value.length})`; + } + return value; + }) + ); } catch (e) { - return obj + return obj; } -} +}; /** * Convert array with arguments to object @@ -72,43 +79,43 @@ const _sanitizeObject = (obj) => { * @private */ const _combineObject = (params, arr) => { - const ret = {} + const ret = {}; _.each(arr, (arg, i) => { - ret[params[i]] = arg - }) - return ret -} + ret[params[i]] = arg; + }); + return ret; +}; /** * Decorate all functions of a service and log debug information if DEBUG is enabled * @param {Object} service the service */ logger.decorateWithLogging = (service) => { - if (config.LOG_LEVEL !== 'debug') { - return + if (config.LOG_LEVEL !== "debug") { + return; } _.each(service, (method, name) => { - const params = method.params || getParams(method) + const params = method.params || getParams(method); service[name] = async function () { // logger.debug(`ENTER ${name}`) // logger.debug('input arguments') - const args = Array.prototype.slice.call(arguments) - logger.debug(util.inspect(_sanitizeObject(_combineObject(params, args)))) + const args = Array.prototype.slice.call(arguments); + logger.debug(util.inspect(_sanitizeObject(_combineObject(params, args)))); try { - const result = await method.apply(this, arguments) + const result = await method.apply(this, arguments); // logger.debug(`EXIT ${name}`) // logger.debug('output arguments') if (result !== null && result !== undefined) { - logger.debug(util.inspect(_sanitizeObject(result))) + logger.debug(util.inspect(_sanitizeObject(result))); } - return result + return result; } catch (e) { - logger.logFullError(e, name) - throw e + logger.logFullError(e, name); + throw e; } - } - }) -} + }; + }); +}; /** * Decorate all functions of a service and validate input values @@ -119,34 +126,34 @@ logger.decorateWithLogging = (service) => { logger.decorateWithValidators = function (service) { _.each(service, (method, name) => { if (!method.schema) { - return + return; } - const params = getParams(method) + const params = getParams(method); service[name] = async function () { - const args = Array.prototype.slice.call(arguments) - const value = _combineObject(params, args) - const normalized = Joi.attempt(value, method.schema) + const args = Array.prototype.slice.call(arguments); + const value = _combineObject(params, args); + const normalized = Joi.attempt(value, method.schema); - const newArgs = [] + const newArgs = []; // Joi will normalize values // for example string number '1' to 1 // if schema type is number _.each(params, (param) => { - newArgs.push(normalized[param]) - }) - return method.apply(this, newArgs) - } - service[name].params = params - }) -} + newArgs.push(normalized[param]); + }); + return method.apply(this, newArgs); + }; + service[name].params = params; + }); +}; /** * Apply logger and validation decorators * @param {Object} service the service to wrap */ logger.buildService = (service) => { - logger.decorateWithValidators(service) + logger.decorateWithValidators(service); // logger.decorateWithLogging(service) -} +}; -module.exports = logger +module.exports = logger; diff --git a/src/common/m2m-helper.js b/src/common/m2m-helper.js new file mode 100644 index 00000000..e967622f --- /dev/null +++ b/src/common/m2m-helper.js @@ -0,0 +1,22 @@ +const _ = require("lodash"); +const config = require("config"); +const m2mAuth = require("tc-core-library-js").auth.m2m; + +class M2MHelper { + static m2m = null; + + constructor() { + M2MHelper.m2m = m2mAuth( + _.pick(config, ["AUTH0_URL", "AUTH0_AUDIENCE", "TOKEN_CACHE_TIME", "AUTH0_PROXY_SERVER_URL"]) + ); + } + /** + * Get M2M token. + * @returns {Promise} the M2M token + */ + getM2MToken() { + return M2MHelper.m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET); + } +} + +module.exports = new M2MHelper(); diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index 3b3278ef..259a6e20 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -1,179 +1,241 @@ -const _ = require('lodash') -const uuid = require('uuid/v4') -const moment = require('moment') +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; -const errors = require('./errors') -const helper = require('./helper') +const { + DomainHelper: { getScanCriteria }, +} = require("@topcoder-framework/lib-common"); -class ChallengePhaseHelper { - /** - * Populate challenge phases. - * @param {Array} phases the phases to populate - * @param {Date} startDate the challenge start date - * @param {String} timelineTemplateId the timeline template id - */ - async populatePhases (phases, startDate, timelineTemplateId) { - if (_.isUndefined(timelineTemplateId)) { - throw new errors.BadRequestError(`Invalid timeline template ID: ${timelineTemplateId}`) - } +const { PhaseDomain } = require("@topcoder-framework/domain-challenge"); - const { timelineTempate, timelineTemplateMap } = await this.getTemplateAndTemplateMap(timelineTemplateId) - const { phaseDefinitionMap } = await this.getPhaseDefinitionsAndMap() +const _ = require("lodash"); - if (!phases || phases.length === 0) { - // auto populate phases - for (const p of timelineTempate) { - phases.push({ ...p }) - } - } +const uuid = require("uuid/v4"); +const moment = require("moment"); - for (const p of phases) { - const phaseDefinition = phaseDefinitionMap.get(p.phaseId) +const errors = require("./errors"); - p.id = uuid() - p.name = phaseDefinition.name - p.description = phaseDefinition.description +const timelineTemplateService = require("../services/TimelineTemplateService"); - // set p.open based on current phase - const phaseTemplate = timelineTemplateMap.get(p.phaseId) - if (phaseTemplate) { - if (!p.duration) { - p.duration = phaseTemplate.defaultDuration - } +const phaseDomain = new PhaseDomain(GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT); - if (phaseTemplate.predecessor) { - const predecessor = _.find(phases, { phaseId: phaseTemplate.predecessor }) - if (!predecessor) { - throw new errors.BadRequestError(`Predecessor ${phaseTemplate.predecessor} not found in given phases.`) - } - p.predecessor = phaseTemplate.predecessor +class ChallengePhaseHelper { + phaseDefinitionMap = {}; + timelineTemplateMap = {}; + + async populatePhasesForChallengeCreation(phases, startDate, timelineTemplateId) { + if (_.isUndefined(timelineTemplateId)) { + throw new errors.BadRequestError(`Invalid timeline template ID: ${timelineTemplateId}`); + } + const { timelineTempate } = await this.getTemplateAndTemplateMap(timelineTemplateId); + console.log("Selected timeline template", JSON.stringify(timelineTempate)); + const { phaseDefinitionMap } = await this.getPhaseDefinitionsAndMap(); + let fixedStartDate = undefined; + const finalPhases = _.map(timelineTempate, (phaseFromTemplate) => { + const phaseDefinition = phaseDefinitionMap.get(phaseFromTemplate.phaseId); + const phaseFromInput = _.find(phases, (p) => p.phaseId === phaseFromTemplate.phaseId); + const phase = { + id: uuid(), + phaseId: phaseFromTemplate.phaseId, + name: phaseDefinition.name, + description: phaseDefinition.description, + duration: _.defaultTo(_.get(phaseFromInput, "duration"), phaseFromTemplate.defaultDuration), + isOpen: false, + predecessor: phaseFromTemplate.predecessor, + constraints: _.defaultTo(_.get(phaseFromInput, "constraints"), []), + scheduledStartDate: undefined, + scheduledEndDate: undefined, + actualStartDate: undefined, + actualEndDate: undefined, + }; + if (_.isUndefined(phase.predecessor)) { + let scheduledStartDate = _.defaultTo( + _.get(phaseFromInput, "scheduledStartDate"), + startDate + ); + if ( + !_.isUndefined(fixedStartDate) && + moment(scheduledStartDate).isSameOrBefore(moment(fixedStartDate)) + ) { + scheduledStartDate = moment(fixedStartDate).add(5, "minutes").toDate().toISOString(); } + phase.scheduledStartDate = moment(scheduledStartDate).toDate().toISOString(); + phase.scheduledEndDate = moment(phase.scheduledStartDate) + .add(phase.duration, "seconds") + .toDate() + .toISOString(); } + if (_.isUndefined(fixedStartDate)) { + fixedStartDate = phase.scheduledStartDate; + } + return phase; + }); + for (let phase of finalPhases) { + if (_.isUndefined(phase.predecessor)) { + continue; + } + const precedecessorPhase = _.find(finalPhases, { + phaseId: phase.predecessor, + }); + if (phase.name === "Iterative Review") { + phase.scheduledStartDate = precedecessorPhase.scheduledStartDate; + } else { + phase.scheduledStartDate = precedecessorPhase.scheduledEndDate; + } + phase.scheduledEndDate = moment(phase.scheduledStartDate) + .add(phase.duration, "seconds") + .toDate() + .toISOString(); } + return finalPhases; + } - // calculate dates - if (!startDate) { - return - } - - // sort phases by predecessor - phases.sort((a, b) => { - if (a.predecessor === b.phaseId) { - return 1 + async populatePhasesForChallengeUpdate( + challengePhases, + newPhases, + timelineTemplateId, + isBeingActivated + ) { + const { timelineTemplateMap } = await this.getTemplateAndTemplateMap(timelineTemplateId); + const { phaseDefinitionMap } = await this.getPhaseDefinitionsAndMap(); + let fixedStartDate = undefined; + const updatedPhases = _.map(challengePhases, (phase) => { + const phaseFromTemplate = timelineTemplateMap.get(phase.phaseId); + const phaseDefinition = phaseDefinitionMap.get(phase.phaseId); + const newPhase = _.find(newPhases, (p) => p.phaseId === phase.phaseId); + const updatedPhase = { + ...phase, + predecessor: phaseFromTemplate && phaseFromTemplate.predecessor, + description: phaseDefinition.description, + }; + if (updatedPhase.name === "Post-Mortem") { + updatedPhase.predecessor = "a93544bc-c165-4af4-b55e-18f3593b457a"; } - if (b.predecessor === a.phaseId) { - return -1 + if (_.isUndefined(updatedPhase.actualEndDate)) { + updatedPhase.duration = _.defaultTo(_.get(newPhase, "duration"), updatedPhase.duration); } - return 0 - }) - - let isSubmissionPhaseOpen = false - - for (let p of phases) { - const predecessor = timelineTemplateMap.get(p.predecessor) - - if (predecessor == null) { - if (p.name === 'Registration') { - p.scheduledStartDate = moment(startDate).toDate() + if (_.isUndefined(updatedPhase.predecessor)) { + let scheduledStartDate = _.defaultTo( + _.get(newPhase, "scheduledStartDate"), + updatedPhase.scheduledStartDate + ); + if ( + !_.isUndefined(fixedStartDate) && + moment(scheduledStartDate).isSameOrBefore(moment(fixedStartDate)) + ) { + scheduledStartDate = moment(fixedStartDate).add(5, "minutes").toDate().toISOString(); } - if (_.includes(['Submission', 'Checkpoint Submission'], p.name)) { - if (p.scheduledStartDate != null) { - p.scheduledStartDate = moment(p.scheduledStartDate).toDate() - } else { - p.scheduledStartDate = moment(startDate).add(5, 'minutes').toDate() - } + if (isBeingActivated && moment(scheduledStartDate).isSameOrBefore(moment())) { + updatedPhase.isOpen = true; + updatedPhase.scheduledStartDate = moment().toDate().toISOString(); + updatedPhase.actualStartDate = updatedPhase.scheduledStartDate; + } else if (_.isUndefined(phase.actualStartDate)) { + updatedPhase.scheduledStartDate = moment(scheduledStartDate).toDate().toISOString(); } - - if (moment(p.scheduledStartDate).isSameOrBefore(moment())) { - p.actualStartDate = p.scheduledStartDate - } else { - delete p.actualStartDate - } - - p.scheduledEndDate = moment(p.scheduledStartDate).add(p.duration, 'seconds').toDate() - if (moment(p.scheduledEndDate).isBefore(moment())) { - delete p.actualEndDate - } else { - p.actualEndDate = p.scheduledEndDate - } - } else { - const precedecessorPhase = _.find(phases, { phaseId: predecessor.phaseId }) - if (precedecessorPhase == null) { - throw new errors.BadRequestError(`Predecessor ${predecessor.phaseId} not found in given phases.`) - } - let phaseEndDate = moment(precedecessorPhase.scheduledEndDate) - if (precedecessorPhase.actualEndDate != null && moment(precedecessorPhase.actualEndDate).isAfter(phaseEndDate)) { - phaseEndDate = moment(precedecessorPhase.actualEndDate) - } else { - phaseEndDate = moment(precedecessorPhase.scheduledEndDate) - } - - p.scheduledStartDate = phaseEndDate.toDate() - p.scheduledEndDate = moment(p.scheduledStartDate).add(p.duration, 'seconds').toDate() + updatedPhase.scheduledEndDate = moment(updatedPhase.scheduledStartDate) + .add(updatedPhase.duration, "seconds") + .toDate() + .toISOString(); } - - p.isOpen = moment().isBetween(p.scheduledStartDate, p.scheduledEndDate) - - if (p.isOpen) { - if (p.name === 'Submission') { - isSubmissionPhaseOpen = true - } - delete p.actualEndDate + if ( + _.isUndefined(phase.actualEndDate) && + !_.isUndefined(newPhase) && + !_.isUndefined(newPhase.constraints) + ) { + updatedPhase.constraints = newPhase.constraints; } - - if (moment(p.scheduledStartDate).isAfter(moment())) { - delete p.actualStartDate - delete p.actualEndDate + if (_.isUndefined(fixedStartDate)) { + fixedStartDate = updatedPhase.scheduledStartDate; } - - if (p.name === 'Post-Mortem' && isSubmissionPhaseOpen) { - delete p.actualStartDate - delete p.actualEndDate - p.isOpen = false + return updatedPhase; + }); + let iterativeReviewSet = false; + for (let phase of updatedPhases) { + if (_.isUndefined(phase.predecessor)) { + continue; + } + const predecessorPhase = _.find(updatedPhases, { + phaseId: phase.predecessor, + }); + if (phase.name === "Iterative Review") { + if (!iterativeReviewSet) { + if (_.isUndefined(phase.actualStartDate)) { + phase.scheduledStartDate = predecessorPhase.scheduledStartDate; + } + iterativeReviewSet = true; + } + } else if (_.isUndefined(phase.actualStartDate)) { + phase.scheduledStartDate = predecessorPhase.scheduledEndDate; + } + if (_.isUndefined(phase.actualEndDate)) { + phase.scheduledEndDate = moment(phase.scheduledStartDate) + .add(phase.duration, "seconds") + .toDate() + .toISOString(); } } + return updatedPhases; + } - // phases.sort((a, b) => moment(a.scheduledStartDate).isAfter(b.scheduledStartDate)) + handlePhasesAfterCancelling(phases) { + return _.map(phases, (phase) => { + const shouldClosePhase = _.includes( + ["Registration", "Submission", "Checkpoint Submission"], + phase.name + ); + return { + ...phase, + isOpen: shouldClosePhase ? false : phase.isOpen, + actualEndDate: shouldClosePhase ? moment().toDate().toISOString() : phase.actualEndDate, + }; + }); } - async validatePhases (phases) { + async validatePhases(phases) { if (!phases || phases.length === 0) { - return + return; } - const records = await helper.scan('Phase') - const map = new Map() - _.each(records, (r) => { - map.set(r.id, r) - }) - const invalidPhases = _.filter(phases, (p) => !map.has(p.phaseId)) + const { phaseDefinitionMap } = await this.getPhaseDefinitionsAndMap(); + const invalidPhases = _.filter(phases, (p) => !phaseDefinitionMap.has(p.phaseId)); if (invalidPhases.length > 0) { throw new errors.BadRequestError( `The following phases are invalid: ${toString(invalidPhases)}` - ) + ); } } - async getPhaseDefinitionsAndMap () { - const records = await helper.scan('Phase') - const map = new Map() - _.each(records, (r) => { - map.set(r.id, r) - }) - return { phaseDefinitions: records, phaseDefinitionMap: map } + async getPhase(phaseId) { + const { phaseDefinitionMap } = await this.getPhaseDefinitionsAndMap(); + return phaseDefinitionMap.get(phaseId); } - async getTemplateAndTemplateMap (timelineTemplateId) { - const records = await helper.getById('TimelineTemplate', timelineTemplateId) - const map = new Map() - _.each(records.phases, (r) => { - map.set(r.phaseId, r) - }) + async getPhaseDefinitionsAndMap() { + if (_.isEmpty(this.phaseDefinitionMap)) { + const { items: records } = await phaseDomain.scan({ criteria: getScanCriteria({}) }); + + const map = new Map(); + _.each(records, (r) => { + map.set(r.id, r); + }); + + this.phaseDefinitionMap = { phaseDefinitions: records, phaseDefinitionMap: map }; + } + return this.phaseDefinitionMap; + } - return { - timelineTempate: records.phases, - timelineTemplateMap: map + async getTemplateAndTemplateMap(timelineTemplateId) { + if (_.isEmpty(this.timelineTemplateMap[timelineTemplateId])) { + const records = await timelineTemplateService.getTimelineTemplate(timelineTemplateId); + const map = new Map(); + _.each(records.phases, (r) => { + map.set(r.phaseId, r); + }); + + this.timelineTemplateMap[timelineTemplateId] = { + timelineTempate: records.phases, + timelineTemplateMap: map, + }; } + return this.timelineTemplateMap[timelineTemplateId]; } } -module.exports = new ChallengePhaseHelper() +module.exports = new ChallengePhaseHelper(); diff --git a/src/common/project-helper.js b/src/common/project-helper.js new file mode 100644 index 00000000..c7c3392c --- /dev/null +++ b/src/common/project-helper.js @@ -0,0 +1,93 @@ +const _ = require("lodash"); + +const axios = require("axios"); +const config = require("config"); +const HttpStatus = require("http-status-codes"); +const m2mHelper = require("./m2m-helper"); +const { hasAdminRole } = require("./role-helper"); +const errors = require("./errors"); + +class ProjectHelper { + /** + * Get Project Details + * @param {String} projectId the project id + * @param {String} currentUser the user + * + * @returns {Promise} the project details + */ + async getProject(projectId, currentUser) { + let token = await m2mHelper.getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + try { + const res = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (currentUser.isMachine || hasAdminRole(currentUser)) { + return res.data; + } + if ( + _.get(res, "data.type") === "self-service" && + _.includes(config.SELF_SERVICE_WHITELIST_HANDLES, currentUser.handle.toLowerCase()) + ) { + return res.data; + } + if ( + !_.find( + _.get(res, "data.members", []), + (m) => _.toString(m.userId) === _.toString(currentUser.userId) + ) + ) { + throw new errors.ForbiddenError(`You don't have access to project with ID: ${projectId}`); + } + return res.data; + } catch (err) { + if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`); + } else { + // re-throw other error + throw err; + } + } + } + + /** + * This functions gets the default billing account for a given project id + * + * @param {Number} projectId The id of the project for which to get the default terms of use + * @returns {Promise} The billing account ID + */ + async getProjectBillingInformation(projectId) { + const token = await m2mHelper.getM2MToken(); + const projectUrl = `${config.PROJECTS_API_URL}/${projectId}/billingAccount`; + try { + const res = await axios.get(projectUrl, { + headers: { Authorization: `Bearer ${token}` }, + }); + + let markup = _.get(res, "data.markup", null) + ? _.toNumber(_.get(res, "data.markup", null)) + : null; + + if (markup && markup > 0) { + markup = (markup * 100) / 10000; + } + return { + billingAccountId: _.get(res, "data.tcBillingAccountId", null), + markup, + }; + } catch (err) { + const responseCode = _.get(err, "response.status"); + + if (responseCode === HttpStatus.NOT_FOUND) { + return { + billingAccountId: null, + markup: null, + }; + } else { + throw err; + } + } + } +} + +module.exports = new ProjectHelper(); diff --git a/src/common/role-helper.js b/src/common/role-helper.js new file mode 100644 index 00000000..f30720f9 --- /dev/null +++ b/src/common/role-helper.js @@ -0,0 +1,20 @@ +const constants = require("../../app-constants"); + +/** + * Check if the user has admin role + * @param {Object} authUser the user + */ +function hasAdminRole(authUser) { + if (authUser && authUser.roles) { + for (const role of authUser.roles) { + if (role.toLowerCase() === constants.UserRoles.Admin.toLowerCase()) { + return true; + } + } + } + return false; +} + +module.exports = { + hasAdminRole, +}; diff --git a/src/common/s3ParseUrl.js b/src/common/s3ParseUrl.js index 242ced07..e84f51ba 100644 --- a/src/common/s3ParseUrl.js +++ b/src/common/s3ParseUrl.js @@ -2,48 +2,48 @@ * Extract components from s3 url. */ -module.exports = function s3ParseUrl (url) { - var decodedUrl = decodeURIComponent(url) - let match +module.exports = function s3ParseUrl(url) { + var decodedUrl = decodeURIComponent(url); + let match; // http://s3.amazonaws.com/bucket/key1/key2 - match = decodedUrl.match(/^https?:\/\/s3.amazonaws.com\/([^/]+)\/?(.*?)$/) + match = decodedUrl.match(/^https?:\/\/s3.amazonaws.com\/([^/]+)\/?(.*?)$/); if (match) { return { bucket: match[1], key: match[2], - region: '' - } + region: "", + }; } // http://s3-aws-region.amazonaws.com/bucket/key1/key2 - match = decodedUrl.match(/^https?:\/\/s3-([^.]+).amazonaws.com\/([^/]+)\/?(.*?)$/) + match = decodedUrl.match(/^https?:\/\/s3-([^.]+).amazonaws.com\/([^/]+)\/?(.*?)$/); if (match) { return { bucket: match[2], key: match[3], - region: match[1] - } + region: match[1], + }; } // http://bucket.s3.amazonaws.com/key1/key2 - match = decodedUrl.match(/^https?:\/\/([^.]+).s3.amazonaws.com\/?(.*?)$/) + match = decodedUrl.match(/^https?:\/\/([^.]+).s3.amazonaws.com\/?(.*?)$/); if (match) { return { bucket: match[1], key: match[2], - region: '' - } + region: "", + }; } // http://bucket.s3-aws-region.amazonaws.com/key1/key2 or, // http://bucket.s3.aws-region.amazonaws.com/key1/key2 - match = decodedUrl.match(/^https?:\/\/([^.]+).(?:s3-|s3\.)([^.]+).amazonaws.com\/?(.*?)$/) + match = decodedUrl.match(/^https?:\/\/([^.]+).(?:s3-|s3\.)([^.]+).amazonaws.com\/?(.*?)$/); if (match) { return { bucket: match[1], key: match[3], - region: match[2] - } + region: match[2], + }; } -} +}; diff --git a/src/common/srm-helper.js b/src/common/srm-helper.js new file mode 100644 index 00000000..a87791e0 --- /dev/null +++ b/src/common/srm-helper.js @@ -0,0 +1,251 @@ +const _ = require("lodash"); +const moment = require("moment"); + +const SRMScheduleKeyMappings = _.reduce( + [ + "roundId", + "name", + "shortName", + "contestName", + "roundType", + "status", + "registrationStartTime", + "registrationEndTime", + "codingStartTime", + "codingEndTime", + "intermissionStartTime", + "intermissionEndTime", + "challengeStartTime", + "challengeEndTime", + "systestStartTime", + "systestEndTime", + ], + (acc, field) => ({ ...acc, [_.toLower(field)]: field }), + {} +); + +const PracticeProblemsKeyMappings = _.reduce( + [ + "problemId", + "componentId", + "roomId", + "roundId", + "divisionId", + "problemName", + "problemType", + "difficulty", + "status", + "points", + "myPoints", + ], + (acc, field) => ({ ...acc, [_.toLower(field)]: field }), + {} +); + +/** + * Get schedule query + * @param {Object} filter the query filter + * @param {Array} filter.statuses the statues + * @param {Date} filter.registrationStartTimeAfter the start of the registration time + * @param {Date=} filter.registrationStartTimeBefore the end of the registration time + * @param {String} filter.sortBy the sort field + * @param {String} filter.sortOrder the sort order + * @param {Number} filter.page the sort order + * @param {Number} filter.perPage the sort order + */ +function getSRMScheduleQuery(filter) { + const offset = (filter.page - 1) * filter.perPage; + let sortBy = filter.sortBy; + if (filter.sortBy === "registrationStartTime") { + sortBy = "reg.start_time"; + } else if (filter.sortBy === "codingStartTime") { + sortBy = "coding.start_time"; + } else if (filter.sortBy === "challengeStartTime") { + sortBy = "challenge.start_time"; + } + const statuses = _.join( + _.map(filter.statuses, (s) => `'${_.toUpper(s)}'`), + "," + ); + const registrationTimeFilter = `reg.start_time >= '${moment( + filter.registrationStartTimeAfter + ).format("yyyy-MM-DD HH:mm:ss")}'${ + filter.registrationStartTimeBefore + ? ` AND reg.start_time <= '${moment(filter.registrationStartTimeBefore).format( + "yyyy-MM-DD HH:mm:ss" + )}'` + : "" + }`; + + const query = `SELECT + SKIP ${offset} + FIRST ${filter.perPage} + r.round_id AS roundId + , r.name AS name + , r.short_name AS shortName + , c.name AS contestName + , rt.round_type_desc AS roundType + , r.status AS status + , reg.start_time AS registrationStartTime + , reg.end_time AS registrationEndTime + , coding.start_time AS codingStartTime + , coding.end_time AS codingEndTime + , intermission.start_time AS intermissionStartTime + , intermission.end_time AS intermissionEndTime + , challenge.start_time AS challengeStartTime + , challenge.end_time AS challengeEndTime + , systest.start_time AS systestStartTime + , systest.end_time AS systestEndTime + FROM + informixoltp:contest AS c + INNER JOIN informixoltp:round AS r ON r.contest_id = c.contest_id + INNER JOIN informixoltp:round_type_lu AS rt ON rt.round_type_id = r.round_type_id + LEFT JOIN informixoltp:round_segment AS reg ON reg.round_id = r.round_id AND reg.segment_id = 1 + LEFT JOIN informixoltp:round_segment AS coding ON coding.round_id = r.round_id AND coding.segment_id = 2 + LEFT JOIN informixoltp:round_segment AS intermission ON intermission.round_id = r.round_id AND intermission.segment_id = 3 + LEFT JOIN informixoltp:round_segment AS challenge ON challenge.round_id = r.round_id AND challenge.segment_id = 4 + LEFT JOIN informixoltp:round_segment AS systest ON systest.round_id = r.round_id AND systest.segment_id = 5 + WHERE + r.round_type_id in (1,2,10) AND + UPPER(r.status) in (${statuses}) AND + ${registrationTimeFilter} + ORDER BY ${sortBy} ${filter.sortOrder}`; + return query; +} + +/** + * Get schedule query + * @param {Object} criteria the query criteria + * @param {String} criteria.userId the user id + * @param {String} criteria.sortBy the sort field + * @param {String} criteria.sortOrder the sort order + * @param {Number} criteria.page the sort order + * @param {Number} criteria.perPage the sort order + * @param {String=} criteria.difficulty the sort order + * @param {String=} criteria.status the sort order + * @param {Number=} criteria.pointsLowerBound the sort order + * @param {Number=} criteria.pointsUpperBound the statues + * @param {String=} criteria.problemName the start of the registration time + */ +function getPracticeProblemsQuery(criteria) { + const offset = (criteria.page - 1) * criteria.perPage; + let sortBy = criteria.sortBy; + if (criteria.sortBy === "problemId") { + sortBy = "p.problem_id"; + } else if (criteria.sortBy === "problemName") { + sortBy = "p.name"; + } else if (criteria.sortBy === "problemType") { + sortBy = "ptl.problem_type_desc"; + } else if (criteria.sortBy === "points") { + sortBy = "rc.points"; + } else if (criteria.sortBy === "difficulty") { + sortBy = "p.proposed_difficulty_id"; + } else if (criteria.sortBy === "status") { + sortBy = "pcs.status_id"; + } else if (criteria.sortBy === "myPoints") { + sortBy = "NVL(pcs.points, 0)"; + } + const filters = []; + if (criteria.difficulty) { + if (criteria.difficulty === "easy") { + filters.push(`p.proposed_difficulty_id=1`); + } else if (criteria.difficulty === "medium") { + filters.push(`p.proposed_difficulty_id=2`); + } else if (criteria.difficulty === "hard") { + filters.push(`p.proposed_difficulty_id=3`); + } + } + if (criteria.status) { + if (criteria.status === "new") { + filters.push("NVL(pcs.status_id, 0) < 120"); + } else if (criteria.status === "viewed") { + filters.push("pcs.status_id >= 120 AND pcs.status_id != 150"); + } else if (criteria.status === "solved") { + filters.push("pcs.status_id = 150"); + } + } + if (criteria.pointsLowerBound) { + filters.push(`rc.points >= ${criteria.pointsLowerBound}`); + } + if (criteria.pointsUpperBound) { + filters.push(`rc.points <= ${criteria.pointsUpperBound}`); + } + if (criteria.problemName) { + filters.push( + `lower(p.name) like '%${_.toLower(_.replace(criteria.problemName, /[^a-z0-9]/gi, ""))}%'` + ); + } + + const queryCount = `SELECT count(*) AS count`; + + const querySelect = `SELECT + SKIP ${offset} + FIRST ${criteria.perPage} + p.problem_id AS problemId + , c.component_id AS componentId + , ro.room_id AS roomId + , rc.round_id AS roundId + , rc.division_id AS divisionId + , p.name AS problemName + , ptl.problem_type_desc AS problemType + , CASE WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 1) THEN 'Easy'::nvarchar(50) + WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 2) THEN 'Medium'::nvarchar(50) + WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 3) THEN 'Hard'::nvarchar(50) + END AS difficulty + , rc.points AS points + , CASE WHEN NVL(pcs.status_id, 0) < 120 THEN 'New'::nvarchar(50) + WHEN pcs.status_id = 150 THEN 'Solved'::nvarchar(50) + WHEN pcs.status_id >= 120 AND pcs.status_id != 150 THEN 'Viewed'::nvarchar(50) + END AS status + , NVL(pcs.points, 0) AS myPoints`; + + const queryFrom = `FROM informixoltp:problem p + INNER JOIN informixoltp:problem_type_lu ptl ON ptl.problem_type_id = p.problem_type_id + INNER JOIN informixoltp:component c ON c.problem_id = p.problem_id + INNER JOIN informixoltp:round_component rc ON rc.component_id = c.component_id + INNER JOIN informixoltp:round r ON r.round_id = rc.round_id AND r.status = 'A' AND r.round_type_id = 3 + INNER JOIN informixoltp:room ro ON ro.round_id = rc.round_id AND ro.room_type_id = 3 + LEFT JOIN informixoltp:component_state pcs ON pcs.round_id = rc.round_id AND pcs.component_id = c.component_id AND pcs.coder_id = ${criteria.userId}`; + + const queryWhere = filters.length ? `WHERE ${_.join(filters, " AND ")}` : ""; + + const queryOrder = `ORDER BY ${sortBy} ${criteria.sortOrder}`; + + const query = `${querySelect} ${queryFrom} ${queryWhere} ${queryOrder}`; + const countQuery = `${queryCount} ${queryFrom} ${queryWhere}`; + return { query, countQuery }; +} + +function convertSRMScheduleQueryOutput(queryOutput) { + return transformDatabaseResponse(queryOutput, SRMScheduleKeyMappings); +} + +function convertPracticeProblemsQueryOutput(queryOutput) { + return transformDatabaseResponse(queryOutput, PracticeProblemsKeyMappings); +} + +function transformDatabaseResponse(databaseResponse, keyMappings) { + const transformedData = []; + + if (databaseResponse && databaseResponse.rows && Array.isArray(databaseResponse.rows)) { + databaseResponse.rows.forEach((row) => { + const record = {}; + if (row.fields && Array.isArray(row.fields)) { + row.fields.forEach((field) => { + const lowercaseKey = field.key.toLowerCase(); + const mappedKey = keyMappings[lowercaseKey] || lowercaseKey; + record[mappedKey] = field.value; + }); + } + transformedData.push(record); + }); + } + return transformedData; +} + +module.exports = { + getSRMScheduleQuery, + convertSRMScheduleQueryOutput, + getPracticeProblemsQuery, + convertPracticeProblemsQueryOutput, +}; diff --git a/src/common/transformer.js b/src/common/transformer.js new file mode 100644 index 00000000..86861f76 --- /dev/null +++ b/src/common/transformer.js @@ -0,0 +1,88 @@ +const _ = require("lodash"); +const { compareVersions } = require("compare-versions"); +const challengeService = require("../services/ChallengeService"); + +function transformData(data, fieldsToDelete) { + if (!fieldsToDelete || !fieldsToDelete.length) { + return data; + } + + if (_.isArray(data)) { + _.each(data, (item, index) => { + data[index] = transformData(item, fieldsToDelete); + }); + } else if (_.isObject(data)) { + for (const field of fieldsToDelete) { + delete data[field]; + } + if (data.result) { + data.result = transformData(data.result, fieldsToDelete); + } + } + + return data; +} + +function transformServices() { + _.each(services, (service, serviceName) => { + const serviceConfig = servicesConfig[serviceName]; + if (!serviceConfig) { + return; + } + + _.each(service, (method, methodName) => { + service[methodName] = async function () { + const args = Array.prototype.slice.call(arguments); + + // No transform need for this method + if (!serviceConfig.methods.includes(methodName)) { + return await method.apply(this, args.slice(1)); + } + + // args[0] is request, get version header + const request = args[0]; + const apiVersion = request.headers["app-version"] || "1.0.0"; + + const fieldsToDelete = []; + _.each(serviceConfig.fieldsVersion, (version, field) => { + // If input version less than required version, delete fields + if (compareVersions(apiVersion, version) < 0) { + fieldsToDelete.push(field); + } + }); + + // Transform request body by deleting fields + if (_.isArray(request.body) || _.isObject(request.body)) { + transformData(request.body, fieldsToDelete); + } + + const data = await method.apply(this, args.slice(1)); + + // Transform response data by deleting fields + return transformData(data, fieldsToDelete); + }; + service[methodName].params = ["req", ...method.params]; + }); + }); +} + +// Define the version config for services +const servicesConfig = { + challengeService: { + methods: ["searchChallenges", "getChallenge", "createChallenge", "updateChallenge"], + fieldsVersion: { + skills: "1.1.0", + payments: "2.0.0", + }, + }, +}; + +// Define the services to export +const services = { + challengeService, +}; + +// Transform services before export +transformServices(); + +module.exports = services; diff --git a/src/controllers/AttachmentController.js b/src/controllers/AttachmentController.js index a1f64963..e5fa00af 100644 --- a/src/controllers/AttachmentController.js +++ b/src/controllers/AttachmentController.js @@ -1,19 +1,19 @@ /** * Controller for attachment endpoints */ -const HttpStatus = require('http-status-codes') -const _ = require('lodash') -const service = require('../services/AttachmentService') +const HttpStatus = require("http-status-codes"); +const _ = require("lodash"); +const service = require("../services/AttachmentService"); /** * Create attachment * @param {Object} req the request * @param {Object} res the response */ -async function createAttachment (req, res) { - const body = _.isArray(req.body) ? req.body : [req.body] - const result = await service.createAttachment(req.authUser, req.params.challengeId, body) - res.status(HttpStatus.CREATED).send(result) +async function createAttachment(req, res) { + const body = _.isArray(req.body) ? req.body : [req.body]; + const result = await service.createAttachment(req.authUser, req.params.challengeId, body); + res.status(HttpStatus.CREATED).send(result); } /** @@ -21,9 +21,13 @@ async function createAttachment (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getAttachment (req, res) { - const result = await service.getAttachment(req.authUser, req.params.challengeId, req.params.attachmentId) - res.send(result) +async function getAttachment(req, res) { + const result = await service.getAttachment( + req.authUser, + req.params.challengeId, + req.params.attachmentId + ); + res.send(result); } /** @@ -31,9 +35,14 @@ async function getAttachment (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function updateAttachment (req, res) { - const result = await service.fullyUpdateAttachment(req.authUser, req.params.challengeId, req.params.attachmentId, req.body) - res.send(result) +async function updateAttachment(req, res) { + const result = await service.fullyUpdateAttachment( + req.authUser, + req.params.challengeId, + req.params.attachmentId, + req.body + ); + res.send(result); } /** @@ -41,9 +50,14 @@ async function updateAttachment (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function partiallyUpdateAttachment (req, res) { - const result = await service.partiallyUpdateAttachment(req.authUser, req.params.challengeId, req.params.attachmentId, req.body) - res.send(result) +async function partiallyUpdateAttachment(req, res) { + const result = await service.partiallyUpdateAttachment( + req.authUser, + req.params.challengeId, + req.params.attachmentId, + req.body + ); + res.send(result); } /** @@ -51,9 +65,13 @@ async function partiallyUpdateAttachment (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function deleteAttachment (req, res) { - const result = await service.deleteAttachment(req.authUser, req.params.challengeId, req.params.attachmentId) - res.send(result) +async function deleteAttachment(req, res) { + const result = await service.deleteAttachment( + req.authUser, + req.params.challengeId, + req.params.attachmentId + ); + res.send(result); } /** @@ -61,11 +79,15 @@ async function deleteAttachment (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function downloadAttachment (req, res) { - const result = await service.downloadAttachment(req.authUser, req.params.challengeId, req.params.attachmentId) - res.attachment(result.fileName) - res.set('Content-Type', result.mimetype) - res.send(result.data) +async function downloadAttachment(req, res) { + const result = await service.downloadAttachment( + req.authUser, + req.params.challengeId, + req.params.attachmentId + ); + res.attachment(result.fileName); + res.set("Content-Type", result.mimetype); + res.send(result.data); } module.exports = { @@ -74,5 +96,5 @@ module.exports = { updateAttachment, partiallyUpdateAttachment, deleteAttachment, - downloadAttachment -} + downloadAttachment, +}; diff --git a/src/controllers/AuditLogController.js b/src/controllers/AuditLogController.js index d4199aaa..771ec371 100644 --- a/src/controllers/AuditLogController.js +++ b/src/controllers/AuditLogController.js @@ -1,20 +1,20 @@ /** * Controller for challenge type endpoints */ -const service = require('../services/AuditLogService') -const helper = require('../common/helper') +const service = require("../services/AuditLogService"); +const helper = require("../common/helper"); /** * Search audit logs * @param {Object} req the request * @param {Object} res the response */ -async function searchAuditLogs (req, res) { - const result = await service.searchAuditLogs(req.query) - helper.setResHeaders(req, res, result) - res.send(result.result) +async function searchAuditLogs(req, res) { + const result = await service.searchAuditLogs(req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } module.exports = { - searchAuditLogs -} + searchAuditLogs, +}; diff --git a/src/controllers/ChallengeController.js b/src/controllers/ChallengeController.js index 3fe6ee5f..2d726578 100644 --- a/src/controllers/ChallengeController.js +++ b/src/controllers/ChallengeController.js @@ -1,37 +1,44 @@ /** * Controller for challenge endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/ChallengeService') -const helper = require('../common/helper') -const logger = require('../common/logger') +const HttpStatus = require("http-status-codes"); +const { challengeService: service } = require("../common/transformer"); +const helper = require("../common/helper"); +const logger = require("../common/logger"); /** * Search challenges * @param {Object} req the request * @param {Object} res the response */ -async function searchChallenges (req, res) { - let result = await service.searchChallenges(req.authUser, { ...req.query, ...req.body }) +async function searchChallenges(req, res) { + let result = await service.searchChallenges(req, req.authUser, { + ...req.query, + ...req.body, + }); if (!result.total && req.query.legacyId) { // maybe the legacyId is roundId for mm challenge // mm challenge use projectId as legacyId try { - logger.debug(`Staring to get mm challengeId`) - const legacyId = await helper.getProjectIdByRoundId(req.query.legacyId) - logger.debug(`Get mm challengeId successfully ${legacyId}`) - result = await service.searchChallenges(req.authUser, { ...req.query, ...req.body, legacyId }) - logger.debug(`Get mm challenge successfully`) + logger.debug(`Staring to get mm challengeId`); + const legacyId = await helper.getProjectIdByRoundId(req.query.legacyId); + logger.debug(`Get mm challengeId successfully ${legacyId}`); + result = await service.searchChallenges(req, req.authUser, { + ...req.query, + ...req.body, + legacyId, + }); + logger.debug(`Get mm challenge successfully`); } catch (e) { - logger.debug(`Failed to get projectId with error: ${e.message}`) + logger.debug(`Failed to get projectId with error: ${e.message}`); } } else { if (req.query.legacyId) { - logger.debug(`Skipped to get mm challenge`) + logger.debug(`Skipped to get mm challenge`); } } - helper.setResHeaders(req, res, result) - res.send(result.result) + helper.setResHeaders(req, res, result); + res.send(result.result); } /** @@ -39,10 +46,12 @@ async function searchChallenges (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function createChallenge (req, res) { - logger.debug(`createChallenge User: ${JSON.stringify(req.authUser)} - Body: ${JSON.stringify(req.body)}`) - const result = await service.createChallenge(req.authUser, req.body, req.userToken) - res.status(HttpStatus.CREATED).send(result) +async function createChallenge(req, res) { + logger.debug( + `createChallenge User: ${JSON.stringify(req.authUser)} - Body: ${JSON.stringify(req.body)}` + ); + const result = await service.createChallenge(req, req.authUser, req.body, req.userToken); + res.status(HttpStatus.CREATED).send(result); } /** @@ -50,9 +59,9 @@ async function createChallenge (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function sendNotifications (req, res) { - const result = await service.sendNotifications(req.authUser, req.params.challengeId) - res.status(HttpStatus.CREATED).send(result) +async function sendNotifications(req, res) { + const result = await service.sendNotifications(req, req.authUser, req.params.challengeId); + res.status(HttpStatus.CREATED).send(result); } /** @@ -60,9 +69,14 @@ async function sendNotifications (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getChallenge (req, res) { - const result = await service.getChallenge(req.authUser, req.params.challengeId, req.query.checkIfExists) - res.send(result) +async function getChallenge(req, res) { + const result = await service.getChallenge( + req, + req.authUser, + req.params.challengeId, + req.query.checkIfExists + ); + res.send(result); } /** @@ -70,51 +84,99 @@ async function getChallenge (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getChallengeStatistics (req, res) { - const result = await service.getChallengeStatistics(req.authUser, req.params.challengeId) - res.send(result) +async function getChallengeStatistics(req, res) { + const result = await service.getChallengeStatistics(req, req.authUser, req.params.challengeId); + res.send(result); } /** - * Fully update challenge + * Partially update challenge * @param {Object} req the request * @param {Object} res the response */ -async function fullyUpdateChallenge (req, res) { - logger.debug(`fullyUpdateChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${req.params.challengeId} - Body: ${JSON.stringify(req.body)}`) - const result = await service.fullyUpdateChallenge(req.authUser, req.params.challengeId, req.body) - res.send(result) +async function updateChallenge(req, res) { + logger.debug( + `updateChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${ + req.params.challengeId + } - Body: ${JSON.stringify(req.body)}` + ); + const result = await service.updateChallenge(req, req.authUser, req.params.challengeId, req.body); + res.send(result); } /** - * Partially update challenge + * Update Legacy Payout (Updates informixoltp:payment_detail) + * This has no effect other than to keep DW in sync for looker with + * Updates that happen in Wallet + */ +async function updateLegacyPayout(req, res) { + logger.debug( + `updateLegacyPayout User: ${JSON.stringify(req.authUser)} - ChallengeID: ${ + req.params.challengeId + } - Body: ${JSON.stringify(req.body)}` + ); + const result = await service.updateLegacyPayout( + req, + req.authUser, + req.params.challengeId, + req.body + ); + res.send(result); +} + +/** + * Delete challenge * @param {Object} req the request * @param {Object} res the response */ -async function partiallyUpdateChallenge (req, res) { - logger.debug(`partiallyUpdateChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${req.params.challengeId} - Body: ${JSON.stringify(req.body)}`) - const result = await service.partiallyUpdateChallenge(req.authUser, req.params.challengeId, req.body) - res.send(result) +async function deleteChallenge(req, res) { + logger.debug( + `deleteChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${req.params.challengeId}` + ); + const result = await service.deleteChallenge(req, req.authUser, req.params.challengeId); + res.send(result); } /** - * Delete challenge + * Advance phase * @param {Object} req the request * @param {Object} res the response */ -async function deleteChallenge (req, res) { - logger.debug(`deleteChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${req.params.challengeId}`) - const result = await service.deleteChallenge(req.authUser, req.params.challengeId) - res.send(result) +async function advancePhase(req, res) { + res.send(await service.advancePhase(req, req.authUser, req.params.challengeId, req.body)); +} + +/** + * Get SRM Schedule + * @param {Object} req the request + * @param {Object} res the response + */ +async function getSRMSchedule(req, res) { + const result = await service.getSRMSchedule(req, req.query); + res.send(result); +} + +/** + * Get Practice Problems + * @param {Object} req the request + * @param {Object} res the response + */ +async function getPracticeProblems(req, res) { + const result = await service.getPracticeProblems(req, req.authUser, req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } module.exports = { searchChallenges, createChallenge, getChallenge, - fullyUpdateChallenge, - partiallyUpdateChallenge, + updateChallenge, + updateLegacyPayout, deleteChallenge, getChallengeStatistics, - sendNotifications -} + sendNotifications, + advancePhase, + getSRMSchedule, + getPracticeProblems, +}; diff --git a/src/controllers/ChallengePhaseController.js b/src/controllers/ChallengePhaseController.js index d2f68118..b68914be 100644 --- a/src/controllers/ChallengePhaseController.js +++ b/src/controllers/ChallengePhaseController.js @@ -1,19 +1,19 @@ /** * Controller for challenge phase endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/PhaseService') -const helper = require('../common/helper') +const HttpStatus = require("http-status-codes"); +const service = require("../services/PhaseService"); +const helper = require("../common/helper"); /** * Search phases * @param {Object} req the request * @param {Object} res the response */ -async function searchPhases (req, res) { - const result = await service.searchPhases(req.query) - helper.setResHeaders(req, res, result) - res.send(result.result) +async function searchPhases(req, res) { + const result = await service.searchPhases(req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } /** @@ -21,9 +21,9 @@ async function searchPhases (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function createPhase (req, res) { - const result = await service.createPhase(req.body) - res.status(HttpStatus.CREATED).send(result) +async function createPhase(req, res) { + const result = await service.createPhase(req.body); + res.status(HttpStatus.CREATED).send(result); } /** @@ -31,9 +31,9 @@ async function createPhase (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getPhase (req, res) { - const result = await service.getPhase(req.params.challengePhaseId) - res.send(result) +async function getPhase(req, res) { + const result = await service.getPhase(req.params.challengePhaseId); + res.send(result); } /** @@ -41,9 +41,9 @@ async function getPhase (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function fullyUpdatePhase (req, res) { - const result = await service.fullyUpdatePhase(req.params.challengePhaseId, req.body) - res.send(result) +async function fullyUpdatePhase(req, res) { + const result = await service.fullyUpdatePhase(req.params.challengePhaseId, req.body); + res.send(result); } /** @@ -51,9 +51,9 @@ async function fullyUpdatePhase (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function partiallyUpdatePhase (req, res) { - const result = await service.partiallyUpdatePhase(req.params.challengePhaseId, req.body) - res.send(result) +async function partiallyUpdatePhase(req, res) { + const result = await service.partiallyUpdatePhase(req.params.challengePhaseId, req.body); + res.send(result); } /** @@ -61,9 +61,9 @@ async function partiallyUpdatePhase (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function deletePhase (req, res) { - const result = await service.deletePhase(req.params.challengePhaseId) - res.send(result) +async function deletePhase(req, res) { + const result = await service.deletePhase(req.params.challengePhaseId); + res.send(result); } module.exports = { @@ -72,5 +72,5 @@ module.exports = { getPhase, fullyUpdatePhase, partiallyUpdatePhase, - deletePhase -} + deletePhase, +}; diff --git a/src/controllers/ChallengeTimelineTemplateController.js b/src/controllers/ChallengeTimelineTemplateController.js index 28eb54bb..ae69740e 100644 --- a/src/controllers/ChallengeTimelineTemplateController.js +++ b/src/controllers/ChallengeTimelineTemplateController.js @@ -1,19 +1,19 @@ /** * Controller for challenge type timeline templates endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/ChallengeTimelineTemplateService') -const helper = require('../common/helper') +const HttpStatus = require("http-status-codes"); +const service = require("../services/ChallengeTimelineTemplateService"); +const helper = require("../common/helper"); /** * Search challenge type timeline templates. * @param {Object} req the request * @param {Object} res the response */ -async function searchChallengeTimelineTemplates (req, res) { - const result = await service.searchChallengeTimelineTemplates(req.query) - helper.setResHeaders(req, res, result) - res.send(result.result) +async function searchChallengeTimelineTemplates(req, res) { + const result = await service.searchChallengeTimelineTemplates(req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } /** @@ -21,9 +21,9 @@ async function searchChallengeTimelineTemplates (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function createChallengeTimelineTemplate (req, res) { - const result = await service.createChallengeTimelineTemplate(req.body) - res.status(HttpStatus.CREATED).send(result) +async function createChallengeTimelineTemplate(req, res) { + const result = await service.createChallengeTimelineTemplate(req.body); + res.status(HttpStatus.CREATED).send(result); } /** @@ -31,9 +31,9 @@ async function createChallengeTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getChallengeTimelineTemplate (req, res) { - const result = await service.getChallengeTimelineTemplate(req.params.challengeTimelineTemplateId) - res.send(result) +async function getChallengeTimelineTemplate(req, res) { + const result = await service.getChallengeTimelineTemplate(req.params.challengeTimelineTemplateId); + res.send(result); } /** @@ -41,9 +41,12 @@ async function getChallengeTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function fullyUpdateChallengeTimelineTemplate (req, res) { - const result = await service.fullyUpdateChallengeTimelineTemplate(req.params.challengeTimelineTemplateId, req.body) - res.send(result) +async function fullyUpdateChallengeTimelineTemplate(req, res) { + const result = await service.fullyUpdateChallengeTimelineTemplate( + req.params.challengeTimelineTemplateId, + req.body + ); + res.send(result); } /** @@ -51,9 +54,11 @@ async function fullyUpdateChallengeTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function deleteChallengeTimelineTemplate (req, res) { - const result = await service.deleteChallengeTimelineTemplate(req.params.challengeTimelineTemplateId) - res.send(result) +async function deleteChallengeTimelineTemplate(req, res) { + const result = await service.deleteChallengeTimelineTemplate( + req.params.challengeTimelineTemplateId + ); + res.send(result); } module.exports = { @@ -61,5 +66,5 @@ module.exports = { createChallengeTimelineTemplate, getChallengeTimelineTemplate, fullyUpdateChallengeTimelineTemplate, - deleteChallengeTimelineTemplate -} + deleteChallengeTimelineTemplate, +}; diff --git a/src/controllers/ChallengeTrackController.js b/src/controllers/ChallengeTrackController.js index 6e795362..cac350f1 100644 --- a/src/controllers/ChallengeTrackController.js +++ b/src/controllers/ChallengeTrackController.js @@ -1,19 +1,19 @@ /** * Controller for challenge type endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/ChallengeTrackService') -const helper = require('../common/helper') +const HttpStatus = require("http-status-codes"); +const service = require("../services/ChallengeTrackService"); +const helper = require("../common/helper"); /** * Search challenge types * @param {Object} req the request * @param {Object} res the response */ -async function searchChallengeTracks (req, res) { - const result = await service.searchChallengeTracks(req.query) - helper.setResHeaders(req, res, result) - res.send(result.result) +async function searchChallengeTracks(req, res) { + const result = await service.searchChallengeTracks(req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } /** @@ -21,9 +21,9 @@ async function searchChallengeTracks (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function createChallengeTrack (req, res) { - const result = await service.createChallengeTrack(req.body) - res.status(HttpStatus.CREATED).send(result) +async function createChallengeTrack(req, res) { + const result = await service.createChallengeTrack(req.body); + res.status(HttpStatus.CREATED).send(result); } /** @@ -31,9 +31,9 @@ async function createChallengeTrack (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getChallengeTrack (req, res) { - const result = await service.getChallengeTrack(req.params.challengeTrackId) - res.send(result) +async function getChallengeTrack(req, res) { + const result = await service.getChallengeTrack(req.params.challengeTrackId); + res.send(result); } /** @@ -41,9 +41,9 @@ async function getChallengeTrack (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function fullyUpdateChallengeTrack (req, res) { - const result = await service.fullyUpdateChallengeTrack(req.params.challengeTrackId, req.body) - res.send(result) +async function fullyUpdateChallengeTrack(req, res) { + const result = await service.fullyUpdateChallengeTrack(req.params.challengeTrackId, req.body); + res.send(result); } /** @@ -51,9 +51,9 @@ async function fullyUpdateChallengeTrack (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function partiallyUpdateChallengeTrack (req, res) { - const result = await service.partiallyUpdateChallengeTrack(req.params.challengeTrackId, req.body) - res.send(result) +async function partiallyUpdateChallengeTrack(req, res) { + const result = await service.partiallyUpdateChallengeTrack(req.params.challengeTrackId, req.body); + res.send(result); } /** @@ -61,9 +61,9 @@ async function partiallyUpdateChallengeTrack (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function deleteChallengeTrack (req, res) { - const result = await service.deleteChallengeTrack(req.params.challengeTrackId) - res.send(result) +async function deleteChallengeTrack(req, res) { + const result = await service.deleteChallengeTrack(req.params.challengeTrackId); + res.send(result); } module.exports = { @@ -72,5 +72,5 @@ module.exports = { getChallengeTrack, fullyUpdateChallengeTrack, partiallyUpdateChallengeTrack, - deleteChallengeTrack -} + deleteChallengeTrack, +}; diff --git a/src/controllers/ChallengeTypeController.js b/src/controllers/ChallengeTypeController.js index 85d529f2..dd6b6fc2 100644 --- a/src/controllers/ChallengeTypeController.js +++ b/src/controllers/ChallengeTypeController.js @@ -1,19 +1,19 @@ /** * Controller for challenge type endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/ChallengeTypeService') -const helper = require('../common/helper') +const HttpStatus = require("http-status-codes"); +const service = require("../services/ChallengeTypeService"); +const helper = require("../common/helper"); /** * Search challenge types * @param {Object} req the request * @param {Object} res the response */ -async function searchChallengeTypes (req, res) { - const result = await service.searchChallengeTypes(req.query) - helper.setResHeaders(req, res, result) - res.send(result.result) +async function searchChallengeTypes(req, res) { + const result = await service.searchChallengeTypes(req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } /** @@ -21,9 +21,9 @@ async function searchChallengeTypes (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function createChallengeType (req, res) { - const result = await service.createChallengeType(req.body) - res.status(HttpStatus.CREATED).send(result) +async function createChallengeType(req, res) { + const result = await service.createChallengeType(req.body); + res.status(HttpStatus.CREATED).send(result); } /** @@ -31,9 +31,9 @@ async function createChallengeType (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getChallengeType (req, res) { - const result = await service.getChallengeType(req.params.challengeTypeId) - res.send(result) +async function getChallengeType(req, res) { + const result = await service.getChallengeType(req.params.challengeTypeId); + res.send(result); } /** @@ -41,9 +41,9 @@ async function getChallengeType (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function fullyUpdateChallengeType (req, res) { - const result = await service.fullyUpdateChallengeType(req.params.challengeTypeId, req.body) - res.send(result) +async function fullyUpdateChallengeType(req, res) { + const result = await service.fullyUpdateChallengeType(req.params.challengeTypeId, req.body); + res.send(result); } /** @@ -51,9 +51,9 @@ async function fullyUpdateChallengeType (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function partiallyUpdateChallengeType (req, res) { - const result = await service.partiallyUpdateChallengeType(req.params.challengeTypeId, req.body) - res.send(result) +async function partiallyUpdateChallengeType(req, res) { + const result = await service.partiallyUpdateChallengeType(req.params.challengeTypeId, req.body); + res.send(result); } /** @@ -61,9 +61,9 @@ async function partiallyUpdateChallengeType (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function deleteChallengeType (req, res) { - const result = await service.deleteChallengeType(req.params.challengeTypeId) - res.send(result) +async function deleteChallengeType(req, res) { + const result = await service.deleteChallengeType(req.params.challengeTypeId); + res.send(result); } module.exports = { @@ -72,5 +72,5 @@ module.exports = { getChallengeType, fullyUpdateChallengeType, partiallyUpdateChallengeType, - deleteChallengeType -} + deleteChallengeType, +}; diff --git a/src/controllers/HealthController.js b/src/controllers/HealthController.js index 6c7dafa4..3748b368 100644 --- a/src/controllers/HealthController.js +++ b/src/controllers/HealthController.js @@ -1,37 +1,37 @@ /** * Controller for health check endpoint */ -const config = require('config') -const service = require('../services/ChallengeTypeService') -const errors = require('../common/errors') +const config = require("config"); +const service = require("../services/ChallengeTypeService"); +const errors = require("../common/errors"); // the topcoder-healthcheck-dropin library returns checksRun count, // here it follows that to return such count -let checksRun = 0 +let checksRun = 0; /** * Check health of the app * @param {Object} req the request * @param {Object} res the response */ -async function checkHealth (req, res) { +async function checkHealth(req, res) { // perform a quick database access operation, if there is no error and is quick, then consider it healthy; // there are just a few challenge types, so search challenge types should be efficient operation, // and it just searches a single challenge type, it should be quick operation - checksRun += 1 - const timestampMS = new Date().getTime() + checksRun += 1; + const timestampMS = new Date().getTime(); try { - await service.searchChallengeTypes({ page: 1, perPage: 1 }) + await service.searchChallengeTypes({ page: 1, perPage: 1 }); } catch (e) { - throw new errors.ServiceUnavailableError(`There is database operation error, ${e.message}`) + throw new errors.ServiceUnavailableError(`There is database operation error, ${e.message}`); } if (new Date().getTime() - timestampMS > Number(config.HEALTH_CHECK_TIMEOUT)) { - throw new errors.ServiceUnavailableError('Database operation is slow.') + throw new errors.ServiceUnavailableError("Database operation is slow."); } // there is no error, and it is quick, then return checks run count - res.send({ checksRun }) + res.send({ checksRun }); } module.exports = { - checkHealth -} + checkHealth, +}; diff --git a/src/controllers/SupportController.js b/src/controllers/SupportController.js index 07aa69fb..f1ade8c4 100644 --- a/src/controllers/SupportController.js +++ b/src/controllers/SupportController.js @@ -1,21 +1,23 @@ /** * Controller for support endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/SupportService') -const logger = require('../common/logger') +const HttpStatus = require("http-status-codes"); +const service = require("../services/SupportService"); +const logger = require("../common/logger"); /** * Create challenge * @param {Object} req the request * @param {Object} res the response */ -async function createRequest (req, res) { - logger.debug(`createRequest User: ${JSON.stringify(req.authUser)} - Body: ${JSON.stringify(req.body)}`) - const result = await service.createRequest(req.authUser, req.body) - res.status(HttpStatus.CREATED).send(result) +async function createRequest(req, res) { + logger.debug( + `createRequest User: ${JSON.stringify(req.authUser)} - Body: ${JSON.stringify(req.body)}` + ); + const result = await service.createRequest(req.authUser, req.body); + res.status(HttpStatus.CREATED).send(result); } module.exports = { - createRequest -} + createRequest, +}; diff --git a/src/controllers/TimelineTemplateController.js b/src/controllers/TimelineTemplateController.js index cc77c918..8dcbd0f4 100644 --- a/src/controllers/TimelineTemplateController.js +++ b/src/controllers/TimelineTemplateController.js @@ -1,19 +1,19 @@ /** * Controller for challenge phase endpoints */ -const HttpStatus = require('http-status-codes') -const service = require('../services/TimelineTemplateService') -const helper = require('../common/helper') +const HttpStatus = require("http-status-codes"); +const service = require("../services/TimelineTemplateService"); +const helper = require("../common/helper"); /** * Search timeline templates. * @param {Object} req the request * @param {Object} res the response */ -async function searchTimelineTemplates (req, res) { - const result = await service.searchTimelineTemplates(req.query) - helper.setResHeaders(req, res, result) - res.send(result.result) +async function searchTimelineTemplates(req, res) { + const result = await service.searchTimelineTemplates(req.query); + helper.setResHeaders(req, res, result); + res.send(result.result); } /** @@ -21,9 +21,9 @@ async function searchTimelineTemplates (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function createTimelineTemplate (req, res) { - const result = await service.createTimelineTemplate(req.body) - res.status(HttpStatus.CREATED).send(result) +async function createTimelineTemplate(req, res) { + const result = await service.createTimelineTemplate(req.body); + res.status(HttpStatus.CREATED).send(result); } /** @@ -31,9 +31,9 @@ async function createTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function getTimelineTemplate (req, res) { - const result = await service.getTimelineTemplate(req.params.timelineTemplateId) - res.send(result) +async function getTimelineTemplate(req, res) { + const result = await service.getTimelineTemplate(req.params.timelineTemplateId); + res.send(result); } /** @@ -41,9 +41,9 @@ async function getTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function fullyUpdateTimelineTemplate (req, res) { - const result = await service.fullyUpdateTimelineTemplate(req.params.timelineTemplateId, req.body) - res.send(result) +async function fullyUpdateTimelineTemplate(req, res) { + const result = await service.fullyUpdateTimelineTemplate(req.params.timelineTemplateId, req.body); + res.send(result); } /** @@ -51,9 +51,12 @@ async function fullyUpdateTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function partiallyUpdateTimelineTemplate (req, res) { - const result = await service.partiallyUpdateTimelineTemplate(req.params.timelineTemplateId, req.body) - res.send(result) +async function partiallyUpdateTimelineTemplate(req, res) { + const result = await service.partiallyUpdateTimelineTemplate( + req.params.timelineTemplateId, + req.body + ); + res.send(result); } /** @@ -61,9 +64,9 @@ async function partiallyUpdateTimelineTemplate (req, res) { * @param {Object} req the request * @param {Object} res the response */ -async function deleteTimelineTemplate (req, res) { - const result = await service.deleteTimelineTemplate(req.params.timelineTemplateId) - res.send(result) +async function deleteTimelineTemplate(req, res) { + const result = await service.deleteTimelineTemplate(req.params.timelineTemplateId); + res.send(result); } module.exports = { @@ -72,5 +75,5 @@ module.exports = { getTimelineTemplate, fullyUpdateTimelineTemplate, partiallyUpdateTimelineTemplate, - deleteTimelineTemplate -} + deleteTimelineTemplate, +}; diff --git a/src/init-es.js b/src/init-es.js index 1b5f8a40..e0f055d9 100644 --- a/src/init-es.js +++ b/src/init-es.js @@ -6,103 +6,111 @@ * node src/init-es * node src/init-es force */ -const config = require('config') -const logger = require('./common/logger') -const helper = require('./common/helper') +const config = require("config"); +const logger = require("./common/logger"); +const helper = require("./common/helper"); -const client = helper.getESClient() +const client = helper.getESClient(); const initES = async () => { - if (process.argv.length === 3 && process.argv[2] === 'force') { - logger.info(`Delete index ${config.ES.ES_INDEX} if any.`) + if (process.argv.length === 3 && process.argv[2] === "force") { + logger.info(`Delete index ${config.ES.ES_INDEX} if any.`); try { - await client.indices.delete({ index: config.ES.ES_INDEX }) + await client.indices.delete({ index: config.ES.ES_INDEX }); } catch (err) { // ignore } } - const exists = await client.indices.exists({ index: config.ES.ES_INDEX }) + const { body: exists } = await client.indices.exists({ index: config.ES.ES_INDEX }); if (exists) { - logger.info(`The index ${config.ES.ES_INDEX} exists.`) + logger.info(`The index ${config.ES.ES_INDEX} exists.`); } else { - logger.info(`The index ${config.ES.ES_INDEX} will be created.`) + logger.info(`The index ${config.ES.ES_INDEX} will be created.`); - const body = { mappings: {} } - body.mappings[config.get('ES.ES_TYPE')] = { - properties: { - id: { type: 'keyword' }, - name: { - type: 'keyword', - fields: { - text: { - type: 'text' - } + const body = { + mappings: { + // properties: { + properties: { + id: { type: "keyword" }, + name: { + type: "keyword", + fields: { + text: { + type: "text", + }, + }, + normalizer: "custom_sort_normalizer", }, - normalizer: 'custom_sort_normalizer' - }, - status: { - type: 'keyword', - fields: { - text: { - type: 'text' - } + status: { + type: "keyword", + fields: { + text: { + type: "text", + }, + }, + normalizer: "custom_sort_normalizer", }, - normalizer: 'custom_sort_normalizer' - }, - type: { - type: 'keyword', - fields: { - text: { - type: 'text' - } + type: { + type: "keyword", + fields: { + text: { + type: "text", + }, + }, + normalizer: "custom_sort_normalizer", + }, + prizeSets: { + properties: { + type: { type: "text" }, + prizes: { + properties: { + type: { type: "text" }, + value: { type: "float" }, + }, + }, + }, }, - normalizer: 'custom_sort_normalizer' }, - prizeSets: { - properties: { - type: { type: 'text' }, - prizes: { - properties: { - type: { type: 'text' }, - value: { type: 'float' } - } - } - } - } + dynamic_templates: [ + { + metadata: { + path_match: "metadata.*", + mapping: { + type: "text", + }, + }, + }, + ], + //} }, - dynamic_templates: [{ - metadata: { - path_match: 'metadata.*', - mapping: { - type: 'text' - } - } - }] - } + }; + body.settings = { analysis: { normalizer: { custom_sort_normalizer: { - type: 'custom', + type: "custom", char_filter: [], - filter: ['lowercase', 'asciifolding'] - } - } - } - } + filter: ["lowercase", "asciifolding"], + }, + }, + }, + }; await client.indices.create({ index: config.ES.ES_INDEX, - body - }) + body, + }); } -} +}; -initES().then(() => { - logger.info('Done!') - process.exit() -}).catch((e) => { - logger.logFullError(e) - process.exit() -}) +initES() + .then(() => { + logger.info("Done!"); + process.exit(); + }) + .catch((e) => { + logger.logFullError(e); + process.exit(); + }); diff --git a/src/models/Attachment.js b/src/models/Attachment.js deleted file mode 100644 index 0eee2a87..00000000 --- a/src/models/Attachment.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * This defines Attachment model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - url: { - type: String, - required: true - }, - fileSize: { - type: Number, - required: true - }, - name: { - type: String, - required: true - }, - challengeId: { - type: String, - required: true - }, - description: { - type: String - } -}, -{ - throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/AuditLog.js b/src/models/AuditLog.js deleted file mode 100644 index 3158ac32..00000000 --- a/src/models/AuditLog.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * This defines AuditLog model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - challengeId: { - type: String, - required: true - }, - fieldName: { - type: String, - required: true - }, - oldValue: { - type: String, - required: true - }, - newValue: { - type: String, - required: true - }, - created: { - type: Date, - required: true - }, - createdBy: { - type: String, - required: true - }, - memberId: { - type: String - } -}, -{ - throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/Challenge.js b/src/models/Challenge.js deleted file mode 100644 index 5aec842f..00000000 --- a/src/models/Challenge.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * This defines Challenge model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - legacyId: { - type: Number, - required: false - }, - typeId: { - type: String, - required: true - }, - trackId: { - type: String, - required: true - }, - legacy: { - type: Object, - required: false - }, - billing: { - type: Object, - required: false - }, - name: { - type: String, - required: true - }, - description: { - type: String, - required: false - }, - privateDescription: { - type: String, - required: false - }, - descriptionFormat: { - type: String, - default: 'HTML', - required: true - }, - metadata: { - type: [Object], - required: false - }, - task: { - type: Object, - required: false - }, - timelineTemplateId: { - type: String, - required: false - }, - phases: { - type: Array, - required: false - }, - events: { - type: [Object], - required: false - }, - terms: { - type: Array, - required: false - }, - prizeSets: { - type: [Object], - required: false - }, - // tag names - tags: { - type: Array, - required: false - }, - projectId: { - type: Number, - required: false - }, - startDate: { - type: Date, - required: false - }, - endDate: { - type: Date, - required: false - }, - status: { - type: String, - required: true - }, - attachments: { - type: Array, - required: false - }, - // group names - groups: { - type: Array, - required: false - }, - // winners - winners: { - type: Array, - required: false - }, - discussions: { - type: [Object], - required: false - }, - overview: { - type: Object - }, - created: { - type: Date, - required: true - }, - createdBy: { - type: String, - required: true - }, - updated: { - type: Date, - required: false - }, - updatedBy: { - type: String, - required: false - } -}, -{ - // throughput: { read: 4, write: 2 }, - throughput: 'ON_DEMAND', - useDocumentTypes: true -} -) - -module.exports = schema diff --git a/src/models/ChallengeTimelineTemplate.js b/src/models/ChallengeTimelineTemplate.js deleted file mode 100644 index 881e521e..00000000 --- a/src/models/ChallengeTimelineTemplate.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This defines ChallengeType-TimelineTemplate mapping model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - trackId: { - type: String, - required: true - }, - typeId: { - type: String, - required: true - }, - timelineTemplateId: { - type: String, - required: true - }, - isDefault: { - type: Boolean, - required: false - } -}, -{ - throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/ChallengeTrack.js b/src/models/ChallengeTrack.js deleted file mode 100644 index 908e029e..00000000 --- a/src/models/ChallengeTrack.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This defines ChallengeTrack model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - name: { - type: String, - required: true - }, - description: { - type: String, - required: false - }, - isActive: { - type: Boolean, - required: true - }, - abbreviation: { - type: String, - required: true - } -}, -{ - throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/ChallengeType.js b/src/models/ChallengeType.js deleted file mode 100644 index d96e022a..00000000 --- a/src/models/ChallengeType.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * This defines ChallengeType model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - name: { - type: String, - required: true - }, - description: { - type: String, - required: false - }, - isActive: { - type: Boolean, - required: true - }, - isTask: { - type: Boolean, - default: false - }, - abbreviation: { - type: String, - required: true - } -}, -{ - throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/Phase.js b/src/models/Phase.js deleted file mode 100644 index 4fdf56ad..00000000 --- a/src/models/Phase.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * This defines Phase model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - name: { - type: String, - required: true - }, - description: { - type: String, - required: false - }, - isOpen: { - type: Boolean, - required: true - }, - duration: { - type: Number, - required: true - } -}, -{ - throughput: 'ON_DEMAND' - // throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/TimelineTemplate.js b/src/models/TimelineTemplate.js deleted file mode 100644 index fe40c605..00000000 --- a/src/models/TimelineTemplate.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This defines TimelineTemplate model. - */ - -const dynamoose = require('dynamoose') - -const Schema = dynamoose.Schema - -const schema = new Schema({ - id: { - type: String, - hashKey: true, - required: true - }, - name: { - type: String, - required: true - }, - description: { - type: String, - required: false - }, - isActive: { - type: Boolean, - required: true - }, - phases: { - type: Array, - required: true - } -}, -{ - throughput: { read: 4, write: 2 } -}) - -module.exports = schema diff --git a/src/models/index.js b/src/models/index.js deleted file mode 100644 index 8a006613..00000000 --- a/src/models/index.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Initialize and export all model schemas. - */ - -const config = require('config') -const dynamoose = require('dynamoose') - -const awsConfigs = config.AMAZON.IS_LOCAL_DB ? { - accessKeyId: config.AMAZON.AWS_ACCESS_KEY_ID, - secretAccessKey: config.AMAZON.AWS_SECRET_ACCESS_KEY, - region: config.AMAZON.AWS_REGION -} : { - region: config.AMAZON.AWS_REGION -} - -dynamoose.AWS.config.update(awsConfigs) - -if (config.AMAZON.IS_LOCAL_DB) { - dynamoose.local(config.AMAZON.DYNAMODB_URL) -} - -// console.log(config.AMAZON.IS_LOCAL_DB, config.AMAZON.AWS_ACCESS_KEY_ID, config.AMAZON.AWS_SECRET_ACCESS_KEY) -// console.log(JSON.stringify(dynamoose.AWS.config)) - -dynamoose.setDefaults({ - create: false, - update: false, - waitForActive: false -}) - -module.exports = { - Challenge: dynamoose.model('Challenge', require('./Challenge')), - ChallengeType: dynamoose.model('ChallengeType', require('./ChallengeType')), - ChallengeTrack: dynamoose.model('ChallengeTrack', require('./ChallengeTrack')), - ChallengeTimelineTemplate: dynamoose.model('ChallengeTimelineTemplate', require('./ChallengeTimelineTemplate')), - AuditLog: dynamoose.model('AuditLog', require('./AuditLog')), - Phase: dynamoose.model('Phase', require('./Phase')), - TimelineTemplate: dynamoose.model('TimelineTemplate', require('./TimelineTemplate')), - Attachment: dynamoose.model('Attachment', require('./Attachment')) -} diff --git a/src/phase-management/PhaseAdvancer.js b/src/phase-management/PhaseAdvancer.js new file mode 100644 index 00000000..3a50ba66 --- /dev/null +++ b/src/phase-management/PhaseAdvancer.js @@ -0,0 +1,422 @@ +const config = require("config"); +const uuid = require("uuid/v4"); +const { Engine } = require("json-rules-engine"); + +const rulesJSON = require("./phase-rules.json"); +const errors = require("../common/errors"); + +const helper = require("../common/helper"); +const { PhaseFact } = require("@topcoder-framework/lib-common"); + +// Helper functions + +// TODO: Move these to a common place + +const normalizeName = (name) => name.replace(/ /g, ""); + +const shouldCheckConstraint = (operation, phase, constraintName, rules) => { + const normalizedConstraintName = normalizeName(constraintName); + return ( + operation === "close" && + phase.constraints && + rules.constraintRules[normalizeName(phase.name)]?.includes(normalizedConstraintName) + ); +}; + +const parseDate = (dateString) => { + const date = new Date(dateString).getTime(); + return isNaN(date) ? null : date; +}; + +// End of helper functions + +class PhaseAdvancer { + #rules = rulesJSON; + #challengeDomain; + + constructor(challengeDomain) { + this.#challengeDomain = challengeDomain; + } + + #factGenerators = { + Registration: async (challengeId, phaseSpecificFacts) => ({ + registrantCount: await this.#getRegistrantCount(challengeId), + }), + Submission: async (challengeId, phaseSpecificFacts) => ({ + submissionCount: await this.#getSubmissionCount(challengeId), + }), + Review: async (challengeId, phaseSpecificFacts) => ({ + allSubmissionsReviewed: await this.#areAllSubmissionsReviewed(challengeId), + }), + IterativeReview: async (challengeId, phaseSpecificFacts, phases) => ({ + hasActiveUnreviewedSubmissions: await this.#hasActiveUnreviewedSubmissions( + challengeId, + phaseSpecificFacts, + phases + ), + wasSubmissionReviewedInCurrentOpenIterativeReviewPhase: + await this.#wasSubmissionReviewedInCurrentOpenIterativeReviewPhase( + challengeId, + phaseSpecificFacts + ), + hasWinningSubmission: await this.#hasWinningSubmission(phaseSpecificFacts), + insertIterativeReviewPhaseOnCloseWithoutWinningSubmission: true, + shouldOpenNextIterativeReviewPhase: await this.#shouldOpenNextIterativeReviewPhase( + phaseSpecificFacts + ), + }), + AppealsResponse: async (challengeId) => ({ + allAppealsResolved: await this.#areAllAppealsResolved(challengeId), + }), + }; + + async #generateFacts(challengeId, legacyId, phases, phase, operation) { + const phaseSpecificFactRequest = { + legacyId, + facts: this.#getPhaseFactName(phase.name), + }; + + const phaseSpecificFacts = await this.#challengeDomain.getPhaseFacts(phaseSpecificFactRequest); + const facts = { + name: phase.name, + isOpen: phase.isOpen, + isClosed: !phase.isOpen && phase.actualEndDate != null, + isPastScheduledStartTime: this.#isPastTime(phase.scheduledStartDate), + isPastScheduledEndTime: this.#isPastTime(phase.scheduledEndDate), + isPostMortemOpen: phases.find((p) => p.name === "Post-Mortem")?.isOpen, + hasPredecessor: phase.predecessorId != null, + isPredecessorPhaseClosed: + phase.predecessorId != null + ? this.#isPastTime(phases.find((p) => p.phaseId === phase.predecessorId)?.actualEndDate) + : true, + nextPhase: phases.find((p) => p.predecessor === phase.phaseId)?.name, + }; + + if (this.#factGenerators[normalizeName(phase.name)]) { + const additionalFacts = await this.#factGenerators[normalizeName(phase.name)]( + challengeId, + phaseSpecificFacts, + phases + ); + Object.assign(facts, additionalFacts); + } + + return facts; + } + + async advancePhase(challengeId, legacyId, phases, operation, phaseName) { + phaseName = phaseName.replace(/-/g, " "); + + const matchedPhases = phases + .filter((phase) => phase.actualEndDate == null && phase.name === phaseName) + .sort((a, b) => new Date(a.scheduledStartDate) - new Date(b.scheduledStartDate)); + + if (matchedPhases.length === 0) { + throw new errors.BadRequestError(`Phase ${phaseName} not found or already closed`); + } + + const phase = matchedPhases[0]; // We only advance the earliest phase + + const essentialRules = this.#rules[`${operation}Rules`][normalizeName(phase.name)] + ? this.#rules[`${operation}Rules`][normalizeName(phase.name)].map((rule) => ({ + name: rule.name, + conditions: rule.conditions, + event: rule.event, + })) + : []; + + const constraintRules = + phase.constraints + ?.filter((constraint) => + shouldCheckConstraint(operation, phase, constraint.name, this.#rules) + ) + .map((constraint) => ({ + name: `Constraint: ${constraint.name}`, + conditions: { + all: [ + { + fact: this.#rules.constraintNameFactMap[normalizeName(constraint.name)], + operator: "greaterThanInclusive", + value: constraint.value, + }, + ], + }, + event: { + type: `can${operation.toLowerCase()}`, + }, + })) || []; + + const rules = [...essentialRules, ...constraintRules]; + const facts = await this.#generateFacts(challengeId, legacyId, phases, phase, operation); + + for (const rule of rules) { + const ruleExecutionResult = await this.#executeRule(rule, facts); + + if (!ruleExecutionResult.success) { + return { + success: false, + message: `Cannot ${operation} phase ${phase.name} for challenge ${challengeId}`, + detail: `Rule ${rule.name} failed`, + failureReasons: ruleExecutionResult.failureReasons, + }; + } + } + + let next = []; + + if (operation === "open") { + await this.#open(challengeId, phases, phase); + } else if (operation === "close") { + await this.#close(challengeId, phases, phase); + + if (facts.hasWinningSubmission) { + console.log("Challenge has a winning submission. Close the challenge."); + } else { + console.log("Checking if inserting a phase is required"); + const insertedPhase = this.#insertPhaseIfRequired(phases, phase, facts); + + if (insertedPhase) { + next.push(insertedPhase); + } + } + if (next.length == 0) { + next = phases.filter((p) => p.predecessor === phase.phaseId); + } + } + + return { + success: true, + hasWinningSubmission: facts.hasWinningSubmission, + message: `Successfully ${operation}d phase ${phase.name} for challenge ${challengeId}`, + updatedPhases: phases, + next: { + operation: operation === "close" && next.length > 0 ? "open" : undefined, + phases: next, + }, + }; + } + + async #open(challengeId, phases, phase) { + phase.isOpen = true; + const actualStartDate = new Date(); + phase.actualStartDate = actualStartDate.toISOString(); + phase.scheduledEndDate = new Date( + actualStartDate.getTime() + phase.duration * 1000 + ).toISOString(); + + const scheduledStartDate = parseDate(phase.scheduledStartDate); + const delta = scheduledStartDate - actualStartDate; // in milliseconds + + if (delta !== 0) { + console.log("Updating subsequent phases"); + this.#updateSubsequentPhases(phases, phase, -delta); + } + + console.log(`Updated phases: ${JSON.stringify(phases)}`); + } + + async #close(challengeId, phases, phase) { + console.log(`Closing phase ${phase.name} for challenge ${challengeId}`); + + phase.isOpen = false; + const actualEndDate = new Date(); + phase.actualEndDate = actualEndDate.toISOString(); + + const scheduledEndDate = parseDate(phase.scheduledEndDate); + const delta = scheduledEndDate - actualEndDate; + + if (delta !== 0) { + this.#updateSubsequentPhases(phases, phase, -delta); + } + + console.log(`Updated phases: ${JSON.stringify(phases)}`); + } + + #insertPhaseIfRequired(phases, phase, facts) { + // Check if inserting Iterative Review phase is required + if ( + !facts.hasWinningSubmission && + facts.insertIterativeReviewPhaseOnCloseWithoutWinningSubmission + ) { + const phaseToAdd = { + id: uuid(), + phaseId: phase.phaseId, + name: phase.name, + duration: 24 * 60 * 60, // 24 hours + scheduledStartDate: phase.actualEndDate, + scheduledEndDate: new Date( + parseDate(phase.actualEndDate) + phase.duration * 1000 + ).toISOString(), + actualStartDate: + facts.shouldOpenNextIterativeReviewPhase == true ? phase.actualEndDate : undefined, + actualEndDate: undefined, + isOpen: facts.shouldOpenNextIterativeReviewPhase == true, + constraints: phase.constraints, + description: phase.description, + predecessor: phase.id, + }; + phases.push(phaseToAdd); + + return phaseToAdd; + } + return null; + } + + #isPastTime(dateString) { + if (dateString == null) { + return false; + } + const date = new Date(dateString); + const now = new Date(); + return date <= now; + } + + async #getRegistrantCount(challengeId) { + console.log(`Getting registrant count for challenge ${challengeId}`); + // TODO: getChallengeResources loops through all pages, which is not necessary here, we can just get the first page and total count from header[X-Total] + const submitters = await helper.getChallengeResourcesCount( + challengeId, + config.SUBMITTER_ROLE_ID + ); + return submitters[config.SUBMITTER_ROLE_ID] || 0; + } + + async #getSubmissionCount(challengeId) { + console.log(`Getting submission count for challenge ${challengeId}`); + // TODO: getChallengeSubmissions loops through all pages, which is not necessary here, we can just get the first page and total count from header[X-Total] + const submissions = await helper.getChallengeSubmissionsCount(challengeId); + return submissions["Contest Submission"] || 0; + } + + async #areAllSubmissionsReviewed(challengeId) { + console.log(`Getting review count for challenge ${challengeId}`); + return Promise.resolve(true); + } + + async #areAllAppealsResolved(challengeId) { + console.log(`Checking if all appeals are resolved for challenge ${challengeId}`); + return Promise.resolve(false); + } + + async #wasSubmissionReviewedInCurrentOpenIterativeReviewPhase(challengeId, phaseSpecificFacts) { + const { factResponses } = phaseSpecificFacts; + if (!factResponses || factResponses.length === 0) { + return false; + } + const [ + { response: { wasSubmissionReviewedInCurrentOpenIterativeReviewPhase = false } } = { + response: { wasSubmissionReviewedInCurrentOpenIterativeReviewPhase: false }, + }, + ] = factResponses; + + return wasSubmissionReviewedInCurrentOpenIterativeReviewPhase; + } + + async #shouldOpenNextIterativeReviewPhase(phaseSpecificFacts) { + const { factResponses } = phaseSpecificFacts; + if (!factResponses || factResponses.length === 0) { + return false; + } + const [ + { response: { submissionCount = 0, reviewCount = 0 } } = { + response: { submissionCount: 0, reviewCount: 0 }, + }, + ] = factResponses; + + return submissionCount > reviewCount + 1; // are there pending reviews after the current one? + } + + async #hasActiveUnreviewedSubmissions(challengeId, phaseSpecificFacts, phases) { + console.log( + `Checking if there are active unreviewed submissions for challenge ${challengeId} using phaseSpecificFacts: ${JSON.stringify( + phaseSpecificFacts + )}` + ); + + if (phaseSpecificFacts.factResponses.length > 0) { + const [ + { response: { submissionCount = 0, reviewCount = 0 } } = { + response: { submissionCount: 0, reviewCount: 0 }, + }, + ] = phaseSpecificFacts.factResponses; + + const numClosedIterativeReviewPhases = phases.filter( + (phase) => phase.phaseType === "Iterative Review" && !phase.isOpen + ).length; + + console.log( + `numClosedIterativeReviewPhases: ${numClosedIterativeReviewPhases}; submissionCount: ${submissionCount}; reviewCount: ${reviewCount}` + ); + + return submissionCount > reviewCount + numClosedIterativeReviewPhases; + } + return Promise.resolve(false); + } + + async #hasWinningSubmission(phaseSpecificFacts) { + const { factResponses } = phaseSpecificFacts; + if (!factResponses || factResponses.length === 0) { + return false; + } + const [ + { response: { hasWinningSubmission = false } } = { + response: { hasWinningSubmission: false }, + }, + ] = factResponses; + + return hasWinningSubmission; + } + + async #executeRule(rule, facts) { + const ruleEngine = new Engine(); + ruleEngine.addRule(rule); + const result = await ruleEngine.run(facts); + + const failureReasons = result.failureResults.map((failureResult) => ({ + rule: rule.name, + failedConditions: failureResult.conditions.all + .filter((condition) => !condition.result) + .map((condition) => ({ + fact: condition.fact, + operator: condition.operator, + value: condition.value, + })), + })); + + return { + success: result.events.length > 0, + failureReasons, + }; + } + + // prettier-ignore + #updateSubsequentPhases(phases, currentPhase, delta) { + let nextPhase = phases.find((phase) => phase.predecessor === currentPhase.phaseId); + console.log('next-phase'); + + while (nextPhase) { + nextPhase.scheduledStartDate = new Date(new Date(nextPhase.scheduledStartDate).getTime() + delta).toISOString(); + nextPhase.scheduledEndDate = new Date(new Date(nextPhase.scheduledEndDate).getTime() + delta).toISOString(); + nextPhase = phases.find((phase) => phase.predecessor === nextPhase.phaseId); + } + } + + #getPhaseFactName(phaseName) { + if (phaseName === "Submission") { + return [PhaseFact.PHASE_FACT_SUBMISSION]; + } + if (phaseName === "Review") { + return [PhaseFact.PHASE_FACT_REVIEW]; + } + if (phaseName === "Iterative Review") { + return [PhaseFact.PHASE_FACT_ITERATIVE_REVIEW]; + } + if (phaseName === "Appeals") { + return [PhaseFact.PHASE_FACT_APPEALS]; + } + + return []; + } +} + +module.exports = PhaseAdvancer; diff --git a/src/phase-management/mocks/develop-close.json b/src/phase-management/mocks/develop-close.json new file mode 100644 index 00000000..c6359976 --- /dev/null +++ b/src/phase-management/mocks/develop-close.json @@ -0,0 +1,80 @@ +[ + { + "duration": 1800, + "scheduledEndDate": "2023-05-14T22:05:00.000Z", + "isOpen": false, + "name": "Registration", + "phaseId": "a93544bc-c165-4af4-b55e-18f3593b457a", + "description": "Registration Phase", + "id": "c15fb03f-9fc0-40b2-ad1a-f88583f7b5bf", + "constraints": [ + { + "name": "Number of Registrants", + "value": 1 + } + ], + "scheduledStartDate": "2023-05-14T21:35:00.000Z" + }, + { + "duration": 60, + "scheduledEndDate": "2023-05-14T21:48:29.949Z", + "isOpen": true, + "name": "Submission", + "phaseId": "6950164f-3c5e-4bdc-abc8-22aaf5a1bd49", + "description": "Submission Phase", + "id": "5c3ddfaa-9bac-4efc-8382-78bf43da8235", + "constraints": [], + "scheduledStartDate": "2023-05-14T21:30:00.000Z", + "actualStartDate": "2023-05-14T21:47:29.949Z" + }, + { + "duration": 172800, + "scheduledEndDate": "2023-05-16T22:25:29.949Z", + "isOpen": false, + "name": "Review", + "phaseId": "aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b", + "description": "Review Phase", + "id": "b677110f-83ab-4d9c-8fb5-fa32919fbb7f", + "predecessor": "6950164f-3c5e-4bdc-abc8-22aaf5a1bd49", + "constraints": [ + { + "name": "Scorecard", + "value": 30002133 + }, + { + "name": "Number of Reviewers", + "value": 2 + } + ], + "scheduledStartDate": "2023-05-14T22:25:29.949Z" + }, + { + "duration": 86400, + "scheduledEndDate": "2023-05-17T22:25:29.949Z", + "isOpen": false, + "name": "Appeals", + "phaseId": "1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6", + "description": "Appeals Phase", + "id": "fb24215a-4e6e-4ac7-9cb8-e985fed5e37e", + "predecessor": "aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b", + "constraints": [ + { + "name": "View Response During Appeals", + "value": 0 + } + ], + "scheduledStartDate": "2023-05-16T22:25:29.949Z" + }, + { + "duration": 43200, + "scheduledEndDate": "2023-05-18T10:25:29.949Z", + "isOpen": false, + "name": "Appeals Response", + "phaseId": "797a6af7-cd3f-4436-9fca-9679f773bee9", + "description": "Appeals Response Phase", + "id": "3de1ff86-fa7d-4a72-b456-530da0b98e6c", + "predecessor": "1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6", + "constraints": [], + "scheduledStartDate": "2023-05-17T22:25:29.949Z" + } +] diff --git a/src/phase-management/mocks/develop.json b/src/phase-management/mocks/develop.json new file mode 100644 index 00000000..f9cef2a8 --- /dev/null +++ b/src/phase-management/mocks/develop.json @@ -0,0 +1,79 @@ +[ + { + "duration": 1800, + "scheduledEndDate": "2023-05-14T22:05:00.000Z", + "isOpen": false, + "name": "Registration", + "phaseId": "a93544bc-c165-4af4-b55e-18f3593b457a", + "description": "Registration Phase", + "id": "c15fb03f-9fc0-40b2-ad1a-f88583f7b5bf", + "constraints": [ + { + "name": "Number of Registrants", + "value": 1 + } + ], + "scheduledStartDate": "2023-05-14T21:35:00.000Z" + }, + { + "duration": 1800, + "scheduledEndDate": "2023-05-14T22:08:00.000Z", + "isOpen": false, + "name": "Submission", + "phaseId": "6950164f-3c5e-4bdc-abc8-22aaf5a1bd49", + "description": "Submission Phase", + "id": "5c3ddfaa-9bac-4efc-8382-78bf43da8235", + "constraints": [], + "scheduledStartDate": "2023-05-14T21:30:00.000Z" + }, + { + "duration": 172800, + "scheduledEndDate": "2023-05-16T22:08:00.000Z", + "isOpen": false, + "name": "Review", + "phaseId": "aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b", + "description": "Review Phase", + "id": "b677110f-83ab-4d9c-8fb5-fa32919fbb7f", + "predecessor": "6950164f-3c5e-4bdc-abc8-22aaf5a1bd49", + "constraints": [ + { + "name": "Scorecard", + "value": 30002133 + }, + { + "name": "Number of Reviewers", + "value": 2 + } + ], + "scheduledStartDate": "2023-05-14T22:08:00.000Z" + }, + { + "duration": 86400, + "scheduledEndDate": "2023-05-17T22:08:00.000Z", + "isOpen": false, + "name": "Appeals", + "phaseId": "1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6", + "description": "Appeals Phase", + "id": "fb24215a-4e6e-4ac7-9cb8-e985fed5e37e", + "predecessor": "aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b", + "constraints": [ + { + "name": "View Response During Appeals", + "value": 0 + } + ], + "scheduledStartDate": "2023-05-16T22:08:00.000Z" + }, + { + "duration": 43200, + "scheduledEndDate": "2023-05-18T10:08:00.000Z", + "isOpen": false, + "name": "Appeals Response", + "phaseId": "797a6af7-cd3f-4436-9fca-9679f773bee9", + "description": "Appeals Response Phase", + "id": "3de1ff86-fa7d-4a72-b456-530da0b98e6c", + "predecessor": "1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6", + "constraints": [], + "scheduledStartDate": "2023-05-17T22:08:00.000Z" + } +] diff --git a/src/phase-management/mocks/f2f.json b/src/phase-management/mocks/f2f.json new file mode 100644 index 00000000..6ebb1271 --- /dev/null +++ b/src/phase-management/mocks/f2f.json @@ -0,0 +1,50 @@ +[ + { + "duration": 600, + "scheduledStartDate": "2023-05-13T18:43:00.000Z", + "scheduledEndDate": "2023-05-13T18:53:00.000Z", + "name": "Registration", + "phaseId": "a93544bc-c165-4af4-b55e-18f3593b457a", + "id": "1d5b61ad-fb3a-4395-b31c-45d97f29818f", + "isOpen": false, + "constraints": [ + { + "name": "Number of Registrants", + "value": 1 + } + ], + "description": "Registration Phase" + }, + { + "duration": 720, + "scheduledStartDate": "2023-05-13T18:45:00.000Z", + "scheduledEndDate": "2023-05-13T18:57:00.000Z", + "name": "Submission", + "phaseId": "6950164f-3c5e-4bdc-abc8-22aaf5a1bd49", + "id": "23a3fec2-8ae0-4003-85e7-1ff9183a502f", + "isOpen": false, + "constraints": [], + "description": "Submission Phase" + }, + { + "duration": 86400, + "scheduledStartDate": "2023-05-13T18:45:00.000Z", + "scheduledEndDate": "2023-05-14T18:45:00.000Z", + "name": "Iterative Review", + "phaseId": "003a4b14-de5d-43fc-9e35-835dbeb6af1f", + "id": "7fe16e5f-f780-4c60-898d-1437989e3344", + "isOpen": false, + "constraints": [ + { + "name": "Scorecard", + "value": 30001551 + }, + { + "name": "Number of Reviewers", + "value": 1 + } + ], + "description": "Iterative Review Phase", + "predecessor": "6950164f-3c5e-4bdc-abc8-22aaf5a1bd49" + } +] diff --git a/src/phase-management/phase-rules.json b/src/phase-management/phase-rules.json new file mode 100644 index 00000000..ac1d250b --- /dev/null +++ b/src/phase-management/phase-rules.json @@ -0,0 +1,422 @@ +{ + "openRules": { + "Open": [ + { + "name": "Open Registration & Submission", + "conditions": { + "all": [ + { + "fact": "isPastScheduledStartTime", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ], + "Registration": [ + { + "name": "Registration Open", + "conditions": { + "all": [ + { + "fact": "isPastScheduledStartTime", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ], + "Submission": [ + { + "name": "Submission Open", + "conditions": { + "all": [ + { + "fact": "isPastScheduledStartTime", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ], + "Review": [ + { + "name": "Review Open", + "conditions": { + "all": [ + { + "fact": "isPastScheduledStartTime", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "isPredecessorPhaseClosed", + "operator": "equal", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ], + "IterativeReview": [ + { + "name": "Iterative Review Open", + "conditions": { + "all": [ + { + "fact": "hasActiveUnreviewedSubmissions", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ], + "Appeals": [ + { + "name": "Appeals Open", + "conditions": { + "all": [ + { + "fact": "isPastScheduledStartTime", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "isPredecessorPhaseClosed", + "operator": "equal", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ], + "AppealsResponse": [ + { + "name": "Appeals Response Open", + "conditions": { + "all": [ + { + "fact": "isPastScheduledStartTime", + "operator": "equal", + "value": true + }, + { + "fact": "isOpen", + "operator": "notEqual", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "isPredecessorPhaseClosed", + "operator": "equal", + "value": true + }, + { + "fact": "isPostMortemOpen", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canOpen" + } + } + ] + }, + "closeRules": { + "Open": [ + { + "name": "Close Registration & Submission", + "conditions": { + "all": [ + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "isPastScheduledEndTime", + "operator": "equal", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canClose" + } + } + ], + "Registration": [ + { + "name": "Registration Close", + "conditions": { + "all": [ + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "isPastScheduledEndTime", + "operator": "equal", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "registrantCount", + "operator": "greaterThanInclusive", + "value": 1 + } + ] + }, + "event": { + "type": "canClose" + } + } + ], + "Submission": [ + { + "name": "Submission Close", + "conditions": { + "all": [ + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "isPastScheduledEndTime", + "operator": "equal", + "value": true + } + ] + }, + "event": { + "type": "canClose" + } + } + ], + "Review": [ + { + "name": "Review Close", + "conditions": { + "all": [ + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "isPastScheduledEndTime", + "operator": "equal", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "allSubmissionsReviewed", + "operator": "equal", + "value": true + } + ] + }, + "event": { + "type": "canClose" + } + } + ], + "IterativeReview": [ + { + "name": "Iterative Review Close", + "conditions": { + "all": [ + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "wasSubmissionReviewedInCurrentOpenIterativeReviewPhase", + "operator": "equal", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canClose" + } + } + ], + "Appeals": [ + { + "name": "Appeals Close", + "conditions": { + "all": [ + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "isPastScheduledEndTime", + "operator": "equal", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + } + ] + }, + "event": { + "type": "canClose" + } + } + ], + "AppealsResponse": [ + { + "name": "Appeals Response Close", + "conditions": { + "all": [ + { + "fact": "isOpen", + "operator": "equal", + "value": true + }, + { + "fact": "isPastScheduledEndTime", + "operator": "equal", + "value": true + }, + { + "fact": "isClosed", + "operator": "notEqual", + "value": true + }, + { + "fact": "allAppealsResolved", + "operator": "equal", + "value": true + } + ] + }, + "event": { + "type": "canClose" + } + } + ] + }, + "constraintRules": { + "Registration": ["NumberofRegistrants"], + "Submission": ["NumberofSubmissions"] + }, + "constraintNameFactMap": { + "NumberofRegistrants": "registrantCount", + "NumberofSubmissions": "submissionCount" + } +} diff --git a/src/routes.js b/src/routes.js index 5a2b98cd..8adb7d2a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2,346 +2,399 @@ * Contains all routes */ -const constants = require('../app-constants') -const { SCOPES: { - READ, - CREATE, - UPDATE, - DELETE, - ALL -} } = require('config') +const constants = require("../app-constants"); +const { + SCOPES: { PAYMENT, READ, CREATE, UPDATE, DELETE, ALL }, +} = require("config"); module.exports = { - '/challenges': { + "/challenges": { get: { - controller: 'ChallengeController', - method: 'searchChallenges', - access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.SelfServiceCustomer, constants.UserRoles.Manager, constants.UserRoles.User], - scopes: [READ, ALL] + controller: "ChallengeController", + method: "searchChallenges", + access: [ + constants.UserRoles.Admin, + constants.UserRoles.Copilot, + constants.UserRoles.SelfServiceCustomer, + constants.UserRoles.Manager, + constants.UserRoles.User, + ], + scopes: [READ, ALL], }, post: { - controller: 'ChallengeController', - method: 'createChallenge', - auth: 'jwt', - access: [constants.UserRoles.Admin, constants.UserRoles.SelfServiceCustomer, constants.UserRoles.Copilot, constants.UserRoles.Manager, constants.UserRoles.User], - scopes: [CREATE, ALL] - } + controller: "ChallengeController", + method: "createChallenge", + auth: "jwt", + access: [ + constants.UserRoles.Admin, + constants.UserRoles.SelfServiceCustomer, + constants.UserRoles.Copilot, + constants.UserRoles.Manager, + ], + scopes: [CREATE, ALL], + }, }, - '/challenges/support-requests': { + "/challenges/support-requests": { post: { - controller: 'SupportController', - method: 'createRequest' - } + controller: "SupportController", + method: "createRequest", + }, + }, + "/challenges/srms/schedule": { + get: { + controller: "ChallengeController", + method: "getSRMSchedule", + }, }, - '/challenges/health': { + "/challenges/srms/practice/problems": { get: { - controller: 'HealthController', - method: 'checkHealth' - } + controller: "ChallengeController", + method: "getPracticeProblems", + auth: "jwt", + }, + }, + "/challenges/health": { + get: { + controller: "HealthController", + method: "checkHealth", + }, }, - '/challenges/:challengeId': { + "/challenges/:challengeId": { get: { - controller: 'ChallengeController', - method: 'getChallenge', - scopes: [READ, ALL] + controller: "ChallengeController", + method: "getChallenge", + scopes: [READ, ALL], }, + // @deprecated put: { - controller: 'ChallengeController', - method: 'fullyUpdateChallenge', - auth: 'jwt', - access: [constants.UserRoles.Admin, constants.UserRoles.SelfServiceCustomer, constants.UserRoles.Copilot, constants.UserRoles.Manager, constants.UserRoles.User], - scopes: [UPDATE, ALL] + controller: "ChallengeController", + method: "updateChallenge", + auth: "jwt", + access: [ + constants.UserRoles.Admin, + constants.UserRoles.SelfServiceCustomer, + constants.UserRoles.Copilot, + constants.UserRoles.Manager, + ], + scopes: [UPDATE, ALL], }, patch: { - controller: 'ChallengeController', - method: 'partiallyUpdateChallenge', - auth: 'jwt', - access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.SelfServiceCustomer, constants.UserRoles.Manager, constants.UserRoles.User], - scopes: [UPDATE, ALL] + controller: "ChallengeController", + method: "updateChallenge", + auth: "jwt", + access: [ + constants.UserRoles.Admin, + constants.UserRoles.SelfServiceCustomer, + constants.UserRoles.Copilot, + constants.UserRoles.Manager, + ], + scopes: [UPDATE, ALL], }, delete: { - controller: 'ChallengeController', - method: 'deleteChallenge', - auth: 'jwt', - access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.SelfServiceCustomer, constants.UserRoles.Manager, constants.UserRoles.User], - scopes: [DELETE, ALL] - } + controller: "ChallengeController", + method: "deleteChallenge", + auth: "jwt", + access: [ + constants.UserRoles.Admin, + constants.UserRoles.Copilot, + constants.UserRoles.SelfServiceCustomer, + constants.UserRoles.Manager, + ], + scopes: [DELETE, ALL], + }, + }, + "/challenges/:challengeId/advance-phase": { + post: { + controller: "ChallengeController", + method: "advancePhase", + auth: "jwt", + access: [constants.UserRoles.Admin], + scopes: [UPDATE, ALL], + }, + }, + "/challenges/:challengeId/legacy-payment": { + patch: { + controller: "ChallengeController", + method: "updateLegacyPayout", + auth: "jwt", + scopes: [PAYMENT], + }, }, - '/challenges/:challengeId/statistics': { + "/challenges/:challengeId/statistics": { get: { - controller: 'ChallengeController', - method: 'getChallengeStatistics' - } + controller: "ChallengeController", + method: "getChallengeStatistics", + }, }, - '/challenges/:challengeId/notifications': { + "/challenges/:challengeId/notifications": { post: { - controller: 'ChallengeController', - method: 'sendNotifications', - auth: 'jwt', + controller: "ChallengeController", + method: "sendNotifications", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/challenge-types': { + "/challenge-types": { get: { - controller: 'ChallengeTypeController', - method: 'searchChallengeTypes' + controller: "ChallengeTypeController", + method: "searchChallengeTypes", }, post: { - controller: 'ChallengeTypeController', - method: 'createChallengeType', - auth: 'jwt', + controller: "ChallengeTypeController", + method: "createChallengeType", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/challenge-types/:challengeTypeId': { + "/challenge-types/:challengeTypeId": { get: { - controller: 'ChallengeTypeController', - method: 'getChallengeType' + controller: "ChallengeTypeController", + method: "getChallengeType", }, put: { - controller: 'ChallengeTypeController', - method: 'fullyUpdateChallengeType', - auth: 'jwt', + controller: "ChallengeTypeController", + method: "fullyUpdateChallengeType", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, patch: { - controller: 'ChallengeTypeController', - method: 'partiallyUpdateChallengeType', - auth: 'jwt', + controller: "ChallengeTypeController", + method: "partiallyUpdateChallengeType", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, delete: { - controller: 'ChallengeTypeController', - method: 'deleteChallengeType', - auth: 'jwt', + controller: "ChallengeTypeController", + method: "deleteChallengeType", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [DELETE, ALL] - } + scopes: [DELETE, ALL], + }, }, - '/challenge-tracks': { + "/challenge-tracks": { get: { - controller: 'ChallengeTrackController', - method: 'searchChallengeTracks' + controller: "ChallengeTrackController", + method: "searchChallengeTracks", }, post: { - controller: 'ChallengeTrackController', - method: 'createChallengeTrack', - auth: 'jwt', + controller: "ChallengeTrackController", + method: "createChallengeTrack", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/challenge-tracks/:challengeTrackId': { + "/challenge-tracks/:challengeTrackId": { get: { - controller: 'ChallengeTrackController', - method: 'getChallengeTrack' + controller: "ChallengeTrackController", + method: "getChallengeTrack", }, put: { - controller: 'ChallengeTrackController', - method: 'fullyUpdateChallengeTrack', - auth: 'jwt', + controller: "ChallengeTrackController", + method: "fullyUpdateChallengeTrack", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, patch: { - controller: 'ChallengeTrackController', - method: 'partiallyUpdateChallengeTrack', - auth: 'jwt', + controller: "ChallengeTrackController", + method: "partiallyUpdateChallengeTrack", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, delete: { - controller: 'ChallengeTrackController', - method: 'deleteChallengeTrack', - auth: 'jwt', + controller: "ChallengeTrackController", + method: "deleteChallengeTrack", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [DELETE, ALL] - } + scopes: [DELETE, ALL], + }, }, - '/challenge-timelines': { + "/challenge-timelines": { get: { - controller: 'ChallengeTimelineTemplateController', - method: 'searchChallengeTimelineTemplates' + controller: "ChallengeTimelineTemplateController", + method: "searchChallengeTimelineTemplates", // auth: 'jwt', // access: [constants.UserRoles.Admin, constants.UserRoles.Copilot], // scopes: [READ, ALL] }, post: { - controller: 'ChallengeTimelineTemplateController', - method: 'createChallengeTimelineTemplate', - auth: 'jwt', + controller: "ChallengeTimelineTemplateController", + method: "createChallengeTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/challenge-timelines/:challengeTimelineTemplateId': { + "/challenge-timelines/:challengeTimelineTemplateId": { get: { - controller: 'ChallengeTimelineTemplateController', - method: 'getChallengeTimelineTemplate' + controller: "ChallengeTimelineTemplateController", + method: "getChallengeTimelineTemplate", // auth: 'jwt', // access: [constants.UserRoles.Admin, constants.UserRoles.Copilot], // scopes: [READ, ALL] }, put: { - controller: 'ChallengeTimelineTemplateController', - method: 'fullyUpdateChallengeTimelineTemplate', - auth: 'jwt', + controller: "ChallengeTimelineTemplateController", + method: "fullyUpdateChallengeTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, delete: { - controller: 'ChallengeTimelineTemplateController', - method: 'deleteChallengeTimelineTemplate', - auth: 'jwt', + controller: "ChallengeTimelineTemplateController", + method: "deleteChallengeTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [DELETE, ALL] - } + scopes: [DELETE, ALL], + }, }, - '/challenge-audit-logs': { + "/challenge-audit-logs": { get: { - controller: 'AuditLogController', - method: 'searchAuditLogs', - auth: 'jwt', + controller: "AuditLogController", + method: "searchAuditLogs", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [READ] - } + scopes: [READ], + }, }, - '/challenge-phases': { + "/challenge-phases": { get: { - controller: 'ChallengePhaseController', - method: 'searchPhases', - scopes: [READ, ALL] + controller: "ChallengePhaseController", + method: "searchPhases", + scopes: [READ, ALL], }, post: { - controller: 'ChallengePhaseController', - method: 'createPhase', - auth: 'jwt', + controller: "ChallengePhaseController", + method: "createPhase", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/challenge-phases/:challengePhaseId': { + "/challenge-phases/:challengePhaseId": { get: { - controller: 'ChallengePhaseController', - method: 'getPhase', - auth: 'jwt', + controller: "ChallengePhaseController", + method: "getPhase", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [READ, ALL] + scopes: [READ, ALL], }, put: { - controller: 'ChallengePhaseController', - method: 'fullyUpdatePhase', - auth: 'jwt', + controller: "ChallengePhaseController", + method: "fullyUpdatePhase", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, patch: { - controller: 'ChallengePhaseController', - method: 'partiallyUpdatePhase', - auth: 'jwt', + controller: "ChallengePhaseController", + method: "partiallyUpdatePhase", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, delete: { - controller: 'ChallengePhaseController', - method: 'deletePhase', - auth: 'jwt', + controller: "ChallengePhaseController", + method: "deletePhase", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [DELETE, ALL] - } + scopes: [DELETE, ALL], + }, }, - '/timeline-templates': { + "/timeline-templates": { get: { - controller: 'TimelineTemplateController', - method: 'searchTimelineTemplates', - scopes: [READ, ALL] + controller: "TimelineTemplateController", + method: "searchTimelineTemplates", + scopes: [READ, ALL], }, post: { - controller: 'TimelineTemplateController', - method: 'createTimelineTemplate', - auth: 'jwt', + controller: "TimelineTemplateController", + method: "createTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/timeline-templates/:timelineTemplateId': { + "/timeline-templates/:timelineTemplateId": { get: { - controller: 'TimelineTemplateController', - method: 'getTimelineTemplate', - auth: 'jwt', + controller: "TimelineTemplateController", + method: "getTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [READ, ALL] + scopes: [READ, ALL], }, put: { - controller: 'TimelineTemplateController', - method: 'fullyUpdateTimelineTemplate', - auth: 'jwt', + controller: "TimelineTemplateController", + method: "fullyUpdateTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, patch: { - controller: 'TimelineTemplateController', - method: 'partiallyUpdateTimelineTemplate', - auth: 'jwt', + controller: "TimelineTemplateController", + method: "partiallyUpdateTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, delete: { - controller: 'TimelineTemplateController', - method: 'deleteTimelineTemplate', - auth: 'jwt', + controller: "TimelineTemplateController", + method: "deleteTimelineTemplate", + auth: "jwt", access: [constants.UserRoles.Admin], - scopes: [DELETE, ALL] - } + scopes: [DELETE, ALL], + }, }, - '/challenges/:challengeId/attachments': { + "/challenges/:challengeId/attachments": { post: { - controller: 'AttachmentController', - method: 'createAttachment', - auth: 'jwt', + controller: "AttachmentController", + method: "createAttachment", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [CREATE, ALL] - } + scopes: [CREATE, ALL], + }, }, - '/challenges/:challengeId/attachments/:attachmentId': { + "/challenges/:challengeId/attachments/:attachmentId": { get: { - controller: 'AttachmentController', - method: 'getAttachment', - auth: 'jwt', // any authenticated role is allowed - scopes: [READ, ALL] + controller: "AttachmentController", + method: "getAttachment", + auth: "jwt", // any authenticated role is allowed + scopes: [READ, ALL], }, put: { - controller: 'AttachmentController', - method: 'updateAttachment', - auth: 'jwt', + controller: "AttachmentController", + method: "updateAttachment", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, patch: { - controller: 'AttachmentController', - method: 'partiallyUpdateAttachment', - auth: 'jwt', + controller: "AttachmentController", + method: "partiallyUpdateAttachment", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [UPDATE, ALL] + scopes: [UPDATE, ALL], }, delete: { - controller: 'AttachmentController', - method: 'deleteAttachment', - auth: 'jwt', + controller: "AttachmentController", + method: "deleteAttachment", + auth: "jwt", access: [constants.UserRoles.Admin, constants.UserRoles.Copilot, constants.UserRoles.Manager], - scopes: [DELETE, ALL] - } + scopes: [DELETE, ALL], + }, }, - '/challenges/:challengeId/attachments/:attachmentId/download': { + "/challenges/:challengeId/attachments/:attachmentId/download": { get: { - controller: 'AttachmentController', - method: 'downloadAttachment', - auth: 'jwt', // any authenticated role is allowed - scopes: [READ, ALL] - } - } -} + controller: "AttachmentController", + method: "downloadAttachment", + auth: "jwt", // any authenticated role is allowed + scopes: [READ, ALL], + }, + }, +}; diff --git a/src/scripts/check-templates.js b/src/scripts/check-templates.js index fab103ca..b8e50e00 100644 --- a/src/scripts/check-templates.js +++ b/src/scripts/check-templates.js @@ -1,18 +1,22 @@ -const _ = require('lodash') -const axios = require('axios') -const TEMPLATE_ID = '2d0807fa-ece1-4328-a260-76f5f6b559e0' // RUX challenge +const _ = require("lodash"); +const axios = require("axios"); +const TEMPLATE_ID = "2d0807fa-ece1-4328-a260-76f5f6b559e0"; // RUX challenge // const TEMPLATE_ID = '7ebf1c69-f62f-4d3a-bdfb-fe9ddb56861c' // dev challenge // const TEMPLATE_ID = 'd4201ca4-8437-4d63-9957-3f7708184b07' // design with checkpoint -async function main () { - let res - res = await axios.get('http://api.topcoder-dev.com/v5/timeline-templates') - const template = _.find(res.data, entry => entry.id === TEMPLATE_ID) - res = await axios.get('http://api.topcoder-dev.com/v5/challenge-phases') - const phases = res.data +async function main() { + let res; + res = await axios.get("http://api.topcoder-dev.com/v5/timeline-templates"); + const template = _.find(res.data, (entry) => entry.id === TEMPLATE_ID); + res = await axios.get("http://api.topcoder-dev.com/v5/challenge-phases"); + const phases = res.data; _.each(template.phases, (phase) => { - const phaseInstance = _.find(phases, p => p.id === phase.phaseId) - const pred = phase.predecessor ? _.find(phases, p => p.id === phase.predecessor) : null - console.log(`Phase Length: ${phase.defaultDuration / 60 / 60} hrs \t ${phaseInstance.name} - Depends on ${pred ? pred.name : 'nothing'}`) - }) + const phaseInstance = _.find(phases, (p) => p.id === phase.phaseId); + const pred = phase.predecessor ? _.find(phases, (p) => p.id === phase.predecessor) : null; + console.log( + `Phase Length: ${phase.defaultDuration / 60 / 60} hrs \t ${phaseInstance.name} - Depends on ${ + pred ? pred.name : "nothing" + }` + ); + }); } -main() +main(); diff --git a/src/scripts/create-tables.js b/src/scripts/create-tables.js index 624e3f13..6496b4e6 100644 --- a/src/scripts/create-tables.js +++ b/src/scripts/create-tables.js @@ -2,29 +2,31 @@ * Create table schemes in database */ -const models = require('../models') -const { includes } = require('lodash') -const logger = require('../common/logger') +const models = require("../models"); +const { includes } = require("lodash"); +const logger = require("../common/logger"); -logger.info('Requesting to create tables...') +logger.info("Requesting to create tables..."); -const promises = [] -const skipModels = ['DynamoDB'] +const promises = []; +const skipModels = ["DynamoDB"]; -Object.keys(models).forEach(modelName => { +Object.keys(models).forEach((modelName) => { if (!includes(skipModels, modelName)) { - promises.push(models[modelName].$__.table.create()) + promises.push(models[modelName].$__.table.create()); } else { - logger.info(`Skipping ${modelName}`) + logger.info(`Skipping ${modelName}`); } -}) +}); Promise.all(promises) .then(() => { - logger.info('All tables have been requested to be created. Creating processes is run asynchronously') - process.exit() + logger.info( + "All tables have been requested to be created. Creating processes is run asynchronously" + ); + process.exit(); }) .catch((err) => { - logger.logFullError(err) - process.exit(1) - }) + logger.logFullError(err); + process.exit(1); + }); diff --git a/src/scripts/drop-tables.js b/src/scripts/drop-tables.js index ccab820b..412c864e 100644 --- a/src/scripts/drop-tables.js +++ b/src/scripts/drop-tables.js @@ -2,29 +2,31 @@ * Drop tables in database. All data will be cleared. */ -const models = require('../models') -const { includes } = require('lodash') -const logger = require('../common/logger') +const models = require("../models"); +const { includes } = require("lodash"); +const logger = require("../common/logger"); -logger.info('Requesting to delete tables...') +logger.info("Requesting to delete tables..."); -const promises = [] -const skipModels = ['DynamoDB'] +const promises = []; +const skipModels = ["DynamoDB"]; -Object.keys(models).forEach(modelName => { +Object.keys(models).forEach((modelName) => { if (!includes(skipModels, modelName)) { - promises.push(models[modelName].$__.table.delete()) + promises.push(models[modelName].$__.table.delete()); } else { - logger.info(`Skipping ${modelName}`) + logger.info(`Skipping ${modelName}`); } -}) +}); Promise.all(promises) .then(() => { - logger.info('All tables have been requested to be deleted. Deleting processes is run asynchronously') - process.exit() + logger.info( + "All tables have been requested to be deleted. Deleting processes is run asynchronously" + ); + process.exit(); }) .catch((err) => { - logger.logFullError(err) - process.exit(1) - }) + logger.logFullError(err); + process.exit(1); + }); diff --git a/src/scripts/seed-tables.js b/src/scripts/seed-tables.js index 415fa78b..8a259fd6 100644 --- a/src/scripts/seed-tables.js +++ b/src/scripts/seed-tables.js @@ -2,35 +2,35 @@ * Insert seed data to tables in database */ -const { get, includes } = require('lodash') -const models = require('../models') -const logger = require('../common/logger') +const { get, includes } = require("lodash"); +const models = require("../models"); +const logger = require("../common/logger"); -logger.info('Requesting to insert seed data to the tables...') +logger.info("Requesting to insert seed data to the tables..."); -const promises = [] -const skipModels = [] +const promises = []; +const skipModels = []; -Object.keys(models).forEach(modelName => { +Object.keys(models).forEach((modelName) => { if (includes(skipModels, modelName)) { - logger.warn(`Skipping Seed Model ${modelName}`) - return + logger.warn(`Skipping Seed Model ${modelName}`); + return; } try { - const data = require(`./seed/${modelName}.json`) - logger.info(`Inserting ${get(data, 'length')} records in table ${modelName}`) - promises.push(models[modelName].batchPut(data)) + const data = require(`./seed/${modelName}.json`); + logger.info(`Inserting ${get(data, "length")} records in table ${modelName}`); + promises.push(models[modelName].batchPut(data)); } catch (e) { - logger.warn(`No records will be inserted in table ${modelName}`) + logger.warn(`No records will be inserted in table ${modelName}`); } -}) +}); Promise.all(promises) .then(() => { - logger.info('All tables have been inserted with the data. The processes is run asynchronously') - process.exit() + logger.info("All tables have been inserted with the data. The processes is run asynchronously"); + process.exit(); }) .catch((err) => { - logger.logFullError(err) - process.exit(1) - }) + logger.logFullError(err); + process.exit(1); + }); diff --git a/src/scripts/sync-es.js b/src/scripts/sync-es.js index d6f5b8da..b5ba3388 100644 --- a/src/scripts/sync-es.js +++ b/src/scripts/sync-es.js @@ -2,33 +2,55 @@ * Migrate Data from Dynamo DB to ES */ -const config = require('config') -const logger = require('../common/logger') -const helper = require('../common/helper') +const config = require("config"); +const logger = require("../common/logger"); +const helper = require("../common/helper"); +const models = require("../models"); +const _ = require("lodash"); -const esClient = helper.getESClient() +const esClient = helper.getESClient(); + +async function indexChallenge(challenge) { + console.log("Indexing challenge", challenge.id); + try { + await esClient.update({ + index: config.get("ES.ES_INDEX"), + type: config.get("ES.OPENSEARCH") == "false" ? config.get("ES.ES_TYPE") : undefined, + id: challenge.id, + body: { doc: challenge, doc_as_upsert: true }, + }); + } catch (err) { + console.log("Error indexing challenge", challenge.id, err); + } +} /* * Migrate records from DB to ES */ -async function migrateRecords () { - const result = await helper.scan('Challenge') - for (const challenge of result) { - await esClient.update({ - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), - id: challenge.id, - body: { doc: challenge, doc_as_upsert: true } - }) +async function migrateRecords() { + let results = await models["Challenge"].scan().exec(); + let lastKey = results.lastKey; + + for (const challenge of results) { + await indexChallenge(challenge); + } + + while (!_.isUndefined(results.lastKey)) { + const results = await models["Challenge"].scan().startAt(lastKey).exec(); + for (const challenge of results) { + await indexChallenge(challenge); + } + + lastKey = results.lastKey; } } migrateRecords() .then(() => { - logger.info('Done') - process.exit() + logger.info("Done"); + process.exit(); }) .catch((err) => { - logger.logFullError(err) - process.exit(1) - }) + logger.logFullError(err); + process.exit(1); + }); diff --git a/src/scripts/update-es-mappings.js b/src/scripts/update-es-mappings.js index d9151674..8b351907 100644 --- a/src/scripts/update-es-mappings.js +++ b/src/scripts/update-es-mappings.js @@ -1,68 +1,70 @@ -const config = require('config') -const logger = require('../common/logger') -const helper = require('../common/helper') +const config = require("config"); +const logger = require("../common/logger"); +const helper = require("../common/helper"); -const esClient = helper.getESClient() +const esClient = helper.getESClient(); -function sleep (ms) { +function sleep(ms) { return new Promise((resolve) => { - setTimeout(resolve, ms) - }) + setTimeout(resolve, ms); + }); } -function createIndex (indexName) { - const body = { mappings: {} } - body.mappings[config.get('ES.ES_TYPE')] = { +function createIndex(indexName) { + const body = { mappings: {} }; + body.mappings[config.get("ES.ES_TYPE")] = { properties: { - id: { type: 'keyword' }, + id: { type: "keyword" }, name: { - type: 'keyword', - normalizer: 'custom_sort_normalizer' + type: "keyword", + normalizer: "custom_sort_normalizer", }, prizeSets: { properties: { - type: { type: 'text' }, + type: { type: "text" }, prizes: { properties: { - type: { type: 'text' }, - value: { type: 'float' } - } - } - } - } + type: { type: "text" }, + value: { type: "float" }, + }, + }, + }, + }, }, - dynamic_templates: [{ - metadata: { - path_match: 'metadata.*', - mapping: { - type: 'text' - } - } - }] - } + dynamic_templates: [ + { + metadata: { + path_match: "metadata.*", + mapping: { + type: "text", + }, + }, + }, + ], + }; body.settings = { analysis: { normalizer: { custom_sort_normalizer: { - type: 'custom', + type: "custom", char_filter: [], - filter: ['lowercase', 'asciifolding'] - } - } - } - } + filter: ["lowercase", "asciifolding"], + }, + }, + }, + }; return esClient.indices.create({ index: indexName, - body - }) + body, + }); } -async function updateMappings () { - const tempReindexing = config.get('ES.TEMP_REINDEXING') - let indexName = config.get('ES.ES_INDEX') - let backupIndex = 'challenge_tmp_dont_use_for_querying_backup' - let newIndexName = 'challenge_tmp_dont_use_for_querying' +async function updateMappings() { + const tempReindexing = config.get("ES.TEMP_REINDEXING"); + let indexName = config.get("ES.ES_INDEX"); + let backupIndex = "challenge_tmp_dont_use_for_querying_backup"; + let newIndexName = "challenge_tmp_dont_use_for_querying"; // if (tempReindexing) { // try { @@ -132,10 +134,10 @@ async function updateMappings () { updateMappings() .then(() => { - logger.info('Done') - process.exit() + logger.info("Done"); + process.exit(); }) .catch((err) => { - logger.logFullError(err) - process.exit(1) - }) + logger.logFullError(err); + process.exit(1); + }); diff --git a/src/scripts/view-data.js b/src/scripts/view-data.js index 8208005f..7d321bf4 100644 --- a/src/scripts/view-data.js +++ b/src/scripts/view-data.js @@ -2,33 +2,35 @@ * View table data. */ -require('../../app-bootstrap') -const _ = require('lodash') -const models = require('../models') -const logger = require('../common/logger') -const helper = require('../common/helper') +require("../../app-bootstrap"); +const _ = require("lodash"); +const models = require("../models"); +const logger = require("../common/logger"); +const helper = require("../common/helper"); const viewData = async (modelName) => { - const fieldNames = _.keys(models[modelName].$__.table.schema.attributes) - const records = await helper.scan(modelName) - console.log(_.map(records, e => _.pick(e, fieldNames))) -} + const fieldNames = _.keys(models[modelName].$__.table.schema.attributes); + const records = await helper.scan(modelName); + console.log(_.map(records, (e) => _.pick(e, fieldNames))); +}; if (process.argv.length === 2) { - logger.info(`Please provide one of the following table name: [${_.keys(models)}]`) - process.exit(1) + logger.info(`Please provide one of the following table name: [${_.keys(models)}]`); + process.exit(1); } else { - const modelName = process.argv[2] + const modelName = process.argv[2]; if (_.keys(models).includes(modelName)) { - viewData(modelName).then(() => { - logger.info('Done!') - process.exit() - }).catch((e) => { - logger.logFullError(e) - process.exit(1) - }) + viewData(modelName) + .then(() => { + logger.info("Done!"); + process.exit(); + }) + .catch((e) => { + logger.logFullError(e); + process.exit(1); + }); } else { - logger.info(`Please provide one of the following table name: [${_.keys(models)}]`) - process.exit(1) + logger.info(`Please provide one of the following table name: [${_.keys(models)}]`); + process.exit(1); } } diff --git a/src/scripts/view-es-data.js b/src/scripts/view-es-data.js index aebce8c5..aa06115b 100644 --- a/src/scripts/view-es-data.js +++ b/src/scripts/view-es-data.js @@ -2,30 +2,27 @@ * View all ES data. */ -const helper = require('../common/helper') -const config = require('config') -const logger = require('../common/logger') +const helper = require("../common/helper"); +const config = require("config"); +const logger = require("../common/logger"); -const esClient = helper.getESClient() +const esClient = helper.getESClient(); -async function showESData () { +async function showESData() { const result = await esClient.search({ - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE') - }) - return result + index: config.get("ES.ES_INDEX"), + }); + return result; } showESData() - .then(result => { - logger.info('All data in ES is shown belows.') - console.log( - JSON.stringify(result.hits.hits, null, 2) - ) - logger.info('Done!') - process.exit() - }) - .catch(err => { - console.log(err) - process.exit(1) + .then((result) => { + logger.info("All data in ES is shown belows."); + console.log(JSON.stringify(result.hits.hits, null, 2)); + logger.info("Done!"); + process.exit(); }) + .catch((err) => { + console.log(err); + process.exit(1); + }); diff --git a/src/services/AttachmentService.js b/src/services/AttachmentService.js index f9e30a81..8c4d6fb3 100644 --- a/src/services/AttachmentService.js +++ b/src/services/AttachmentService.js @@ -1,21 +1,33 @@ - /** * This service provides operations of attachments. */ +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; + +const { + DomainHelper: { getLookupCriteria, getScanCriteria }, +} = require("@topcoder-framework/lib-common"); + +const { AttachmentDomain, ChallengeDomain } = require("@topcoder-framework/domain-challenge"); -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const config = require('config') -const errors = require('../common/errors') -const helper = require('../common/helper') -const s3ParseUrl = require('../common/s3ParseUrl') -const models = require('../models') -const logger = require('../common/logger') -const constants = require('../../app-constants') -const challengeService = require('./ChallengeService') +const _ = require("lodash"); +const Joi = require("joi"); +const config = require("config"); +const errors = require("../common/errors"); +const helper = require("../common/helper"); +const s3ParseUrl = require("../common/s3ParseUrl"); +const logger = require("../common/logger"); +const constants = require("../../app-constants"); +const challengeService = require("./ChallengeService"); -const bucketWhitelist = config.AMAZON.BUCKET_WHITELIST.split(',').map((bucketName) => bucketName.trim()) +const bucketWhitelist = config.AMAZON.BUCKET_WHITELIST.split(",").map((bucketName) => + bucketName.trim() +); + +const attachmentDomain = new AttachmentDomain( + GRPC_CHALLENGE_SERVER_HOST, + GRPC_CHALLENGE_SERVER_PORT +); +const challengeDomain = new ChallengeDomain(GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT); /** * Check if a url is acceptable. @@ -23,15 +35,15 @@ const bucketWhitelist = config.AMAZON.BUCKET_WHITELIST.split(',').map((bucketNam * @param {String} url the url * @returns {undefined} */ -function validateUrl (url) { - const s3UrlObject = s3ParseUrl(url) +function validateUrl(url) { + const s3UrlObject = s3ParseUrl(url); if (!s3UrlObject) { - return + return; } if (bucketWhitelist.includes(s3UrlObject.bucket)) { - return + return; } - throw new errors.BadRequestError(`The bucket ${s3UrlObject.bucket} is not in the whitelist`) + throw new errors.BadRequestError(`The bucket ${s3UrlObject.bucket} is not in the whitelist`); } /** @@ -40,13 +52,13 @@ function validateUrl (url) { * @param {String} attachmentId the attachment id * @returns {Object} the challenge and the attachment */ -async function _getChallengeAttachment (challengeId, attachmentId) { - const challenge = await helper.getById('Challenge', challengeId) - const attachment = await models.Attachment.get(attachmentId) +async function _getChallengeAttachment(challengeId, attachmentId) { + const challenge = await helperchallengeDomain.lookup(getLookupCriteria("id", challengeId)); + const attachment = await attachmentDomain.lookup(getLookupCriteria("id", attachmentId)); if (!attachment || attachment.challengeId !== challengeId) { - throw errors.NotFoundError(`Attachment ${attachmentId} not found in challenge ${challengeId}`) + throw errors.NotFoundError(`Attachment ${attachmentId} not found in challenge ${challengeId}`); } - return { challenge, attachment } + return { challenge, attachment }; } /** @@ -55,38 +67,40 @@ async function _getChallengeAttachment (challengeId, attachmentId) { * @param {Array} attachments the attachments to be created * @returns {Object} the created attachment */ -async function createAttachment (currentUser, challengeId, attachments) { - const challenge = await helper.getById('Challenge', challengeId) - await helper.ensureUserCanModifyChallenge(currentUser, challenge) - const newAttachments = [] +async function createAttachment(currentUser, challengeId, attachments) { + const challenge = await helperchallengeDomain.lookup(getLookupCriteria("id", challengeId)); + await helper.ensureUserCanModifyChallenge(currentUser, challenge); + const newAttachments = []; for (const attachment of attachments) { - validateUrl(attachment.url) - const attachmentObject = { id: uuid(), challengeId, ...attachment } - const newAttachment = await helper.create('Attachment', attachmentObject) - await helper.postBusEvent(constants.Topics.ChallengeAttachmentCreated, newAttachment) - newAttachments.push(newAttachment) + validateUrl(attachment.url); + const attachmentObject = { challengeId, ...attachment }; + const newAttachment = await attachmentDomain.create(attachmentObject); + await helper.postBusEvent(constants.Topics.ChallengeAttachmentCreated, newAttachment); + newAttachments.push(newAttachment); } // update challenge object await challengeService.partiallyUpdateChallenge(currentUser, challengeId, { - attachments: [ - ..._.get(challenge, 'attachments', []), - ...newAttachments - ] - }) + attachments: [..._.get(challenge, "attachments", []), ...newAttachments], + }); // post bus event - return newAttachments + return newAttachments; } createAttachment.schema = { currentUser: Joi.any(), challengeId: Joi.id(), - attachments: Joi.array().items(Joi.object().keys({ - name: Joi.string().required(), - url: Joi.string().uri().required(), - fileSize: Joi.fileSize(), - description: Joi.string() - })).required().min(1) -} + attachments: Joi.array() + .items( + Joi.object().keys({ + name: Joi.string().required(), + url: Joi.string().uri().required(), + fileSize: Joi.fileSize(), + description: Joi.string(), + }) + ) + .required() + .min(1), +}; /** * Get attachment @@ -94,17 +108,17 @@ createAttachment.schema = { * @param {String} attachmentId the attachment id * @returns {Object} the attachment with given id */ -async function getAttachment (currentUser, challengeId, attachmentId) { - const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId) - await helper.ensureUserCanViewChallenge(currentUser, challenge) - return attachment +async function getAttachment(currentUser, challengeId, attachmentId) { + const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId); + await helper.ensureUserCanViewChallenge(currentUser, challenge); + return attachment; } getAttachment.schema = { currentUser: Joi.any(), challengeId: Joi.id(), - attachmentId: Joi.id() -} + attachmentId: Joi.id(), +}; /** * Update attachment. @@ -114,32 +128,37 @@ getAttachment.schema = { * @param {Boolean} isFull the flag indicate it is a fully update operation. * @returns {Object} the updated attachment */ -async function update (currentUser, challengeId, attachmentId, data, isFull) { - const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId) - await helper.ensureUserCanModifyChallenge(currentUser, challenge) - validateUrl(data.url) +async function update(currentUser, challengeId, attachmentId, data, isFull) { + const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId); + await helper.ensureUserCanModifyChallenge(currentUser, challenge); + validateUrl(data.url); if (isFull) { // optional fields can be undefined - attachment.fileSize = data.fileSize - attachment.description = data.description + attachment.fileSize = data.fileSize; + attachment.description = data.description; } - const ret = await helper.update(attachment, data) + const { items } = await attachmentDomain.update({ + filterCriteria: getScanCriteria({ id: attachmentId }), + updateInput: data, + }); // update challenge object - const newAttachments = _.get(challenge, 'attachments', []) + const newAttachments = _.get(challenge, "attachments", []); try { - newAttachments[_.findIndex(newAttachments, a => a.id === attachmentId)] = ret + newAttachments[_.findIndex(newAttachments, (a) => a.id === attachmentId)] = items[0]; await challengeService.partiallyUpdateChallenge(currentUser, challengeId, { - attachments: newAttachments - }) + attachments: newAttachments, + }); } catch (e) { - logger.warn(`The attachment ${attachmentId} does not exist on the challenge object`) + logger.warn(`The attachment ${attachmentId} does not exist on the challenge object`); } // post bus event - await helper.postBusEvent(constants.Topics.ChallengeAttachmentUpdated, - isFull ? ret : _.assignIn({ id: attachmentId }, data)) - return ret + await helper.postBusEvent( + constants.Topics.ChallengeAttachmentUpdated, + isFull ? items[0] : _.assignIn({ id: attachmentId }, data) + ); + return items[0]; } /** @@ -149,21 +168,23 @@ async function update (currentUser, challengeId, attachmentId, data, isFull) { * @param {Object} data the attachment data to be updated * @returns {Object} the updated attachment */ -async function fullyUpdateAttachment (currentUser, challengeId, attachmentId, data) { - return update(currentUser, challengeId, attachmentId, data, true) +async function fullyUpdateAttachment(currentUser, challengeId, attachmentId, data) { + return update(currentUser, challengeId, attachmentId, data, true); } fullyUpdateAttachment.schema = { currentUser: Joi.any(), challengeId: Joi.id(), attachmentId: Joi.id(), - data: Joi.object().keys({ - name: Joi.string().required(), - url: Joi.string().uri().required(), - fileSize: Joi.fileSize(), - description: Joi.string() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string().required(), + url: Joi.string().uri().required(), + fileSize: Joi.fileSize(), + description: Joi.string(), + }) + .required(), +}; /** * Partially update attachment. @@ -172,55 +193,62 @@ fullyUpdateAttachment.schema = { * @param {Object} data the attachment data to be updated * @returns {Object} the updated attachment */ -async function partiallyUpdateAttachment (currentUser, challengeId, attachmentId, data) { - return update(currentUser, challengeId, attachmentId, data) +async function partiallyUpdateAttachment(currentUser, challengeId, attachmentId, data) { + return update(currentUser, challengeId, attachmentId, data); } partiallyUpdateAttachment.schema = { currentUser: Joi.any(), challengeId: Joi.id(), attachmentId: Joi.id(), - data: Joi.object().keys({ - name: Joi.string(), - url: Joi.string().uri(), - fileSize: Joi.fileSize(), - description: Joi.string() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string(), + url: Joi.string().uri(), + fileSize: Joi.fileSize(), + description: Joi.string(), + }) + .required(), +}; /** * Delete attachment. * @param {String} attachmentId the attachment id * @returns {Object} the deleted attachment */ -async function deleteAttachment (currentUser, challengeId, attachmentId) { - const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId) - await helper.ensureUserCanModifyChallenge(currentUser, challenge) - const s3UrlObject = s3ParseUrl(attachment.url) +async function deleteAttachment(currentUser, challengeId, attachmentId) { + const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId); + await helper.ensureUserCanModifyChallenge(currentUser, challenge); + const s3UrlObject = s3ParseUrl(attachment.url); if (s3UrlObject) { - await helper.deleteFromS3(s3UrlObject.bucket, s3UrlObject.key) + await helper.deleteFromS3(s3UrlObject.bucket, s3UrlObject.key); } - await attachment.delete() + + await attachmentDomain.delete(getLookupCriteria("id", attachmentId)); + // update challenge object - const newAttachments = _.get(challenge, 'attachments', []) + const newAttachments = _.get(challenge, "attachments", []); try { - newAttachments.splice(_.findIndex(newAttachments, a => a.id === attachmentId), 1) + newAttachments.splice( + _.findIndex(newAttachments, (a) => a.id === attachmentId), + 1 + ); await challengeService.partiallyUpdateChallenge(currentUser, challengeId, { - attachments: newAttachments - }) + attachments: newAttachments, + }); } catch (e) { - logger.warn(`The attachment ${attachmentId} does not exist on the challenge object`) + logger.warn(`The attachment ${attachmentId} does not exist on the challenge object`); } // post bus event - await helper.postBusEvent(constants.Topics.ChallengeAttachmentDeleted, attachment) - return attachment + await helper.postBusEvent(constants.Topics.ChallengeAttachmentDeleted, attachment); + return attachment; } deleteAttachment.schema = { currentUser: Joi.any(), challengeId: Joi.id(), - attachmentId: Joi.id() -} + attachmentId: Joi.id(), +}; /** * Download attachment. @@ -228,26 +256,26 @@ deleteAttachment.schema = { * @param {String} attachmentId the attachment id * @returns {Promise} the downloaded attachment data */ -async function downloadAttachment (currentUser, challengeId, attachmentId) { - const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId) - await helper.ensureUserCanViewChallenge(currentUser, challenge) - const s3UrlObject = s3ParseUrl(attachment.url) +async function downloadAttachment(currentUser, challengeId, attachmentId) { + const { challenge, attachment } = await _getChallengeAttachment(challengeId, attachmentId); + await helper.ensureUserCanViewChallenge(currentUser, challenge); + const s3UrlObject = s3ParseUrl(attachment.url); if (s3UrlObject) { // download from S3 - const data = await helper.downloadFromS3(s3UrlObject.bucket, s3UrlObject.key) - data.fileName = attachment.name - return data + const data = await helper.downloadFromS3(s3UrlObject.bucket, s3UrlObject.key); + data.fileName = attachment.name; + return data; } - const data = await helper.downloadFromFileStack(attachment.url) - data.fileName = attachment.name - return data + const data = await helper.downloadFromFileStack(attachment.url); + data.fileName = attachment.name; + return data; } downloadAttachment.schema = { currentUser: Joi.any(), challengeId: Joi.id(), - attachmentId: Joi.id() -} + attachmentId: Joi.id(), +}; module.exports = { createAttachment, @@ -255,7 +283,7 @@ module.exports = { fullyUpdateAttachment, partiallyUpdateAttachment, deleteAttachment, - downloadAttachment -} + downloadAttachment, +}; -logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/src/services/AuditLogService.js b/src/services/AuditLogService.js index a94e7b64..ee772c42 100644 --- a/src/services/AuditLogService.js +++ b/src/services/AuditLogService.js @@ -2,9 +2,9 @@ * This service provides operations of audit logs. */ -const _ = require('lodash') -const Joi = require('joi') -const helper = require('../common/helper') +const _ = require("lodash"); +const Joi = require("joi"); +const helper = require("../common/helper"); // const logger = require('../common/logger') /** @@ -12,21 +12,29 @@ const helper = require('../common/helper') * @param {Object} criteria the search criteria * @returns {Object} the search result */ -async function searchAuditLogs (criteria) { - const page = criteria.page || 1 - const perPage = criteria.perPage || 50 - let records = await helper.scanAll('AuditLog') +async function searchAuditLogs(criteria) { + const page = criteria.page || 1; + const perPage = criteria.perPage || 50; + let records = await helper.scanAll("AuditLog"); // TODO this needs to be in ES - if (criteria.fieldName) records = _.filter(records, e => helper.partialMatch(criteria.fieldName, e.fieldName)) - if (criteria.createdDateStart) records = _.filter(records, e => criteria.createdDateStart.getTime() <= e.created.getTime()) - if (criteria.createdDateEnd) records = _.filter(records, e => criteria.createdDateEnd.getTime() <= e.created.getTime()) - if (criteria.challengeId) records = _.filter(records, e => criteria.challengeId === e.challengeId) - if (criteria.createdBy) records = _.filter(records, e => criteria.createdBy.toLowerCase() === e.createdBy.toLowerCase()) + if (criteria.fieldName) + records = _.filter(records, (e) => helper.partialMatch(criteria.fieldName, e.fieldName)); + if (criteria.createdDateStart) + records = _.filter(records, (e) => criteria.createdDateStart.getTime() <= e.created.getTime()); + if (criteria.createdDateEnd) + records = _.filter(records, (e) => criteria.createdDateEnd.getTime() <= e.created.getTime()); + if (criteria.challengeId) + records = _.filter(records, (e) => criteria.challengeId === e.challengeId); + if (criteria.createdBy) + records = _.filter( + records, + (e) => criteria.createdBy.toLowerCase() === e.createdBy.toLowerCase() + ); - const total = records.length - const result = records.slice((page - 1) * perPage, page * perPage) + const total = records.length; + const result = records.slice((page - 1) * perPage, page * perPage); - return { total, page, perPage, result } + return { total, page, perPage, result }; } searchAuditLogs.schema = { @@ -37,12 +45,12 @@ searchAuditLogs.schema = { fieldName: Joi.string(), createdDateStart: Joi.date(), createdDateEnd: Joi.date(), - createdBy: Joi.string() - }) -} + createdBy: Joi.string(), + }), +}; module.exports = { - searchAuditLogs -} + searchAuditLogs, +}; -// logger.buildService(module.exports) +// logger.buildService(module.exports); diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 64c0852a..8bd5e33e 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -2,121 +2,80 @@ * This service provides operations of challenge. */ -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const config = require('config') -const xss = require('xss') -const helper = require('../common/helper') -const logger = require('../common/logger') -const errors = require('../common/errors') -const phaseHelper = require('../common/phase-helper') -const constants = require('../../app-constants') -const models = require('../models') -const HttpStatus = require('http-status-codes') -const moment = require('moment') -const PhaseService = require('./PhaseService') -const ChallengeTypeService = require('./ChallengeTypeService') -const ChallengeTrackService = require('./ChallengeTrackService') -const ChallengeTimelineTemplateService = require('./ChallengeTimelineTemplateService') -const { BadRequestError } = require('../common/errors') - -const esClient = helper.getESClient() - -/** - * Check if user can perform modification/deletion to a challenge - * - * @param {Object} user the JwT user object - * @param {Object} challenge the challenge object - * @returns {undefined} - */ -async function ensureAccessibleForChallenge (user, challenge) { - const userHasFullAccess = await helper.userHasFullAccess(challenge.id, user.userId) - if (!user.isMachine && !helper.hasAdminRole(user) && challenge.createdBy.toLowerCase() !== user.handle.toLowerCase() && !userHasFullAccess) { - throw new errors.ForbiddenError(`Only M2M, admin, challenge's copilot or users with full access can perform modification.`) - } -} - -/** - * Filter challenges by groups access - * @param {Object} currentUser the user who perform operation - * @param {Array} challenges the challenges to filter - * @returns {Array} the challenges that can be accessed by current user - */ -async function filterChallengesByGroupsAccess (currentUser, challenges) { - const res = [] - let userGroups - const needToCheckForGroupAccess = !currentUser ? true : !currentUser.isMachine && !helper.hasAdminRole(currentUser) - const subGroupsMap = {} - for (const challenge of challenges) { - challenge.groups = _.filter(challenge.groups, g => !_.includes(['null', 'undefined'], _.toString(g).toLowerCase())) - let expandedGroups = [] - if (!challenge.groups || _.get(challenge, 'groups.length', 0) === 0 || !needToCheckForGroupAccess) { - res.push(challenge) - } else if (currentUser) { - // get user groups if not yet - if (_.isNil(userGroups)) { - userGroups = await helper.getUserGroups(currentUser.userId) - } - // Expand challenge groups by subGroups - // results are being saved on a hashmap for efficiency - for (const group of challenge.groups) { - let subGroups - if (subGroupsMap[group]) { - subGroups = subGroupsMap[group] - } else { - subGroups = await helper.expandWithSubGroups(group) - subGroupsMap[group] = subGroups - } - expandedGroups = [ - ..._.concat(expandedGroups, subGroups) - ] - } - // check if there is matched group - // logger.debug('Groups', challenge.groups, userGroups) - if (_.find(expandedGroups, (group) => !!_.find(userGroups, (ug) => ug.id === group))) { - res.push(challenge) - } - } +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; + +const { + DomainHelper: { getLookupCriteria, getScanCriteria }, +} = require("@topcoder-framework/lib-common"); + +const _ = require("lodash"); +const Joi = require("joi"); +const uuid = require("uuid/v4"); +const config = require("config"); +const xss = require("xss"); +const helper = require("../common/helper"); +const logger = require("../common/logger"); +const errors = require("../common/errors"); +const constants = require("../../app-constants"); +const HttpStatus = require("http-status-codes"); +const ChallengeTypeService = require("./ChallengeTypeService"); +const ChallengeTrackService = require("./ChallengeTrackService"); +const ChallengeTimelineTemplateService = require("./ChallengeTimelineTemplateService"); +const { BadRequestError } = require("../common/errors"); + +const phaseHelper = require("../common/phase-helper"); +const projectHelper = require("../common/project-helper"); +const challengeHelper = require("../common/challenge-helper"); + +const { Metadata: GrpcMetadata } = require("@grpc/grpc-js"); + +const esClient = helper.getESClient(); + +const PhaseAdvancer = require("../phase-management/PhaseAdvancer"); +const { ChallengeDomain } = require("@topcoder-framework/domain-challenge"); +const { QueryDomain } = require("@topcoder-framework/domain-acl"); + +const { hasAdminRole } = require("../common/role-helper"); +const { + enrichChallengeForResponse, + sanitizeRepeatedFieldsInUpdateRequest, + convertPrizeSetValuesToCents, + convertPrizeSetValuesToDollars, + convertToISOString, +} = require("../common/challenge-helper"); +const deepEqual = require("deep-equal"); +const { getM2MToken } = require("../common/m2m-helper"); +const { + getSRMScheduleQuery, + getPracticeProblemsQuery, + convertSRMScheduleQueryOutput, + convertPracticeProblemsQueryOutput, +} = require("../common/srm-helper"); + +const challengeDomain = new ChallengeDomain( + GRPC_CHALLENGE_SERVER_HOST, + GRPC_CHALLENGE_SERVER_PORT, + { + "grpc.service_config": JSON.stringify({ + methodConfig: [ + { + name: [{ service: "topcoder.domain.service.challenge.Challenge" }], + retryPolicy: { + maxAttempts: 5, + initialBackoff: "0.5s", + maxBackoff: "30s", + backoffMultiplier: 2, + retryableStatusCodes: ["UNAVAILABLE", "DEADLINE_EXCEEDED", "INTERNAL"], + }, + }, + ], + }), } - return res -} +); -/** - * Ensure the user can access the challenge by groups access - * @param {Object} currentUser the user who perform operation - * @param {Object} challenge the challenge to check - */ -async function ensureAccessibleByGroupsAccess (currentUser, challenge) { - const filtered = await filterChallengesByGroupsAccess(currentUser, [challenge]) - if (filtered.length === 0) { - throw new errors.ForbiddenError(`ensureAccessibleByGroupsAccess :: You don't have access to this group! - Current User: ${JSON.stringify(currentUser)} - Challenge: ${JSON.stringify(challenge)} - Filtered: ${JSON.stringify(filtered)} - `) - } -} +const aclQueryDomain = new QueryDomain(config.GRPC_ACL_SERVER_HOST, config.GRPC_ACL_SERVER_PORT); -/** - * Ensure the user can access the groups being updated to - * @param {Object} currentUser the user who perform operation - * @param {Object} data the challenge data to be updated - * @param {String} challenge the original challenge data - */ -async function ensureAcessibilityToModifiedGroups (currentUser, data, challenge) { - const needToCheckForGroupAccess = !currentUser ? true : !currentUser.isMachine && !helper.hasAdminRole(currentUser) - if (!needToCheckForGroupAccess) { - return - } - const userGroups = await helper.getUserGroups(currentUser.userId) - const userGroupsIds = _.map(userGroups, group => group.id) - const updatedGroups = _.difference(_.union(challenge.groups, data.groups), _.intersection(challenge.groups, data.groups)) - const filtered = updatedGroups.filter(g => !userGroupsIds.includes(g)) - if (filtered.length > 0) { - throw new errors.ForbiddenError("ensureAcessibilityToModifiedGroups :: You don't have access to this group!") - } -} +const phaseAdvancer = new PhaseAdvancer(challengeDomain); /** * Search challenges by legacyId @@ -126,42 +85,43 @@ async function ensureAcessibilityToModifiedGroups (currentUser, data, challenge) * @param {Number} perPage the perPage * @returns {Array} the search result */ -async function searchByLegacyId (currentUser, legacyId, page, perPage) { +async function searchByLegacyId(currentUser, legacyId, page, perPage) { const esQuery = { - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), + index: config.get("ES.ES_INDEX"), + type: config.get("ES.OPENSEARCH") == "false" ? config.get("ES.ES_TYPE") : undefined, size: perPage, from: (page - 1) * perPage, body: { query: { term: { - legacyId - } - } - } - } - - logger.debug(`es Query ${JSON.stringify(esQuery)}`) - let docs + legacyId, + }, + }, + }, + }; + let docs; try { - docs = await esClient.search(esQuery) + docs = + config.get("ES.OPENSEARCH") == "false" + ? await esClient.search(esQuery) + : (await esClient.search(esQuery)).body; } catch (e) { - logger.error(`Query Error from ES ${JSON.stringify(e)}`) + logger.error(`Query Error from ES ${JSON.stringify(e)}`); docs = { hits: { - hits: [] - } - } + hits: [], + }, + }; } - const ids = _.map(docs.hits.hits, item => item._source.id) - const result = [] + const ids = _.map(docs.hits.hits, (item) => item._source.id); + const result = []; for (const id of ids) { try { - const challenge = await getChallenge(currentUser, id) - result.push(challenge) + const challenge = await getChallenge(currentUser, id); + result.push(challenge); } catch (e) {} } - return result + return result; } /** @@ -170,312 +130,361 @@ async function searchByLegacyId (currentUser, legacyId, page, perPage) { * @param {Object} criteria the search criteria * @returns {Object} the search result */ -async function searchChallenges (currentUser, criteria) { +async function searchChallenges(currentUser, criteria) { // construct ES query - const page = criteria.page || 1 - const perPage = criteria.perPage || 20 + const page = criteria.page || 1; + const perPage = criteria.perPage || 20; if (!_.isUndefined(criteria.legacyId)) { - const result = await searchByLegacyId(currentUser, criteria.legacyId, page, perPage) - return { total: result.length, page, perPage, result } + const result = await searchByLegacyId(currentUser, criteria.legacyId, page, perPage); + return { total: result.length, page, perPage, result }; } - const boolQuery = [] - let sortByScore = false + const boolQuery = []; + let sortByScore = false; const matchPhraseKeys = [ - 'id', - 'timelineTemplateId', - 'projectId', - 'legacyId', - 'status', - 'createdBy', - 'updatedBy' - ] - - const hasAdminRole = helper.hasAdminRole(currentUser) + "id", + "timelineTemplateId", + "projectId", + "legacyId", + "status", + "createdBy", + "updatedBy", + ]; - const includeSelfService = currentUser && (currentUser.isMachine || hasAdminRole || _.includes(config.SELF_SERVICE_WHITELIST_HANDLES, currentUser.handle.toLowerCase())) + const _hasAdminRole = hasAdminRole(currentUser); - const includedTrackIds = _.isArray(criteria.trackIds) ? criteria.trackIds : [] - - const includedTypeIds = _.isArray(criteria.typeIds) ? criteria.typeIds : [] + const includedTrackIds = _.isArray(criteria.trackIds) ? criteria.trackIds : []; + const includedTypeIds = _.isArray(criteria.typeIds) ? criteria.typeIds : []; if (criteria.type) { - const typeSearchRes = await ChallengeTypeService.searchChallengeTypes({ abbreviation: criteria.type }) + const typeSearchRes = await ChallengeTypeService.searchChallengeTypes({ + abbreviation: criteria.type, + }); if (typeSearchRes.total > 0) { - criteria.typeId = _.get(typeSearchRes, 'result[0].id') + criteria.typeId = _.get(typeSearchRes, "result[0].id"); } } if (criteria.track) { - const trackSearchRes = await ChallengeTrackService.searchChallengeTracks({ abbreviation: criteria.track }) + const trackSearchRes = await ChallengeTrackService.searchChallengeTracks({ + abbreviation: criteria.track, + }); if (trackSearchRes.total > 0) { - criteria.trackId = _.get(trackSearchRes, 'result[0].id') + criteria.trackId = _.get(trackSearchRes, "result[0].id"); } } - if (criteria.types) { for (const t of criteria.types) { - const typeSearchRes = await ChallengeTypeService.searchChallengeTypes({ abbreviation: t }) + const typeSearchRes = await ChallengeTypeService.searchChallengeTypes({ abbreviation: t }); if (typeSearchRes.total > 0 || criteria.types.length === 1) { - includedTypeIds.push(_.get(typeSearchRes, 'result[0].id')) + includedTypeIds.push(_.get(typeSearchRes, "result[0].id")); } } } if (criteria.tracks) { for (const t of criteria.tracks) { - const trackSearchRes = await ChallengeTrackService.searchChallengeTracks({ abbreviation: t }) + const trackSearchRes = await ChallengeTrackService.searchChallengeTracks({ + abbreviation: t, + }); if (trackSearchRes.total > 0) { - includedTrackIds.push(_.get(trackSearchRes, 'result[0].id')) + includedTrackIds.push(_.get(trackSearchRes, "result[0].id")); } } } - if (criteria.typeId) { - includedTypeIds.push(criteria.typeId) + includedTypeIds.push(criteria.typeId); } if (criteria.trackId) { - includedTrackIds.push(criteria.trackId) + includedTrackIds.push(criteria.trackId); } _.forIn(_.pick(criteria, matchPhraseKeys), (value, key) => { if (!_.isUndefined(value)) { - const filter = { match_phrase: {} } - filter.match_phrase[key] = value - boolQuery.push(filter) + const filter = { match_phrase: {} }; + filter.match_phrase[key] = value; + boolQuery.push(filter); } - }) + }); _.forEach(_.keys(criteria), (key) => { - if (_.toString(key).indexOf('meta.') > -1) { + if (_.toString(key).indexOf("meta.") > -1) { // Parse and use metadata key if (!_.isUndefined(criteria[key])) { - const metaKey = key.split('meta.')[1] + const metaKey = key.split("meta.")[1]; boolQuery.push({ bool: { must: [ - { match_phrase: { 'metadata.name': metaKey } }, - { match_phrase: { 'metadata.value': _.toString(criteria[key]) } } - ] - } - }) + { match_phrase: { "metadata.name": metaKey } }, + { match_phrase: { "metadata.value": _.toString(criteria[key]) } }, + ], + }, + }); } } - }) + }); if (includedTypeIds.length > 0) { boolQuery.push({ bool: { - should: _.map(includedTypeIds, t => ({ match_phrase: { typeId: t } })) - } - }) + should: _.map(includedTypeIds, (t) => ({ + match_phrase: { typeId: t }, + })), + }, + }); } if (includedTrackIds.length > 0) { boolQuery.push({ bool: { - should: _.map(includedTrackIds, t => ({ match_phrase: { trackId: t } })) - } - }) + should: _.map(includedTrackIds, (t) => ({ + match_phrase: { trackId: t }, + })), + }, + }); } - const multiMatchQuery = [] + const multiMatchQuery = []; if (criteria.search) { multiMatchQuery.push({ // exact match multi_match: { query: criteria.search, - fields: [ - 'name.text^7', - 'tags^3', - 'description^2' - ], - type: 'phrase_prefix', - boost: 5 - } - }) + fields: ["name.text^7", "tags^3", "skills.name^3", "description^2"], + type: "phrase_prefix", + boost: 5, + }, + }); multiMatchQuery.push({ // match 100% words multi_match: { query: criteria.search, - fields: [ - 'name.text^3.0', - 'tags^2.5', - 'description^1.0' - ], - type: 'most_fields', - minimum_should_match: '100%', - boost: 2.5 - } - }) + fields: ["name.text^3.0", "tags^2.5", "skills.name^2.5", "description^1.0"], + type: "most_fields", + minimum_should_match: "100%", + boost: 2.5, + }, + }); multiMatchQuery.push({ // fuzzy match multi_match: { query: criteria.search, - fields: [ - 'name.text^2.5', - 'tags^1.5', - 'description^1.0' + fields: ["name.text^2.5", "tags^1.5", "skills.name^1.5", "description^1.0"], + type: "most_fields", + minimum_should_match: "50%", + fuzziness: "AUTO", + boost: 1, + }, + }); + boolQuery.push({ + bool: { + should: [ + { wildcard: { name: `*${criteria.search}*` } }, + { wildcard: { name: `${criteria.search}*` } }, + { wildcard: { name: `*${criteria.search}` } }, + { match_phrase: { tags: criteria.search } }, + { match_phrase: { "skills.name": criteria.search } }, ], - type: 'most_fields', - minimum_should_match: '50%', - fuzziness: 'AUTO', - boost: 1 - } - }) - boolQuery.push({ bool: { - should: [ - { wildcard: { name: `*${criteria.search}*` } }, - { wildcard: { name: `${criteria.search}*` } }, - { wildcard: { name: `*${criteria.search}` } }, - { match_phrase: { tags: criteria.search } } - ] - } }) + }, + }); } else { if (criteria.name) { - boolQuery.push({ bool: { - should: [ - { wildcard: { name: `*${criteria.name}*` } }, - { wildcard: { name: `${criteria.name}*` } }, - { wildcard: { name: `*${criteria.name}` } } - ] - } }) + boolQuery.push({ + bool: { + should: [ + { wildcard: { name: `*${criteria.name}*` } }, + { wildcard: { name: `${criteria.name}*` } }, + { wildcard: { name: `*${criteria.name}` } }, + ], + }, + }); } if (criteria.description) { - boolQuery.push({ match_phrase_prefix: { 'description': criteria.description } }) + boolQuery.push({ + match_phrase_prefix: { description: criteria.description }, + }); } } // 'search', 'name', 'description' fields should be sorted by function score unless sortBy param provided. - if (!criteria.sortBy && ( - criteria.search || - criteria.name || - criteria.description - )) { - sortByScore = true + if (!criteria.sortBy && (criteria.search || criteria.name || criteria.description)) { + sortByScore = true; } if (criteria.tag) { - boolQuery.push({ match_phrase: { tags: criteria.tag } }) + boolQuery.push({ match_phrase: { tags: criteria.tag } }); } if (criteria.tags) { boolQuery.push({ bool: { - [criteria.includeAllTags ? 'must' : 'should']: _.map(criteria.tags, t => ({ match_phrase: { tags: t } })) - } - }) + [criteria.includeAllTags ? "must" : "should"]: _.map(criteria.tags, (t) => ({ + match_phrase: { tags: t }, + })), + }, + }); } if (criteria.totalPrizesFrom || criteria.totalPrizesTo) { - const prizeRangeQuery = {} + const prizeRangeQuery = {}; if (criteria.totalPrizesFrom) { - prizeRangeQuery.gte = criteria.totalPrizesFrom + prizeRangeQuery.gte = criteria.totalPrizesFrom; } if (criteria.totalPrizesTo) { - prizeRangeQuery.lte = criteria.totalPrizesTo + prizeRangeQuery.lte = criteria.totalPrizesTo; } - boolQuery.push({ range: { 'overview.totalPrizes': prizeRangeQuery } }) - } - - if (criteria.useSchedulingAPI) { - boolQuery.push({ match_phrase: { 'legacy.useSchedulingAPI': criteria.useSchedulingAPI } }) + boolQuery.push({ range: { "overview.totalPrizes": prizeRangeQuery } }); } if (criteria.selfService) { - boolQuery.push({ match_phrase: { 'legacy.selfService': criteria.selfService } }) + boolQuery.push({ + match_phrase: { "legacy.selfService": criteria.selfService }, + }); } if (criteria.selfServiceCopilot) { - boolQuery.push({ match_phrase: { 'legacy.selfServiceCopilot': criteria.selfServiceCopilot } }) + boolQuery.push({ + match_phrase: { + "legacy.selfServiceCopilot": criteria.selfServiceCopilot, + }, + }); } if (criteria.forumId) { - boolQuery.push({ match_phrase: { 'legacy.forumId': criteria.forumId } }) + boolQuery.push({ match_phrase: { "legacy.forumId": criteria.forumId } }); } if (criteria.reviewType) { - boolQuery.push({ match_phrase: { 'legacy.reviewType': criteria.reviewType } }) + boolQuery.push({ + match_phrase: { "legacy.reviewType": criteria.reviewType }, + }); } if (criteria.confidentialityType) { - boolQuery.push({ match_phrase: { 'legacy.confidentialityType': criteria.confidentialityType } }) + boolQuery.push({ + match_phrase: { + "legacy.confidentialityType": criteria.confidentialityType, + }, + }); } if (criteria.directProjectId) { - boolQuery.push({ match_phrase: { 'legacy.directProjectId': criteria.directProjectId } }) + boolQuery.push({ + match_phrase: { "legacy.directProjectId": criteria.directProjectId }, + }); } if (criteria.currentPhaseName) { - boolQuery.push({ match_phrase: { 'currentPhaseNames': criteria.currentPhaseName } }) + if (criteria.currentPhaseName === "Registration") { + boolQuery.push({ + bool: { + should: [ + { match_phrase: { currentPhaseNames: "Registration" } }, + { match_phrase: { currentPhaseNames: "Open" } }, + ], + minimum_should_match: 1, + }, + }); + } else { + boolQuery.push({ + match_phrase: { currentPhaseNames: criteria.currentPhaseName }, + }); + } } if (criteria.createdDateStart) { - boolQuery.push({ range: { created: { gte: criteria.createdDateStart } } }) + boolQuery.push({ range: { created: { gte: criteria.createdDateStart } } }); } if (criteria.createdDateEnd) { - boolQuery.push({ range: { created: { lte: criteria.createdDateEnd } } }) + boolQuery.push({ range: { created: { lte: criteria.createdDateEnd } } }); } if (criteria.registrationStartDateStart) { - boolQuery.push({ range: { registrationStartDate: { gte: criteria.registrationStartDateStart } } }) + boolQuery.push({ + range: { + registrationStartDate: { gte: criteria.registrationStartDateStart }, + }, + }); } if (criteria.registrationStartDateEnd) { - boolQuery.push({ range: { registrationStartDate: { lte: criteria.registrationStartDateEnd } } }) + boolQuery.push({ + range: { + registrationStartDate: { lte: criteria.registrationStartDateEnd }, + }, + }); } if (criteria.registrationEndDateStart) { - boolQuery.push({ range: { registrationEndDate: { gte: criteria.registrationEndDateStart } } }) + boolQuery.push({ + range: { + registrationEndDate: { gte: criteria.registrationEndDateStart }, + }, + }); } if (criteria.registrationEndDateEnd) { - boolQuery.push({ range: { registrationEndDate: { lte: criteria.registrationEndDateEnd } } }) + boolQuery.push({ + range: { registrationEndDate: { lte: criteria.registrationEndDateEnd } }, + }); } if (criteria.submissionStartDateStart) { - boolQuery.push({ range: { submissionStartDate: { gte: criteria.submissionStartDateStart } } }) + boolQuery.push({ + range: { + submissionStartDate: { gte: criteria.submissionStartDateStart }, + }, + }); } if (criteria.submissionStartDateEnd) { - boolQuery.push({ range: { submissionStartDate: { lte: criteria.submissionStartDateEnd } } }) + boolQuery.push({ + range: { submissionStartDate: { lte: criteria.submissionStartDateEnd } }, + }); } if (criteria.submissionEndDateStart) { - boolQuery.push({ range: { submissionEndDate: { gte: criteria.submissionEndDateStart } } }) + boolQuery.push({ + range: { submissionEndDate: { gte: criteria.submissionEndDateStart } }, + }); } if (criteria.submissionEndDateEnd) { - boolQuery.push({ range: { submissionEndDate: { lte: criteria.submissionEndDateEnd } } }) + boolQuery.push({ + range: { submissionEndDate: { lte: criteria.submissionEndDateEnd } }, + }); } if (criteria.updatedDateStart) { - boolQuery.push({ range: { updated: { gte: criteria.updatedDateStart } } }) + boolQuery.push({ range: { updated: { gte: criteria.updatedDateStart } } }); } if (criteria.updatedDateEnd) { - boolQuery.push({ range: { updated: { lte: criteria.updatedDateEnd } } }) + boolQuery.push({ range: { updated: { lte: criteria.updatedDateEnd } } }); } if (criteria.startDateStart) { - boolQuery.push({ range: { startDate: { gte: criteria.startDateStart } } }) + boolQuery.push({ range: { startDate: { gte: criteria.startDateStart } } }); } if (criteria.startDateEnd) { - boolQuery.push({ range: { startDate: { lte: criteria.startDateEnd } } }) + boolQuery.push({ range: { startDate: { lte: criteria.startDateEnd } } }); } if (criteria.endDateStart) { - boolQuery.push({ range: { endDate: { gte: criteria.endDateStart } } }) + boolQuery.push({ range: { endDate: { gte: criteria.endDateStart } } }); } if (criteria.endDateEnd) { - boolQuery.push({ range: { endDate: { lte: criteria.endDateEnd } } }) + boolQuery.push({ range: { endDate: { lte: criteria.endDateEnd } } }); } - let sortByProp = criteria.sortBy ? criteria.sortBy : 'created' - const sortOrderProp = criteria.sortOrder ? criteria.sortOrder : 'desc' + let sortByProp = criteria.sortBy ? criteria.sortBy : "created"; + + const sortOrderProp = criteria.sortOrder ? criteria.sortOrder : "desc"; - const mustQuery = [] + const mustQuery = []; - const groupsQuery = [] + const groupsQuery = []; if (criteria.tco) { - boolQuery.push({ match_phrase_prefix: { 'events.key': 'tco' } }) + boolQuery.push({ match_phrase_prefix: { "events.key": "tco" } }); } if (criteria.events) { boolQuery.push({ bool: { - [criteria.includeAllEvents ? 'must' : 'should']: _.map(criteria.events, e => ({ match_phrase: { 'events.key': e } })) - } - }) + [criteria.includeAllEvents ? "must" : "should"]: _.map(criteria.events, (e) => ({ + match_phrase: { "events.key": e }, + })), + }, + }); } - const mustNotQuery = [] + const mustNotQuery = []; - let groupsToFilter = [] - let accessibleGroups = [] + let groupsToFilter = []; + let accessibleGroups = []; - if (currentUser && !currentUser.isMachine && !hasAdminRole) { - accessibleGroups = await helper.getCompleteUserGroupTreeIds(currentUser.userId) + if (currentUser && !currentUser.isMachine && !_hasAdminRole) { + accessibleGroups = await helper.getCompleteUserGroupTreeIds(currentUser.userId); } // Filter all groups from the criteria to make sure the user can access those @@ -483,50 +492,48 @@ async function searchChallenges (currentUser, criteria) { // check group access if (_.isUndefined(currentUser)) { if (criteria.group) { - const group = await helper.getGroupById(criteria.group) + const group = await helper.getGroupById(criteria.group); if (group && !group.privateGroup) { - groupsToFilter.push(criteria.group) + groupsToFilter.push(criteria.group); } } if (criteria.groups && criteria.groups.length > 0) { - const promises = [] + const promises = []; _.each(criteria.groups, (g) => { promises.push( (async () => { - const group = await helper.getGroupById(g) + const group = await helper.getGroupById(g); if (group && !group.privateGroup) { - groupsToFilter.push(g) + groupsToFilter.push(g); } })() - ) - }) - await Promise.all(promises) + ); + }); + await Promise.all(promises); } - } else if (!currentUser.isMachine && !hasAdminRole) { + } else if (!currentUser.isMachine && !_hasAdminRole) { if (accessibleGroups.includes(criteria.group)) { - groupsToFilter.push(criteria.group) + groupsToFilter.push(criteria.group); } if (criteria.groups && criteria.groups.length > 0) { _.each(criteria.groups, (g) => { if (accessibleGroups.includes(g)) { - groupsToFilter.push(g) + groupsToFilter.push(g); } - }) + }); } } else { - groupsToFilter = [ - ...(criteria.groups ? criteria.groups : []) - ] + groupsToFilter = [...(criteria.groups ? criteria.groups : [])]; if (criteria.group) { - groupsToFilter.push(criteria.group) + groupsToFilter.push(criteria.group); } } - groupsToFilter = _.uniq(groupsToFilter) + groupsToFilter = _.uniq(groupsToFilter); if (groupsToFilter.length === 0) { // User can't access any of the groups from the filters // We return an empty array as the result - return { total: 0, page, perPage, result: [] } + return { total: 0, page, perPage, result: [] }; } } @@ -534,31 +541,27 @@ async function searchChallenges (currentUser, criteria) { // Return public challenges + challenges from groups that the user has access to if (_.isUndefined(currentUser)) { // If the user is not authenticated, only query challenges that don't have a group - mustNotQuery.push({ exists: { field: 'groups' } }) - } else if (!currentUser.isMachine && !hasAdminRole) { + mustNotQuery.push({ exists: { field: "groups" } }); + } else if (!currentUser.isMachine && !_hasAdminRole) { // If the user is not M2M and is not an admin, return public + challenges from groups the user can access - _.each(accessibleGroups, (g) => { - groupsQuery.push({ match_phrase: { groups: g } }) - }) + groupsQuery.push({ terms: { "groups.keyword": accessibleGroups } }); // include public challenges - groupsQuery.push({ bool: { must_not: { exists: { field: 'groups' } } } }) + groupsQuery.push({ bool: { must_not: { exists: { field: "groups" } } } }); } } else { - _.each(groupsToFilter, (g) => { - groupsQuery.push({ match_phrase: { groups: g } }) - }) + groupsQuery.push({ terms: { "groups.keyword": groupsToFilter } }); } if (criteria.ids) { boolQuery.push({ bool: { - should: _.map(criteria.ids, id => ({ match_phrase: { _id: id } })) - } - }) + should: _.map(criteria.ids, (id) => ({ match_phrase: { _id: id } })), + }, + }); } - const accessQuery = [] - let memberChallengeIds + const accessQuery = []; + let memberChallengeIds; // FIXME: This is wrong! // if (!_.isUndefined(currentUser) && currentUser.handle) { @@ -567,26 +570,26 @@ async function searchChallenges (currentUser, criteria) { if (criteria.memberId) { // logger.error(`memberId ${criteria.memberId}`) - memberChallengeIds = await helper.listChallengesByMember(criteria.memberId) + memberChallengeIds = await helper.listChallengesByMember(criteria.memberId); // logger.error(`response ${JSON.stringify(ids)}`) - accessQuery.push({ terms: { _id: memberChallengeIds } }) - } else if (currentUser && !hasAdminRole && !_.get(currentUser, 'isMachine', false)) { - memberChallengeIds = await helper.listChallengesByMember(currentUser.userId) + accessQuery.push({ terms: { _id: memberChallengeIds } }); + } else if (currentUser && !_hasAdminRole && !_.get(currentUser, "isMachine", false)) { + memberChallengeIds = await helper.listChallengesByMember(currentUser.userId); } if (accessQuery.length > 0) { mustQuery.push({ bool: { - should: accessQuery - } - }) + should: accessQuery, + }, + }); } // FIXME: Tech Debt - let excludeTasks = true + let excludeTasks = true; // if you're an admin or m2m, security rules wont be applied - if (currentUser && (hasAdminRole || _.get(currentUser, 'isMachine', false))) { - excludeTasks = false + if (currentUser && (_hasAdminRole || _.get(currentUser, "isMachine", false))) { + excludeTasks = false; } /** @@ -597,299 +600,255 @@ async function searchChallenges (currentUser, criteria) { * For admins/m2m: * - All tasks will be returned */ - if (currentUser && (hasAdminRole || _.get(currentUser, 'isMachine', false))) { + if (currentUser && (_hasAdminRole || _.get(currentUser, "isMachine", false))) { // For admins/m2m, allow filtering based on task properties if (criteria.isTask) { - boolQuery.push({ match_phrase: { 'task.isTask': criteria.isTask } }) + boolQuery.push({ match_phrase: { "task.isTask": criteria.isTask } }); } if (criteria.taskIsAssigned) { - boolQuery.push({ match_phrase: { 'task.isAssigned': criteria.taskIsAssigned } }) - } - if (criteria.taskMemberId || criteria.memberId) { - boolQuery.push({ match_phrase: { 'task.memberId': criteria.taskMemberId || criteria.memberId } }) + boolQuery.push({ + match_phrase: { "task.isAssigned": criteria.taskIsAssigned }, + }); + } + if (criteria.taskMemberId) { + boolQuery.push({ + match_phrase: { + "task.memberId": criteria.taskMemberId, + }, + }); } } else if (excludeTasks) { mustQuery.push({ bool: { should: [ - ...(_.get(memberChallengeIds, 'length', 0) > 0 ? [{ bool: { should: [ { terms: { _id: memberChallengeIds } } ] } }] : []), - { bool: { must_not: { exists: { field: 'task.isTask' } } } }, - { match_phrase: { 'task.isTask': false } }, + ...(_.get(memberChallengeIds, "length", 0) > 0 + ? [{ bool: { should: [{ terms: { _id: memberChallengeIds } }] } }] + : []), + { bool: { must_not: { exists: { field: "task.isTask" } } } }, + { match_phrase: { "task.isTask": false } }, { bool: { must: [ - { match_phrase: { 'task.isTask': true } }, - { match_phrase: { 'task.isAssigned': false } } - ] - } - }, - ...( - currentUser && !hasAdminRole && !_.get(currentUser, 'isMachine', false) - ? [{ match_phrase: { 'task.memberId': currentUser.userId } }] - : [] - ) - ] - } - }) - } - - if (!includeSelfService) { - mustQuery.push({ - bool: { - should: [ - { bool: { must_not: { exists: { field: 'legacy.selfService' } } } }, - ...(currentUser ? [ - { - bool: { - must: [ - { bool: { must_not: { match_phrase: { 'status': constants.challengeStatuses.New } } } }, - { bool: { must_not: { match_phrase: { 'status': constants.challengeStatuses.Draft } } } }, - { bool: { must_not: { match_phrase: { 'status': constants.challengeStatuses.Approved } } } } - ] - } + { match_phrase: { "task.isTask": true } }, + { match_phrase: { "task.isAssigned": false } }, + ], }, - { bool: { must: { match_phrase: { 'createdBy': currentUser.handle } } } } - ] : [ - { - bool: { - should: [ - { bool: { must: { match_phrase: { 'status': constants.challengeStatuses.Active } } } }, - { bool: { must: { match_phrase: { 'status': constants.challengeStatuses.Completed } } } } - ] - } - } - ]) - ] - } - }) + }, + ...(currentUser && !_hasAdminRole && !_.get(currentUser, "isMachine", false) + ? [{ match_phrase: { "task.memberId": currentUser.userId } }] + : []), + ], + }, + }); } if (groupsQuery.length > 0) { mustQuery.push({ bool: { - should: groupsQuery - } - }) + should: groupsQuery, + }, + }); } if (multiMatchQuery) { mustQuery.push({ bool: { - should: multiMatchQuery - } - }) + should: multiMatchQuery, + }, + }); } if (boolQuery.length > 0) { mustQuery.push({ bool: { - filter: boolQuery - } - }) + filter: boolQuery, + }, + }); } let finalQuery = { - bool: {} - } + bool: {}, + }; if (mustQuery.length > 0) { - finalQuery.bool.must = mustQuery + finalQuery.bool.must = mustQuery; } if (mustNotQuery.length > 0) { - finalQuery.bool.must_not = mustNotQuery + finalQuery.bool.must_not = mustNotQuery; if (!finalQuery.bool.must) { - finalQuery.bool.must = mustQuery + finalQuery.bool.must = mustQuery; } } // if none of the above were set, match all if (!finalQuery.bool.must) { finalQuery = { - match_all: {} - } + match_all: {}, + }; } const esQuery = { - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), + index: config.get("ES.ES_INDEX"), size: perPage, from: (page - 1) * perPage, // Es Index starts from 0 body: { query: finalQuery, - sort: [sortByScore ? { '_score': { 'order': 'desc' } } : { [sortByProp]: { 'order': sortOrderProp, 'missing': '_last', 'unmapped_type': 'String' } }] - } - } + sort: [ + sortByScore + ? { _score: { order: "desc" } } + : { + [sortByProp]: { + order: sortOrderProp, + missing: "_last", + unmapped_type: "keyword", + }, + }, + ], + }, + }; - logger.debug(`es Query ${JSON.stringify(esQuery, null, 4)}`) + logger.info(`ES Query: ${JSON.stringify(esQuery)}`) // Search with constructed query - let docs + let docs; try { - docs = await esClient.search(esQuery) + docs = + config.get("ES.OPENSEARCH") == "false" + ? await esClient.search(esQuery) + : (await esClient.search(esQuery)).body; } catch (e) { + logger.error(JSON.stringify(e)); // Catch error when the ES is fresh and has no data - logger.error(`Query Error from ES ${JSON.stringify(e, null, 4)}`) docs = { hits: { total: 0, - hits: [] - } - } + hits: [], + }, + }; } + // Extract data from hits - const total = docs.hits.total - let result = _.map(docs.hits.hits, item => item._source) + const total = docs.hits.total.value; + + let result = _.map(docs.hits.hits, (item) => item._source); // Hide privateDescription for non-register challenges if (currentUser) { - if (!currentUser.isMachine && !hasAdminRole) { - result = _.each(result, (val) => _.unset(val, 'billing')) - const ids = await helper.listChallengesByMember(currentUser.userId) + if (!currentUser.isMachine && !_hasAdminRole) { + result = _.each(result, (val) => _.unset(val, "billing")); + const ids = await helper.listChallengesByMember(currentUser.userId); result = _.each(result, (val) => { if (!_.includes(ids, val.id)) { - _.unset(val, 'privateDescription') + _.unset(val, "privateDescription"); } - }) + }); } } else { - result = _.each(result, val => { - _.unset(val, 'billing') - _.unset(val, 'privateDescription') - return val - }) + result = _.each(result, (val) => { + _.unset(val, "billing"); + _.unset(val, "privateDescription"); + return val; + }); } - if (criteria.isLightweight === 'true') { - result = _.each(result, val => { + if (criteria.isLightweight === "true") { + result = _.each(result, (val) => { // _.unset(val, 'terms') - _.unset(val, 'description') - _.unset(val, 'privateDescription') - return val - }) - } - - const challengeTypeList = await ChallengeTypeService.searchChallengeTypes({}) - const typeMap = new Map() - _.each(challengeTypeList.result, e => { - typeMap.set(e.id, e.name) - }) - - _.each(result, element => { - element.type = typeMap.get(element.typeId) || 'Code' - }) - _.each(result, async element => { - await getPhasesAndPopulate(element) + _.unset(val, "description"); + _.unset(val, "privateDescription"); + return val; + }); + } + + const challengeTypeList = await ChallengeTypeService.searchChallengeTypes({}); + const typeMap = new Map(); + _.each(challengeTypeList.result, (e) => { + typeMap.set(e.id, e.name); + }); + + _.each(result, (element) => { + element.type = typeMap.get(element.typeId) || "Code"; + }); + _.each(result, async (element) => { + await getPhasesAndPopulate(element); if (element.status !== constants.challengeStatuses.Completed) { - _.unset(element, 'winners') + _.unset(element, "winners"); + } + // TODO: in the long run we wanna do a finer grained filtering of the payments + if (!_hasAdminRole && !_.get(currentUser, "isMachine", false)) { + _.unset(element, "payments"); } - }) + }); - return { total, page, perPage, result } + return { total, page, perPage, result }; } - searchChallenges.schema = { currentUser: Joi.any(), - criteria: Joi.object().keys({ - page: Joi.page(), - perPage: Joi.perPage(), - id: Joi.optionalId(), - selfService: Joi.boolean(), - selfServiceCopilot: Joi.string(), - confidentialityType: Joi.string(), - directProjectId: Joi.number(), - typeIds: Joi.array().items(Joi.optionalId()), - trackIds: Joi.array().items(Joi.optionalId()), - types: Joi.array().items(Joi.string()), - tracks: Joi.array().items(Joi.string()), - typeId: Joi.optionalId(), - trackId: Joi.optionalId(), - type: Joi.string(), - track: Joi.string(), - name: Joi.string(), - search: Joi.string(), - description: Joi.string(), - timelineTemplateId: Joi.string(), // Joi.optionalId(), - reviewType: Joi.string(), - tag: Joi.string(), - tags: Joi.array().items(Joi.string()), - includeAllTags: Joi.boolean().default(true), - projectId: Joi.number().integer().positive(), - forumId: Joi.number().integer(), - legacyId: Joi.number().integer().positive(), - status: Joi.string().valid(_.values(constants.challengeStatuses)), - group: Joi.string(), - startDateStart: Joi.date(), - startDateEnd: Joi.date(), - endDateStart: Joi.date(), - endDateEnd: Joi.date(), - currentPhaseName: Joi.string(), - createdDateStart: Joi.date(), - createdDateEnd: Joi.date(), - updatedDateStart: Joi.date(), - updatedDateEnd: Joi.date(), - registrationStartDateStart: Joi.date(), - registrationStartDateEnd: Joi.date(), - registrationEndDateStart: Joi.date(), - registrationEndDateEnd: Joi.date(), - submissionStartDateStart: Joi.date(), - submissionStartDateEnd: Joi.date(), - submissionEndDateStart: Joi.date(), - submissionEndDateEnd: Joi.date(), - createdBy: Joi.string(), - updatedBy: Joi.string(), - isLightweight: Joi.boolean().default(false), - memberId: Joi.string(), - sortBy: Joi.string().valid(_.values(constants.validChallengeParams)), - sortOrder: Joi.string().valid(['asc', 'desc']), - groups: Joi.array().items(Joi.optionalId()).unique().min(1), - ids: Joi.array().items(Joi.optionalId()).unique().min(1), - isTask: Joi.boolean(), - taskIsAssigned: Joi.boolean(), - taskMemberId: Joi.string(), - events: Joi.array().items(Joi.string()), - includeAllEvents: Joi.boolean().default(true), - useSchedulingAPI: Joi.boolean(), - totalPrizesFrom: Joi.number().min(0), - totalPrizesTo: Joi.number().min(0), - tco: Joi.boolean().default(false) - }).unknown(true) -} - -/** - * Validate the challenge data. - * @param {Object} challenge the challenge data - */ -async function validateChallengeData (challenge) { - let type - let track - if (challenge.typeId) { - try { - type = await helper.getById('ChallengeType', challenge.typeId) - } catch (e) { - if (e.name === 'NotFoundError') { - throw new errors.BadRequestError(`No challenge type found with id: ${challenge.typeId}.`) - } else { - throw e - } - } - } - if (challenge.trackId) { - try { - track = await helper.getById('ChallengeTrack', challenge.trackId) - } catch (e) { - if (e.name === 'NotFoundError') { - throw new errors.BadRequestError(`No challenge track found with id: ${challenge.trackId}.`) - } else { - throw e - } - } - } - if (challenge.timelineTemplateId) { - const template = await helper.getById('TimelineTemplate', challenge.timelineTemplateId) - if (!template.isActive) { - throw new errors.BadRequestError(`The timeline template with id: ${challenge.timelineTemplateId} is inactive`) - } - } - return { type, track } -} + criteria: Joi.object() + .keys({ + page: Joi.page(), + perPage: Joi.perPage(), + id: Joi.optionalId(), + selfService: Joi.boolean(), + selfServiceCopilot: Joi.string(), + confidentialityType: Joi.string(), + directProjectId: Joi.number(), + typeIds: Joi.array().items(Joi.optionalId()), + trackIds: Joi.array().items(Joi.optionalId()), + types: Joi.array().items(Joi.string()), + tracks: Joi.array().items(Joi.string()), + typeId: Joi.optionalId(), + trackId: Joi.optionalId(), + type: Joi.string(), + track: Joi.string(), + name: Joi.string(), + search: Joi.string(), + description: Joi.string(), + timelineTemplateId: Joi.string(), // Joi.optionalId(), + reviewType: Joi.string(), + tag: Joi.string(), + tags: Joi.array().items(Joi.string()), + includeAllTags: Joi.boolean().default(true), + projectId: Joi.number().integer().positive(), + forumId: Joi.number().integer(), + legacyId: Joi.number().integer().positive(), + status: Joi.string().valid(_.values(constants.challengeStatuses)), + group: Joi.string(), + startDateStart: Joi.date(), + startDateEnd: Joi.date(), + endDateStart: Joi.date(), + endDateEnd: Joi.date(), + currentPhaseName: Joi.string(), + createdDateStart: Joi.date(), + createdDateEnd: Joi.date(), + updatedDateStart: Joi.date(), + updatedDateEnd: Joi.date(), + registrationStartDateStart: Joi.date(), + registrationStartDateEnd: Joi.date(), + registrationEndDateStart: Joi.date(), + registrationEndDateEnd: Joi.date(), + submissionStartDateStart: Joi.date(), + submissionStartDateEnd: Joi.date(), + submissionEndDateStart: Joi.date(), + submissionEndDateEnd: Joi.date(), + createdBy: Joi.string(), + updatedBy: Joi.string(), + isLightweight: Joi.boolean().default(false), + memberId: Joi.string(), + sortBy: Joi.string().valid(_.values(constants.validChallengeParams)), + sortOrder: Joi.string().valid(["asc", "desc"]), + groups: Joi.array().items(Joi.optionalId()).unique().min(1), + ids: Joi.array().items(Joi.optionalId()).unique().min(1), + isTask: Joi.boolean(), + taskIsAssigned: Joi.boolean(), + taskMemberId: Joi.string(), + events: Joi.array().items(Joi.string()), + includeAllEvents: Joi.boolean().default(true), + useSchedulingAPI: Joi.boolean(), + totalPrizesFrom: Joi.number().min(0), + totalPrizesTo: Joi.number().min(0), + tco: Joi.boolean().default(false), + }) + .unknown(true), +}; /** * Create challenge. @@ -898,283 +857,338 @@ async function validateChallengeData (challenge) { * @param {String} userToken the user token * @returns {Object} the created challenge */ -async function createChallenge (currentUser, challenge, userToken) { - try { - if (challenge.legacy.selfService) { - if (!challenge.projectId) { - const selfServiceProjectName = `Self service - ${currentUser.handle} - ${challenge.name}` - challenge.projectId = await helper.createSelfServiceProject(selfServiceProjectName, 'N/A', config.NEW_SELF_SERVICE_PROJECT_TYPE, userToken) +async function createChallenge(currentUser, challenge, userToken) { + await challengeHelper.validateCreateChallengeRequest(currentUser, challenge); + let prizeTypeTmp = challengeHelper.validatePrizeSetsAndGetPrizeType(challenge.prizeSets); + + console.log("TYPE", prizeTypeTmp); + if (challenge.legacy.selfService) { + // if self-service, create a new project (what about if projectId is provided in the payload? confirm with business!) + if (!challenge.projectId && challengeHelper.isProjectIdRequired(challenge.timelineTemplateId)) { + const selfServiceProjectName = `Self service - ${currentUser.handle} - ${challenge.name}`; + challenge.projectId = await helper.createSelfServiceProject( + selfServiceProjectName, + "N/A", + config.NEW_SELF_SERVICE_PROJECT_TYPE, + userToken + ); + } + + if (challenge.metadata && challenge.metadata.length > 0) { + for (const entry of challenge.metadata) { + if (challenge.description.includes(`{{${entry.name}}}`)) { + challenge.description = challenge.description + .split(`{{${entry.name}}}`) + .join(entry.value); + } } - } else if (!challenge.projectId) { - throw new errors.BadRequestError('The projectId is required') } - } catch (e) { - throw new errors.ServiceUnavailableError('Fail to create a self-service project') } - if (challenge.legacy.selfService && challenge.metadata && challenge.metadata.length > 0) { - for (const entry of challenge.metadata) { - if (challenge.description.includes(`{{${entry.name}}}`)) { - challenge.description = challenge.description.split(`{{${entry.name}}}`).join(entry.value) - } - } + + /** Ensure project exists, and set direct project id, billing account id & markup */ + if (challengeHelper.isProjectIdRequired(challenge.timelineTemplateId) || challenge.projectId) { + const { projectId } = challenge; + + const { directProjectId } = await projectHelper.getProject(projectId, currentUser); + const { billingAccountId, markup } = await projectHelper.getProjectBillingInformation( + projectId + ); + + _.set(challenge, "legacy.directProjectId", directProjectId); + _.set(challenge, "billing.billingAccountId", billingAccountId); + _.set(challenge, "billing.markup", markup || 0); } - if (!_.isUndefined(_.get(challenge, 'legacy.reviewType'))) { - _.set(challenge, 'legacy.reviewType', _.toUpper(_.get(challenge, 'legacy.reviewType'))) + + if (!_.isUndefined(_.get(challenge, "legacy.reviewType"))) { + _.set(challenge, "legacy.reviewType", _.toUpper(_.get(challenge, "legacy.reviewType"))); } - challenge.name = xss(challenge.name) - challenge.description = xss(challenge.description) + if (!challenge.status) { - challenge.status = constants.challengeStatuses.New + challenge.status = constants.challengeStatuses.New; } + if (!challenge.startDate) { - challenge.startDate = new Date() - } - if (challenge.status === constants.challengeStatuses.Active) { - throw new errors.BadRequestError('You cannot create an Active challenge. Please create a Draft challenge and then change the status to Active.') - } - const { directProjectId } = await helper.ensureProjectExist(challenge.projectId, currentUser) - if (_.get(challenge, 'legacy.pureV5Task') || _.get(challenge, 'legacy.pureV5')) { - _.set(challenge, 'legacy.directProjectId', directProjectId) - } - const { track, type } = await validateChallengeData(challenge) - const { billingAccountId, markup } = await helper.getProjectBillingInformation(_.get(challenge, 'projectId')) - if (billingAccountId && _.isUndefined(_.get(challenge, 'billing.billingAccountId'))) { - _.set(challenge, 'billing.billingAccountId', billingAccountId) - _.set(challenge, 'billing.markup', markup || 0) + challenge.startDate = new Date().toISOString(); + } else { + challenge.startDate = convertToISOString(challenge.startDate); } - if (_.get(type, 'isTask')) { - _.set(challenge, 'task.isTask', true) - if (_.isUndefined(_.get(challenge, 'task.isAssigned'))) { - _.set(challenge, 'task.isAssigned', false) + + const { track, type } = await challengeHelper.validateAndGetChallengeTypeAndTrack(challenge); + + if (_.get(type, "isTask")) { + _.set(challenge, "task.isTask", true); + // this is only applicable for WorkType: Gig, i.e., Tasks created from Salesforce + if (challenge.billing != null && challenge.billing.clientBillingRate != null) { + _.set(challenge, "billing.clientBillingRate", challenge.billing.clientBillingRate); } - if (_.isUndefined(_.get(challenge, 'task.memberId'))) { - _.set(challenge, 'task.memberId', null) - } else { - throw new errors.BadRequestError(`Cannot assign a member before the challenge gets created.`) + + if (_.isUndefined(_.get(challenge, "task.isAssigned"))) { + _.set(challenge, "task.isAssigned", false); } - } - if (challenge.discussions && challenge.discussions.length > 0) { - for (let i = 0; i < challenge.discussions.length; i += 1) { - challenge.discussions[i].id = uuid() - challenge.discussions[i].name = challenge.discussions[i].name.substring(0, config.FORUM_TITLE_LENGTH_LIMIT) + if (_.isUndefined(_.get(challenge, "task.memberId"))) { + _.set(challenge, "task.memberId", null); + } else { + throw new errors.BadRequestError(`Cannot assign a member before the challenge gets created.`); } } + if (challenge.phases && challenge.phases.length > 0) { - await phaseHelper.validatePhases(challenge.phases) + await phaseHelper.validatePhases(challenge.phases); } - helper.ensureNoDuplicateOrNullElements(challenge.tags, 'tags') - helper.ensureNoDuplicateOrNullElements(challenge.groups, 'groups') - // helper.ensureNoDuplicateOrNullElements(challenge.terms, 'terms') - // helper.ensureNoDuplicateOrNullElements(challenge.events, 'events') - - // check groups authorization - await helper.ensureAccessibleByGroupsAccess(currentUser, challenge) // populate phases if (!challenge.timelineTemplateId) { if (challenge.typeId && challenge.trackId) { - const supportedTemplates = await ChallengeTimelineTemplateService.searchChallengeTimelineTemplates({ - typeId: challenge.typeId, - trackId: challenge.trackId, - isDefault: true - }) - const challengeTimelineTemplate = supportedTemplates.result[0] + const supportedTemplates = + await ChallengeTimelineTemplateService.searchChallengeTimelineTemplates({ + typeId: challenge.typeId, + trackId: challenge.trackId, + isDefault: true, + }); + const challengeTimelineTemplate = supportedTemplates.result[0]; if (!challengeTimelineTemplate) { - throw new errors.BadRequestError(`The selected trackId ${challenge.trackId} and typeId: ${challenge.typeId} does not have a default timeline template. Please provide a timelineTemplateId`) + throw new errors.BadRequestError( + `The selected trackId ${challenge.trackId} and typeId: ${challenge.typeId} does not have a default timeline template. Please provide a timelineTemplateId` + ); } - challenge.timelineTemplateId = challengeTimelineTemplate.timelineTemplateId + challenge.timelineTemplateId = challengeTimelineTemplate.timelineTemplateId; } else { - throw new errors.BadRequestError(`trackId and typeId are required to create a challenge`) + throw new errors.BadRequestError(`trackId and typeId are required to create a challenge`); } } - - if (challenge.timelineTemplateId) { - if (!challenge.phases) { - challenge.phases = [] - } - await phaseHelper.populatePhases(challenge.phases, challenge.startDate, challenge.timelineTemplateId) - } + challenge.phases = await phaseHelper.populatePhasesForChallengeCreation( + challenge.phases, + challenge.startDate, + challenge.timelineTemplateId + ); // populate challenge terms // const projectTerms = await helper.getProjectDefaultTerms(challenge.projectId) // challenge.terms = await helper.validateChallengeTerms(_.union(projectTerms, challenge.terms)) // TODO - challenge terms returned from projects api don't have a role associated // this will need to be updated to associate project terms with a roleId - challenge.terms = await helper.validateChallengeTerms(challenge.terms || []) + challenge.terms = await helper.validateChallengeTerms(challenge.terms || []); // default the descriptionFormat if (!challenge.descriptionFormat) { - challenge.descriptionFormat = 'markdown' + challenge.descriptionFormat = "markdown"; } if (challenge.phases && challenge.phases.length > 0) { - challenge.endDate = helper.calculateChallengeEndDate(challenge) + challenge.endDate = helper.calculateChallengeEndDate(challenge); } - // auto-populate totalPrizes - if (challenge.prizeSets) { - const prizeSetsGroup = _.groupBy(challenge.prizeSets, 'type') - if (prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes]) { - const totalPrizes = helper.sumOfPrizes(prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes][0].prizes) - _.assign(challenge, { overview: { totalPrizes } }) - } - } + if (challenge.events == null) challenge.events = []; + if (challenge.attachments == null) challenge.attachments = []; + if (challenge.prizeSets == null) challenge.prizeSets = []; + if (challenge.metadata == null) challenge.metadata = []; + if (challenge.groups == null) challenge.groups = []; + if (challenge.tags == null) challenge.tags = []; + if (challenge.startDate != null) challenge.startDate = challenge.startDate; + if (challenge.endDate != null) challenge.endDate = challenge.endDate; + if (challenge.discussions == null) challenge.discussions = []; + if (challenge.skills == null) challenge.skills = []; - const ret = await helper.create('Challenge', _.assign({ - id: uuid(), - created: moment().utc(), - createdBy: currentUser.handle || currentUser.sub, - updated: moment().utc(), - updatedBy: currentUser.handle || currentUser.sub - }, challenge)) - ret.numOfSubmissions = 0 - ret.numOfRegistrants = 0 - if (ret.phases && ret.phases.length > 0) { - const registrationPhase = _.find(ret.phases, p => p.name === 'Registration') - const submissionPhase = _.find(ret.phases, p => p.name === 'Submission') - ret.currentPhaseNames = _.map(_.filter(ret.phases, p => p.isOpen === true), 'name') - if (registrationPhase) { - ret.registrationStartDate = registrationPhase.actualStartDate || registrationPhase.scheduledStartDate - ret.registrationEndDate = registrationPhase.actualEndDate || registrationPhase.scheduledEndDate - } - if (submissionPhase) { - ret.submissionStartDate = submissionPhase.actualStartDate || submissionPhase.scheduledStartDate - ret.submissionEndDate = submissionPhase.actualEndDate || submissionPhase.scheduledEndDate - } - } + challenge.metadata = challenge.metadata.map((m) => ({ + name: m.name, + value: typeof m.value === "string" ? m.value : JSON.stringify(m.value), + })); + + const grpcMetadata = new GrpcMetadata(); + + grpcMetadata.set("handle", currentUser.handle); + grpcMetadata.set("userId", currentUser.userId); + grpcMetadata.set("token", await getM2MToken()); - if (track) { - ret.track = track.name + const prizeType = challengeHelper.validatePrizeSetsAndGetPrizeType(challenge.prizeSets); + + if (prizeType === constants.prizeTypes.USD) { + convertPrizeSetValuesToCents(challenge.prizeSets); } - if (type) { - ret.type = type.name + + const ret = await challengeDomain.create(challenge, grpcMetadata); + + if (prizeType === constants.prizeTypes.USD) { + convertPrizeSetValuesToDollars(ret.prizeSets, ret.overview); } + ret.numOfSubmissions = 0; + ret.numOfRegistrants = 0; + + enrichChallengeForResponse(ret, track, type); + // Create in ES await esClient.create({ - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), - refresh: config.get('ES.ES_REFRESH'), + index: config.get("ES.ES_INDEX"), + type: config.get("ES.OPENSEARCH") == "false" ? config.get("ES.ES_TYPE") : undefined, + refresh: config.get("ES.ES_REFRESH"), id: ret.id, - body: ret - }) + body: ret, + }); // If the challenge is self-service, add the creating user as the "client manager", *not* the manager // This is necessary for proper handling of the vanilla embed on the self-service work item dashboard if (challenge.legacy.selfService) { if (currentUser.handle) { - await helper.createResource(ret.id, ret.createdBy, config.CLIENT_MANAGER_ROLE_ID) + await helper.createResource(ret.id, ret.createdBy, config.CLIENT_MANAGER_ROLE_ID); } } else { - // if created by a user, add user as a manager, but only if *not* a self-service challenge if (currentUser.handle) { - // logger.debug(`Adding user as manager ${currentUser.handle}`) - await helper.createResource(ret.id, ret.createdBy, config.MANAGER_ROLE_ID) - } else { - // logger.debug(`Not adding manager ${currentUser.sub} ${JSON.stringify(currentUser)}`) + await helper.createResource(ret.id, ret.createdBy, config.MANAGER_ROLE_ID); } } // post bus event - await helper.postBusEvent(constants.Topics.ChallengeCreated, ret) - return ret -} + await helper.postBusEvent(constants.Topics.ChallengeCreated, ret); + return ret; +} createChallenge.schema = { currentUser: Joi.any(), - challenge: Joi.object().keys({ - typeId: Joi.id(), - trackId: Joi.id(), - legacy: Joi.object().keys({ - reviewType: Joi.string().valid(_.values(constants.reviewTypes)).insensitive().default(constants.reviewTypes.Internal), - confidentialityType: Joi.string().default(config.DEFAULT_CONFIDENTIALITY_TYPE), - forumId: Joi.number().integer(), - directProjectId: Joi.number().integer(), - screeningScorecardId: Joi.number().integer(), - reviewScorecardId: Joi.number().integer(), - isTask: Joi.boolean(), - useSchedulingAPI: Joi.boolean(), - pureV5Task: Joi.boolean(), - pureV5: Joi.boolean(), - selfService: Joi.boolean(), - selfServiceCopilot: Joi.string() - }), - billing: Joi.object().keys({ - billingAccountId: Joi.string(), - markup: Joi.number().min(0).max(100) - }).unknown(true), - task: Joi.object().keys({ - isTask: Joi.boolean().default(false), - isAssigned: Joi.boolean().default(false), - memberId: Joi.string().allow(null) - }), - name: Joi.string().required(), - description: Joi.string(), - privateDescription: Joi.string(), - descriptionFormat: Joi.string(), - metadata: Joi.array().items(Joi.object().keys({ - name: Joi.string().required(), - value: Joi.required() - })).unique((a, b) => a.name === b.name), - timelineTemplateId: Joi.string(), // Joi.optionalId(), - phases: Joi.array().items(Joi.object().keys({ - phaseId: Joi.id(), - duration: Joi.number().integer().min(0), - constraints: Joi.object().keys({ - name: Joi.string(), - value: Joi.number().integer().min(0) - }).optional() - })), - events: Joi.array().items(Joi.object().keys({ - id: Joi.number().required(), - name: Joi.string(), - key: Joi.string() - })), - discussions: Joi.array().items(Joi.object().keys({ - id: Joi.optionalId(), + challenge: Joi.object() + .keys({ + typeId: Joi.id(), + trackId: Joi.id(), + legacy: Joi.object().keys({ + reviewType: Joi.string() + .valid(_.values(constants.reviewTypes)) + .insensitive() + .default(constants.reviewTypes.Internal), + confidentialityType: Joi.string().default(config.DEFAULT_CONFIDENTIALITY_TYPE), + forumId: Joi.number().integer(), + directProjectId: Joi.number().integer(), + screeningScorecardId: Joi.number().integer(), + reviewScorecardId: Joi.number().integer(), + isTask: Joi.boolean(), + useSchedulingAPI: Joi.boolean(), + pureV5Task: Joi.boolean(), + pureV5: Joi.boolean(), + selfService: Joi.boolean(), + selfServiceCopilot: Joi.string(), + }), + billing: Joi.object() + .keys({ + billingAccountId: Joi.string(), + markup: Joi.number().min(0).max(100), + clientBillingRate: Joi.number().min(0).max(100), + }) + .unknown(true), + task: Joi.object().keys({ + isTask: Joi.boolean().default(false), + isAssigned: Joi.boolean().default(false), + memberId: Joi.string().allow(null), + }), name: Joi.string().required(), - type: Joi.string().required().valid(_.values(constants.DiscussionTypes)), - provider: Joi.string().required(), - url: Joi.string(), - options: Joi.array().items(Joi.object()) - })), - prizeSets: Joi.array().items(Joi.object().keys({ - type: Joi.string().valid(_.values(constants.prizeSetTypes)).required(), description: Joi.string(), - prizes: Joi.array().items(Joi.object().keys({ - description: Joi.string(), - type: Joi.string().required(), - value: Joi.number().min(0).required() - })).min(1).required() - })), - tags: Joi.array().items(Joi.string()), // tag names - projectId: Joi.number().integer().positive(), - legacyId: Joi.number().integer().positive(), - startDate: Joi.date(), - status: Joi.string().valid(_.values(constants.challengeStatuses)), - groups: Joi.array().items(Joi.optionalId()).unique(), - // gitRepoURLs: Joi.array().items(Joi.string().uri()), - terms: Joi.array().items(Joi.object().keys({ - id: Joi.id(), - roleId: Joi.id() - })) - }).required(), - userToken: Joi.string().required() -} - + privateDescription: Joi.string(), + descriptionFormat: Joi.string(), + metadata: Joi.array() + .items( + Joi.object().keys({ + name: Joi.string().required(), + value: Joi.required(), + }) + ) + .unique((a, b) => a.name === b.name), + timelineTemplateId: Joi.string(), // Joi.optionalId(), + phases: Joi.array().items( + Joi.object().keys({ + phaseId: Joi.id(), + duration: Joi.number().integer().min(0), + constraints: Joi.array() + .items( + Joi.object() + .keys({ + name: Joi.string(), + value: Joi.number().integer().min(0), + }) + .optional() + ) + .optional(), + }) + ), + events: Joi.array().items( + Joi.object().keys({ + id: Joi.number().required(), + name: Joi.string(), + key: Joi.string(), + }) + ), + discussions: Joi.array().items( + Joi.object().keys({ + id: Joi.optionalId(), + name: Joi.string().required(), + type: Joi.string().required().valid(_.values(constants.DiscussionTypes)), + provider: Joi.string().required(), + url: Joi.string(), + options: Joi.array().items(Joi.object()), + }) + ), + prizeSets: Joi.array().items( + Joi.object().keys({ + type: Joi.string().valid(_.values(constants.prizeSetTypes)).required(), + description: Joi.string(), + prizes: Joi.array() + .items( + Joi.object().keys({ + description: Joi.string(), + type: Joi.string().required(), + value: Joi.number().min(0).required(), + }) + ) + .min(1) + .required(), + }) + ), + tags: Joi.array().items(Joi.string()), // tag names + projectId: Joi.number().integer().positive(), + legacyId: Joi.number().integer().positive(), + constraints: Joi.object() + .keys({ + allowedRegistrants: Joi.array().items(Joi.string().trim().lowercase()).optional(), + }) + .optional(), + startDate: Joi.date().iso(), + status: Joi.string().valid([ + constants.challengeStatuses.Active, + constants.challengeStatuses.New, + constants.challengeStatuses.Draft, + constants.challengeStatuses.Approved, + ]), + groups: Joi.array().items(Joi.optionalId()).unique(), + // gitRepoURLs: Joi.array().items(Joi.string().uri()), + terms: Joi.array().items( + Joi.object().keys({ + id: Joi.id(), + roleId: Joi.id(), + }) + ), + skills: Joi.array() + .items( + Joi.object() + .keys({ + id: Joi.id(), + }) + .unknown(true) + ) + .optional(), + }) + .required(), + userToken: Joi.string().required(), +}; /** * Populate phase data from phase API. * @param {Object} the challenge entity */ -async function getPhasesAndPopulate (data) { - _.each(data.phases, async p => { - const phase = await PhaseService.getPhase(p.phaseId) - p.name = phase.name +async function getPhasesAndPopulate(data) { + _.each(data.phases, async (p) => { + const phase = await phaseHelper.getPhase(p.phaseId); + p.name = phase.name; if (phase.description) { - p.description = phase.description + p.description = phase.description; } - }) + }); } /** @@ -1184,80 +1198,79 @@ async function getPhasesAndPopulate (data) { * @param {Boolean} checkIfExists flag to check if challenge exists * @returns {Object} the challenge with given id */ -async function getChallenge (currentUser, id, checkIfExists) { - // get challenge from Elasticsearch - let challenge - // logger.warn(JSON.stringify({ - // index: config.get('ES.ES_INDEX'), - // type: config.get('ES.ES_TYPE'), - // _id: id - // })) +async function getChallenge(currentUser, id, checkIfExists) { + let challenge; try { - challenge = await esClient.getSource({ - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), - id - }) + if (config.get("ES.OPENSEARCH") == "true") { + challenge = ( + await esClient.getSource({ + index: config.get("ES.ES_INDEX"), + id, + }) + ).body; + } else { + challenge = await esClient.getSource({ + index: config.get("ES.ES_INDEX"), + type: config.get("ES.ES_TYPE"), + id, + }); + } } catch (e) { if (e.statusCode === HttpStatus.NOT_FOUND) { - throw new errors.NotFoundError(`Challenge of id ${id} is not found.`) + throw new errors.NotFoundError(`Challenge of id ${id} is not found.`); } else { - throw e + throw e; } } if (checkIfExists) { - return _.pick(challenge, ['id', 'legacyId']) - } - await helper.ensureUserCanViewChallenge(currentUser, challenge) - - // // FIXME: Temporarily hard coded as the migrad - // // populate type property based on the typeId - // if (challenge.typeId) { - // try { - // const type = await helper.getById('ChallengeType', challenge.typeId) - // challenge.type = type.name - // } catch (e) { - // challenge.typeId = '45415132-79fa-4d13-a9ac-71f50020dc10' // TODO Fix HardCode - // const type = await helper.getById('ChallengeType', challenge.typeId) - // challenge.type = type.name - // } - // } - // delete challenge.typeId + return _.pick(challenge, ["id", "legacyId"]); + } + await helper.ensureUserCanViewChallenge(currentUser, challenge); // Remove privateDescription for unregistered users if (currentUser) { - if (!currentUser.isMachine && !helper.hasAdminRole(currentUser)) { - _.unset(challenge, 'billing') + if (!currentUser.isMachine && !hasAdminRole(currentUser)) { + _.unset(challenge, "billing"); if (_.isEmpty(challenge.privateDescription)) { - _.unset(challenge, 'privateDescription') - } else if (!_.get(challenge, 'task.isTask', false) || !_.get(challenge, 'task.isAssigned', false)) { - const memberResources = await helper.listResourcesByMemberAndChallenge(currentUser.userId, challenge.id) + _.unset(challenge, "privateDescription"); + } else if ( + !_.get(challenge, "task.isTask", false) || + !_.get(challenge, "task.isAssigned", false) + ) { + const memberResources = await helper.listResourcesByMemberAndChallenge( + currentUser.userId, + challenge.id + ); if (_.isEmpty(memberResources)) { - _.unset(challenge, 'privateDescription') + _.unset(challenge, "privateDescription"); } } } } else { - _.unset(challenge, 'billing') - _.unset(challenge, 'privateDescription') + _.unset(challenge, "billing"); + _.unset(challenge, "privateDescription"); } if (challenge.phases && challenge.phases.length > 0) { - await getPhasesAndPopulate(challenge) + await getPhasesAndPopulate(challenge); } if (challenge.status !== constants.challengeStatuses.Completed) { - _.unset(challenge, 'winners') + _.unset(challenge, "winners"); } - return challenge -} + // TODO: in the long run we wanna do a finer grained filtering of the payments + if (!hasAdminRole(currentUser) && !_.get(currentUser, "isMachine", false)) { + _.unset(challenge, "payments"); + } + return challenge; +} getChallenge.schema = { currentUser: Joi.any(), id: Joi.id(), - checkIfExists: Joi.boolean() -} + checkIfExists: Joi.boolean(), +}; /** * Get challenge statistics @@ -1265,42 +1278,41 @@ getChallenge.schema = { * @param {String} id the challenge id * @returns {Object} the challenge with given id */ -async function getChallengeStatistics (currentUser, id) { - const challenge = await getChallenge(currentUser, id) - // for now, only Data Science challenges are supported - if (challenge.type !== 'Challenge' && challenge.track !== 'Data Science') { - throw new errors.BadRequestError(`Challenge of type ${challenge.type} and track ${challenge.track} does not support statistics`) - } +async function getChallengeStatistics(currentUser, id) { + const challenge = await getChallenge(currentUser, id); // get submissions - const submissions = await helper.getChallengeSubmissions(challenge.id) + const submissions = await helper.getChallengeSubmissions(challenge.id); // for each submission, load member profile - const map = {} + const map = {}; for (const submission of submissions) { if (!map[submission.memberId]) { // Load member profile and cache - const member = await helper.getMemberById(submission.memberId) + const member = await helper.getMemberById(submission.memberId); map[submission.memberId] = { photoUrl: member.photoURL, - rating: _.get(member, 'maxRating.rating', 0), - ratingColor: _.get(member, 'maxRating.ratingColor', '#9D9FA0'), + rating: _.get(member, "maxRating.rating", 0), + ratingColor: _.get(member, "maxRating.ratingColor", "#9D9FA0"), homeCountryCode: member.homeCountryCode, handle: member.handle, - submissions: [] - } + submissions: [], + }; } // add submission map[submission.memberId].submissions.push({ created: submission.created, - score: _.get(_.find(submission.review || [], r => r.metadata), 'score', 0) - }) - } - return _.map(_.keys(map), (userId) => map[userId]) + score: _.get( + _.find(submission.review || [], (r) => r.metadata), + "score", + 0 + ), + }); + } + return _.map(_.keys(map), (userId) => map[userId]); } - getChallengeStatistics.schema = { currentUser: Joi.any(), - id: Joi.id() -} + id: Joi.id(), +}; /** * Check whether given two PrizeSet Array are different. @@ -1308,660 +1320,562 @@ getChallengeStatistics.schema = { * @param {Array} otherPrizeSets the second PrizeSet Array * @returns {Boolean} true if different, false otherwise */ -function isDifferentPrizeSets (prizeSets = [], otherPrizeSets = []) { - return !_.isEqual(_.sortBy(prizeSets, 'type'), _.sortBy(otherPrizeSets, 'type')) +function isDifferentPrizeSets(prizeSets = [], otherPrizeSets = []) { + return !_.isEqual(_.sortBy(prizeSets, "type"), _.sortBy(otherPrizeSets, "type")); } /** * Validate the winners array. * @param {Array} winners the Winner Array - * @param {String} winchallengeIdners the challenge ID + * @param {Array} challengeResources the challenge resources */ -async function validateWinners (winners, challengeId) { - const challengeResources = await helper.getChallengeResources(challengeId) - const registrants = _.filter(challengeResources, r => r.roleId === config.SUBMITTER_ROLE_ID) +async function validateWinners(winners, challengeResources) { + const registrants = _.filter(challengeResources, (r) => r.roleId === config.SUBMITTER_ROLE_ID); for (const prizeType of _.values(constants.prizeSetTypes)) { - const filteredWinners = _.filter(winners, w => w.type === prizeType) + const filteredWinners = _.filter(winners, (w) => w.type === prizeType); for (const winner of filteredWinners) { - if (!_.find(registrants, r => _.toString(r.memberId) === _.toString(winner.userId))) { - throw new errors.BadRequestError(`Member with userId: ${winner.userId} is not registered on the challenge`) + if (!_.find(registrants, (r) => _.toString(r.memberId) === _.toString(winner.userId))) { + throw new errors.BadRequestError( + `Member with userId: ${winner.userId} is not registered on the challenge` + ); } - const diffWinners = _.differenceWith(filteredWinners, [winner], _.isEqual) + const diffWinners = _.differenceWith(filteredWinners, [winner], _.isEqual); if (diffWinners.length + 1 !== filteredWinners.length) { - throw new errors.BadRequestError(`Duplicate member with placement: ${helper.toString(winner)}`) + throw new errors.BadRequestError( + `Duplicate member with placement: ${helper.toString(winner)}` + ); } // find another member with the placement - const placementExists = _.find(diffWinners, function (w) { return w.placement === winner.placement }) - if (placementExists && (placementExists.userId !== winner.userId || placementExists.handle !== winner.handle)) { - throw new errors.BadRequestError(`Only one member can have a placement: ${winner.placement}`) + const placementExists = _.find(diffWinners, function (w) { + return w.placement === winner.placement; + }); + if ( + placementExists && + (placementExists.userId !== winner.userId || placementExists.handle !== winner.handle) + ) { + throw new errors.BadRequestError( + `Only one member can have a placement: ${winner.placement}` + ); } // find another placement for a member - const memberExists = _.find(diffWinners, function (w) { return w.userId === winner.userId && w.type === winner.type }) + const memberExists = _.find(diffWinners, function (w) { + return w.userId === winner.userId && w.type === winner.type; + }); if (memberExists && memberExists.placement !== winner.placement) { - throw new errors.BadRequestError(`The same member ${winner.userId} cannot have multiple placements`) + throw new errors.BadRequestError( + `The same member ${winner.userId} cannot have multiple placements` + ); } } } } /** - * Update challenge. + * Task shouldn't be launched/completed when it is assigned to the current user self. + * E.g: stop copilots from paying themselves, thus copilots will need to contact manager to launch/complete the task. * @param {Object} currentUser the user who perform operation - * @param {String} challengeId the challenge id - * @param {Object} data the challenge data to be updated - * @param {Boolean} isFull the flag indicate it is a fully update operation. - * @returns {Object} the updated challenge + * @param {Object} challenge the existing challenge + * @param {Object} data the new input challenge data + * @param {Array} challengeResources the challenge resources */ -async function update (currentUser, challengeId, data, isFull) { - const cancelReason = _.cloneDeep(data.cancelReason) - let sendActivationEmail = false - let sendSubmittedEmail = false - let sendCompletedEmail = false - let sendRejectedEmail = false - delete data.cancelReason - if (!_.isUndefined(_.get(data, 'legacy.reviewType'))) { - _.set(data, 'legacy.reviewType', _.toUpper(_.get(data, 'legacy.reviewType'))) - } - if (data.projectId) { - await helper.ensureProjectExist(data.projectId, currentUser) - } - - helper.ensureNoDuplicateOrNullElements(data.tags, 'tags') - helper.ensureNoDuplicateOrNullElements(data.groups, 'groups') - // helper.ensureNoDuplicateOrNullElements(data.gitRepoURLs, 'gitRepoURLs') - - const challenge = await helper.getById('Challenge', challengeId) - let dynamicDescription = _.cloneDeep(data.description || challenge.description) - if (challenge.legacy.selfService && data.metadata && data.metadata.length > 0) { - for (const entry of data.metadata) { - const regexp = new RegExp(`{{${entry.name}}}`, 'g') - dynamicDescription = dynamicDescription.replace(regexp, entry.value) - } - data.description = dynamicDescription - } - if (challenge.legacy.selfService && data.status === constants.challengeStatuses.Draft && challenge.status !== constants.challengeStatuses.Draft) { - sendSubmittedEmail = true - } - // check if it's a self service challenge and project needs to be activated first - if (challenge.legacy.selfService && (data.status === constants.challengeStatuses.Approved || data.status === constants.challengeStatuses.Active) && challenge.status !== constants.challengeStatuses.Active) { - try { - const selfServiceProjectName = `Self service - ${challenge.createdBy} - ${challenge.name}` - const workItemSummary = _.get(_.find(_.get(challenge, 'metadata', []), m => m.name === 'websitePurpose.description'), 'value', 'N/A') - await helper.activateProject(challenge.projectId, currentUser, selfServiceProjectName, workItemSummary) - if (data.status === constants.challengeStatuses.Active) { - sendActivationEmail = true - } - } catch (e) { - await update( - currentUser, - challengeId, - { - ...data, - status: constants.challengeStatuses.CancelledPaymentFailed, - cancelReason: `Failed to activate project. Error: ${e.message}. JSON: ${JSON.stringify(e)}` - }, - false - ) - throw new errors.BadRequestError('Failed to activate the challenge! The challenge has been canceled!') - } +function validateTask(currentUser, challenge, data, challengeResources) { + if (!_.get(challenge, "legacy.pureV5Task")) { + // Not a Task + return; } - const { billingAccountId, markup } = await helper.getProjectBillingInformation(_.get(challenge, 'projectId')) - if (billingAccountId && _.isUndefined(_.get(challenge, 'billing.billingAccountId'))) { - _.set(data, 'billing.billingAccountId', billingAccountId) - _.set(data, 'billing.markup', markup || 0) + // Status changed to Active, indicating launch a Task + const isLaunchTask = + data.status === constants.challengeStatuses.Active && + challenge.status !== constants.challengeStatuses.Active; + + // Status changed to Completed, indicating complete a Task + const isCompleteTask = + data.status === constants.challengeStatuses.Completed && + challenge.status !== constants.challengeStatuses.Completed; + + // When complete a Task, input data should have winners + if (isCompleteTask && (!data.winners || !data.winners.length)) { + throw new errors.BadRequestError("The winners is required to complete a Task"); } - if (billingAccountId && _.includes(config.TOPGEAR_BILLING_ACCOUNTS_ID, _.toString(billingAccountId))) { - if (_.isEmpty(data.metadata)) { - data.metadata = [] - } - if (!_.find(data.metadata, e => e.name === 'postMortemRequired')) { - data.metadata.push({ - name: 'postMortemRequired', - value: 'false' - }) + + if (!currentUser.isMachine && (isLaunchTask || isCompleteTask)) { + // Whether task is assigned to current user + const assignedToCurrentUser = + _.filter( + challengeResources, + (r) => + r.roleId === config.SUBMITTER_ROLE_ID && + _.toString(r.memberId) === _.toString(currentUser.userId) + ).length > 0; + + if (assignedToCurrentUser) { + throw new errors.ForbiddenError( + `You are not allowed to ${ + data.status === constants.challengeStatuses.Active ? "lanuch" : "complete" + } task assigned to yourself. Please contact manager to operate.` + ); } } - if (data.status) { - if (data.status === constants.challengeStatuses.Active) { - if (!_.get(challenge, 'legacy.pureV5Task') && !_.get(challenge, 'legacy.pureV5') && _.isUndefined(_.get(challenge, 'legacyId'))) { - throw new errors.BadRequestError('You cannot activate the challenge as it has not been created on legacy yet. Please try again later or contact support.') - } - // if activating a challenge, the challenge must have a billing account id - if ((!billingAccountId || billingAccountId === null) && - challenge.status === constants.challengeStatuses.Draft) { - throw new errors.BadRequestError('Cannot Activate this project, it has no active billing account.') +} + +/** + * Update challenge. + * @param {Object} currentUser the user who perform operation + * @param {String} challengeId the challenge id + * @param {Object} data the challenge data to be updated + * @param {Boolean} isFull the flag indicate it is a fully update operation. + * @returns {Object} the updated challenge + */ +async function updateChallenge(currentUser, challengeId, data) { + const challenge = await challengeDomain.lookup(getLookupCriteria("id", challengeId)); + const existingPrizeType = challengeHelper.validatePrizeSetsAndGetPrizeType(challenge.prizeSets); + + if (existingPrizeType === constants.prizeTypes.USD) { + convertPrizeSetValuesToDollars(challenge.prizeSets, challenge.overview); + } + + let projectId, billingAccountId, markup; + if (challengeHelper.isProjectIdRequired(challenge.timelineTemplateId)) { + projectId = _.get(challenge, "projectId"); + + ({ billingAccountId, markup } = await projectHelper.getProjectBillingInformation(projectId)); + + if (billingAccountId && _.isUndefined(_.get(challenge, "billing.billingAccountId"))) { + _.set(data, "billing.billingAccountId", billingAccountId); + _.set(data, "billing.markup", markup || 0); + } + + // Make sure the user cannot change the direct project ID + if (data.legacy) { + data.legacy = _.assign({}, challenge.legacy, data.legacy); + _.set(data, "legacy.directProjectId", challenge.legacy.directProjectId); + } + } + + // Remove fields from data that are not allowed to be updated and that match the existing challenge + data = sanitizeData(sanitizeChallenge(data), challenge); + logger.debug(`Sanitized Data: ${JSON.stringify(data)}`); + + const challengeResources = await helper.getChallengeResources(challengeId); + + await challengeHelper.validateChallengeUpdateRequest( + currentUser, + challenge, + data, + challengeResources + ); + validateTask(currentUser, challenge, data, challengeResources); + + let sendActivationEmail = false; + let sendSubmittedEmail = false; + let sendCompletedEmail = false; + let sendRejectedEmail = false; + + /* BEGIN self-service stuffs */ + + // TODO: At some point in the future this should be moved to a Self-Service Challenge Helper + + if (challenge.legacy.selfService) { + // prettier-ignore + sendSubmittedEmail = data.status === constants.challengeStatuses.Draft && challenge.status !== constants.challengeStatuses.Draft; + + if (data.metadata && data.metadata.length > 0) { + let dynamicDescription = _.cloneDeep(data.description || challenge.description); + for (const entry of data.metadata) { + const regexp = new RegExp(`{{${entry.name}}}`, "g"); + dynamicDescription = dynamicDescription.replace(regexp, entry.value); } + data.description = dynamicDescription; } - if (data.status === constants.challengeStatuses.CancelledRequirementsInfeasible || data.status === constants.challengeStatuses.CancelledPaymentFailed) { + + // check if it's a self service challenge and project needs to be activated first + if ( + (data.status === constants.challengeStatuses.Approved || + data.status === constants.challengeStatuses.Active) && + challenge.status !== constants.challengeStatuses.Active && + challengeHelper.isProjectIdRequired(challenge.timelineTemplateId) + ) { try { - await helper.cancelProject(challenge.projectId, cancelReason, currentUser) + const selfServiceProjectName = `Self service - ${challenge.createdBy} - ${challenge.name}`; + const workItemSummary = _.get( + _.find(_.get(challenge, "metadata", []), (m) => m.name === "websitePurpose.description"), + "value", + "N/A" + ); + await helper.activateProject( + projectId, + currentUser, + selfServiceProjectName, + workItemSummary + ); + + sendActivationEmail = data.status === constants.challengeStatuses.Active; } catch (e) { - logger.debug(`There was an error trying to cancel the project: ${e.message}`) + await updateChallenge( + currentUser, + challengeId, + { + ...data, + status: constants.challengeStatuses.CancelledPaymentFailed, + cancelReason: `Failed to activate project. Error: ${e.message}. JSON: ${JSON.stringify( + e + )}`, + }, + false + ); + throw new errors.BadRequestError( + "Failed to activate the challenge! The challenge has been canceled!" + ); } - sendRejectedEmail = true } - if (data.status === constants.challengeStatuses.Completed) { - if (!_.get(challenge, 'legacy.pureV5Task') && !_.get(challenge, 'legacy.pureV5') && challenge.status !== constants.challengeStatuses.Active) { - throw new errors.BadRequestError('You cannot mark a Draft challenge as Completed') + + if ( + data.status === constants.challengeStatuses.Draft && + challengeHelper.isProjectIdRequired(challenge.timelineTemplateId) + ) { + try { + await helper.updateSelfServiceProjectInfo( + projectId, + data.endDate || challenge.endDate, + currentUser + ); + } catch (e) { + logger.debug(`There was an error trying to update the project: ${e.message}`); } - sendCompletedEmail = true } - } - // FIXME: Tech Debt - if (_.get(challenge, 'legacy.track') && _.get(data, 'legacy.track') && _.get(challenge, 'legacy.track') !== _.get(data, 'legacy.track')) { - throw new errors.ForbiddenError('Cannot change legacy.track') - } - if (_.get(challenge, 'trackId') && _.get(data, 'trackId') && _.get(challenge, 'trackId') !== _.get(data, 'trackId')) { - throw new errors.ForbiddenError('Cannot change trackId') - } - if (_.get(challenge, 'typeId') && _.get(data, 'typeId') && _.get(challenge, 'typeId') !== _.get(data, 'typeId')) { - throw new errors.ForbiddenError('Cannot change typeId') - } - if (_.get(challenge, 'legacy.useSchedulingAPI') && _.get(data, 'legacy.useSchedulingAPI') && _.get(challenge, 'legacy.useSchedulingAPI') !== _.get(data, 'legacy.useSchedulingAPI')) { - throw new errors.ForbiddenError('Cannot change legacy.useSchedulingAPI') - } - if (_.get(challenge, 'legacy.pureV5Task') && _.get(data, 'legacy.pureV5Task') && _.get(challenge, 'legacy.pureV5Task') !== _.get(data, 'legacy.pureV5Task')) { - throw new errors.ForbiddenError('Cannot change legacy.pureV5Task') - } - if (_.get(challenge, 'legacy.pureV5') && _.get(data, 'legacy.pureV5') && _.get(challenge, 'legacy.pureV5') !== _.get(data, 'legacy.pureV5')) { - throw new errors.ForbiddenError('Cannot change legacy.pureV5') - } - if (_.get(challenge, 'legacy.selfService') && _.get(data, 'legacy.selfService') && _.get(challenge, 'legacy.selfService') !== _.get(data, 'legacy.selfService')) { - throw new errors.ForbiddenError('Cannot change legacy.selfService') + if ( + (data.status === constants.challengeStatuses.CancelledRequirementsInfeasible || + data.status === constants.challengeStatuses.CancelledPaymentFailed) && + challengeHelper.isProjectIdRequired(challenge.timelineTemplateId) + ) { + try { + await helper.cancelProject(challenge.projectId, data.cancelReason, currentUser); + } catch (e) { + logger.debug(`There was an error trying to cancel the project: ${e.message}`); + } + sendRejectedEmail = true; + } } - if (!_.isUndefined(challenge.legacy) && !_.isUndefined(data.legacy)) { - _.extend(challenge.legacy, data.legacy) - } + /* END self-service stuffs */ - if (!_.isUndefined(challenge.billing) && !_.isUndefined(data.billing)) { - _.extend(challenge.billing, data.billing) - } else if (_.isUndefined(challenge.billing) && !_.isUndefined(data.billing)) { - challenge.billing = data.billing - } + let isChallengeBeingActivated = false; + let isChallengeBeingCancelled = false; + if (data.status) { + if (data.status === constants.challengeStatuses.Active) { + // if activating a challenge, the challenge must have a billing account id + if ( + (!billingAccountId || billingAccountId === null) && + challenge.status === constants.challengeStatuses.Draft && + challengeHelper.isProjectIdRequired(challenge.timelineTemplateId) + ) { + throw new errors.BadRequestError( + "Cannot Activate this project, it has no active billing account." + ); + } + if (challenge.status === constants.challengeStatuses.Draft) { + isChallengeBeingActivated = true; + } + } - await helper.ensureUserCanModifyChallenge(currentUser, challenge) + if ( + _.includes( + [ + constants.challengeStatuses.Cancelled, + constants.challengeStatuses.CancelledRequirementsInfeasible, + constants.challengeStatuses.CancelledPaymentFailed, + constants.challengeStatuses.CancelledFailedReview, + constants.challengeStatuses.CancelledFailedScreening, + constants.challengeStatuses.CancelledZeroSubmissions, + constants.challengeStatuses.CancelledWinnerUnresponsive, + constants.challengeStatuses.CancelledClientRequest, + constants.challengeStatuses.CancelledZeroRegistrations, + ], + data.status + ) + ) { + isChallengeBeingCancelled = true; + } - // check groups access to be updated group values - if (data.groups) { - await ensureAcessibilityToModifiedGroups(currentUser, data, challenge) - } - let newAttachments - if (isFull || !_.isUndefined(data.attachments)) { - newAttachments = data.attachments + if (data.status === constants.challengeStatuses.Completed) { + if ( + !_.get(challenge, "legacy.pureV5Task") && + !_.get(challenge, "legacy.pureV5") && + challenge.status !== constants.challengeStatuses.Active + ) { + throw new errors.BadRequestError("You cannot mark a Draft challenge as Completed"); + } + sendCompletedEmail = true; + } } - await ensureAccessibleForChallenge(currentUser, challenge) - // Only M2M can update url and options of discussions if (data.discussions && data.discussions.length > 0) { if (challenge.discussions && challenge.discussions.length > 0) { for (let i = 0; i < data.discussions.length; i += 1) { if (_.isUndefined(data.discussions[i].id)) { - data.discussions[i].id = uuid() + data.discussions[i].id = uuid(); if (!currentUser.isMachine) { - _.unset(data.discussions, 'url') - _.unset(data.discussions, 'options') + _.unset(data.discussions, "url"); + _.unset(data.discussions, "options"); } } else if (!currentUser.isMachine) { - const existingDiscussion = _.find(_.get(challenge, 'discussions', []), d => d.id === data.discussions[i].id) + const existingDiscussion = _.find( + _.get(challenge, "discussions", []), + (d) => d.id === data.discussions[i].id + ); if (existingDiscussion) { - _.assign(data.discussions[i], _.pick(existingDiscussion, ['url', 'options'])) + _.assign(data.discussions[i], _.pick(existingDiscussion, ["url", "options"])); } else { - _.unset(data.discussions, 'url') - _.unset(data.discussions, 'options') + _.unset(data.discussions, "url"); + _.unset(data.discussions, "options"); } } } } else { for (let i = 0; i < data.discussions.length; i += 1) { - data.discussions[i].id = uuid() - data.discussions[i].name = data.discussions[i].name.substring(0, config.FORUM_TITLE_LENGTH_LIMIT) + data.discussions[i].id = uuid(); + data.discussions[i].name = data.discussions[i].name.substring( + 0, + config.FORUM_TITLE_LENGTH_LIMIT + ); } } } - // Validate the challenge terms - let newTermsOfUse - if (!_.isUndefined(data.terms)) { - // helper.ensureNoDuplicateOrNullElements(data.terms, 'terms') - - // Get the project default terms - const defaultTerms = await helper.getProjectDefaultTerms(challenge.projectId) - - if (defaultTerms) { - // Make sure that the default project terms were not removed - // TODO - there are no default terms returned by v5 - // the terms array is objects with a roleId now, so this _.difference won't work - // const removedTerms = _.difference(defaultTerms, data.terms) - // if (removedTerms.length !== 0) { - // throw new errors.BadRequestError(`Default project terms ${removedTerms} should not be removed`) - // } - } - // newTermsOfUse = await helper.validateChallengeTerms(_.union(data.terms, defaultTerms)) - newTermsOfUse = await helper.validateChallengeTerms(data.terms) - } - - await validateChallengeData(data) - if ((challenge.status === constants.challengeStatuses.Completed || challenge.status === constants.challengeStatuses.Cancelled) && data.status && (data.status !== challenge.status && data.status !== constants.challengeStatuses.CancelledClientRequest)) { - throw new errors.BadRequestError(`Cannot change ${challenge.status} challenge status to ${data.status} status`) - } - - if (data.winners && data.winners.length > 0 && (challenge.status !== constants.challengeStatuses.Completed && data.status !== constants.challengeStatuses.Completed)) { - throw new errors.BadRequestError(`Cannot set winners for challenge with non-completed ${challenge.status} status`) - } - // TODO: Fix this Tech Debt once legacy is turned off - const finalStatus = data.status || challenge.status - const finalTimelineTemplateId = data.timelineTemplateId || challenge.timelineTemplateId - if (!_.get(data, 'legacy.pureV5') && !_.get(challenge, 'legacy.pureV5')) { - if (finalStatus !== constants.challengeStatuses.New && finalTimelineTemplateId !== challenge.timelineTemplateId) { - throw new errors.BadRequestError(`Cannot change the timelineTemplateId for challenges with status: ${finalStatus}`) + const finalStatus = data.status || challenge.status; + const finalTimelineTemplateId = data.timelineTemplateId || challenge.timelineTemplateId; + let timelineTemplateChanged = false; + if ( + !currentUser.isMachine && + !hasAdminRole(currentUser) && + !_.get(data, "legacy.pureV5") && + !_.get(challenge, "legacy.pureV5") + ) { + if ( + finalStatus !== constants.challengeStatuses.New && + finalTimelineTemplateId !== challenge.timelineTemplateId + ) { + throw new errors.BadRequestError( + `Cannot change the timelineTemplateId for challenges with status: ${finalStatus}` + ); } } else if (finalTimelineTemplateId !== challenge.timelineTemplateId) { // make sure there are no previous phases if the timeline template has changed - challenge.phases = [] + challenge.phases = []; + timelineTemplateChanged = true; } if (data.prizeSets) { - if (isDifferentPrizeSets(data.prizeSets, challenge.prizeSets) && finalStatus === constants.challengeStatuses.Completed) { - throw new errors.BadRequestError(`Cannot update prizeSets for challenges with status: ${finalStatus}!`) + if ( + isDifferentPrizeSets(data.prizeSets, challenge.prizeSets) && + finalStatus === constants.challengeStatuses.Completed + ) { + // Allow only M2M to update prizeSets for completed challenges + if (!currentUser.isMachine || (challenge.task != null && challenge.task.isTask !== true)) { + throw new errors.BadRequestError( + `Cannot update prizeSets for challenges with status: ${finalStatus}!` + ); + } } - const prizeSetsGroup = _.groupBy(data.prizeSets, 'type') - if (!prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes] && _.get(challenge, 'overview.totalPrizes')) { - // remove the totalPrizes if challenge prizes are empty - challenge.overview = _.omit(challenge.overview, ['totalPrizes']) + + const prizeSetsGroup = _.groupBy(data.prizeSets, "type"); + if (prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes]) { + const totalPrizes = helper.sumOfPrizes( + prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes][0].prizes + ); + _.assign(data, { overview: { totalPrizes } }); + } + } + + let phasesUpdated = false; + if ( + ((data.phases && data.phases.length > 0) || + isChallengeBeingActivated || + timelineTemplateChanged) && + !isChallengeBeingCancelled + ) { + if ( + challenge.status === constants.challengeStatuses.Completed || + challenge.status.indexOf(constants.challengeStatuses.Cancelled) > -1 + ) { + throw new BadRequestError( + `Challenge phase/start date can not be modified for Completed or Cancelled challenges.` + ); + } + const newStartDate = data.startDate || challenge.startDate; + let newPhases; + if (timelineTemplateChanged) { + newPhases = await phaseHelper.populatePhasesForChallengeCreation( + data.phases, + newStartDate, + finalTimelineTemplateId + ); } else { - const totalPrizes = helper.sumOfPrizes(prizeSetsGroup[constants.prizeSetTypes.ChallengePrizes][0].prizes) - logger.debug(`re-calculate total prizes, current value is ${totalPrizes.value}`) - _.assign(challenge, { overview: { totalPrizes } }) + newPhases = await phaseHelper.populatePhasesForChallengeUpdate( + challenge.phases, + data.phases, + challenge.timelineTemplateId, + isChallengeBeingActivated + ); } + phasesUpdated = true; + data.phases = newPhases; } - - if (data.phases || data.startDate) { - if (challenge.status === constants.challengeStatuses.Completed || challenge.status.indexOf(constants.challengeStatuses.Cancelled) > -1) { - throw new BadRequestError(`Challenge phase/start date can not be modified for Completed or Cancelled challenges.`) - } - - if (data.phases && data.phases.length > 0) { - for (let i = 0; i < challenge.phases.length; i += 1) { - const updatedPhaseInfo = _.find(data.phases, p => p.phaseId === challenge.phases[i].phaseId) - if (updatedPhaseInfo) { - _.extend(challenge.phases[i], updatedPhaseInfo) - } - } - if (challenge.phases.length === 0 && data.phases && data.phases.length > 0) { - challenge.phases = data.phases - } - } - - const newPhases = _.cloneDeep(challenge.phases) || [] - const newStartDate = data.startDate || challenge.startDate - - await phaseHelper.validatePhases(newPhases) - // populate phases - - await phaseHelper.populatePhases(newPhases, newStartDate, data.timelineTemplateId || challenge.timelineTemplateId) - data.phases = newPhases - challenge.phases = newPhases - data.startDate = newStartDate - data.endDate = helper.calculateChallengeEndDate(challenge, data) + if (isChallengeBeingCancelled && challenge.phases && challenge.phases.length > 0) { + data.phases = phaseHelper.handlePhasesAfterCancelling(challenge.phases); + phasesUpdated = true; } - - // PUT HERE - if (data.status) { - if (challenge.legacy.selfService && data.status === constants.challengeStatuses.Draft) { - try { - await helper.updateSelfServiceProjectInfo(challenge.projectId, data.endDate || challenge.endDate, currentUser) - } catch (e) { - logger.debug(`There was an error trying to update the project: ${e.message}`) - } - } + if (phasesUpdated || data.startDate) { + data.startDate = convertToISOString(_.min(_.map(data.phases, "scheduledStartDate"))); + } + if (phasesUpdated || data.endDate) { + data.endDate = convertToISOString(_.max(_.map(data.phases, "scheduledEndDate"))); } if (data.winners && data.winners.length && data.winners.length > 0) { - await validateWinners(data.winners, challengeId) + await validateWinners(data.winners, challengeResources); + if (_.get(challenge, "legacy.pureV5Task", false)) { + _.each(data.winners, (w) => { + w.type = constants.prizeSetTypes.ChallengePrizes; + }); + } } // Only m2m tokens are allowed to modify the `task.*` information on a challenge - if (!_.isUndefined(_.get(data, 'task')) && !currentUser.isMachine) { - if (!_.isUndefined(_.get(challenge, 'task'))) { - logger.info(`User ${currentUser.handle || currentUser.sub} is not allowed to modify the task information on challenge ${challengeId}`) - data.task = challenge.task - logger.info(`Task information on challenge ${challengeId} is reset to ${JSON.stringify(challenge.task)}. Original data: ${JSON.stringify(data.task)}`) + if (!_.isUndefined(_.get(data, "task")) && !currentUser.isMachine) { + if (!_.isUndefined(_.get(challenge, "task"))) { + logger.info( + `User ${ + currentUser.handle || currentUser.sub + } is not allowed to modify the task information on challenge ${challengeId}` + ); + data.task = challenge.task; + logger.info( + `Task information on challenge ${challengeId} is reset to ${JSON.stringify( + challenge.task + )}. Original data: ${JSON.stringify(data.task)}` + ); } else { - delete data.task + delete data.task; } } // task.memberId goes out of sync due to another processor setting "task.memberId" but subsequent immediate update to the task // will not have the memberId set. So we need to set it using winners to ensure it is always in sync. The proper fix is to correct // the sync issue in the processor. However this is quick fix that works since winner.userId is task.memberId. - if (_.get(challenge, 'legacy.pureV5Task') && !_.isUndefined(data.winners)) { - const winnerMemberId = _.get(data.winners, '[0].userId') - logger.info(`Setting task.memberId to ${winnerMemberId} for challenge ${challengeId}. Task ${_.get(data, 'task')} - ${_.get(challenge, 'task')}`) - - if (winnerMemberId != null && _.get(data, 'task.memberId') !== winnerMemberId) { - logger.info(`Task ${challengeId} has a winner ${winnerMemberId}`) + if (_.get(challenge, "legacy.pureV5Task") && !_.isUndefined(data.winners)) { + const winnerMemberId = _.get(data.winners, "[0].userId"); + logger.info( + `Setting task.memberId to ${winnerMemberId} for challenge ${challengeId}. Task ${_.get( + data, + "task" + )} - ${_.get(challenge, "task")}` + ); + + if (winnerMemberId != null && _.get(data, "task.memberId") !== winnerMemberId) { + logger.info(`Task ${challengeId} has a winner ${winnerMemberId}`); data.task = { isTask: true, isAssigned: true, - memberId: winnerMemberId - } - logger.warn(`task.memberId mismatched with winner memberId. task.memberId is updated to ${winnerMemberId}`) + memberId: winnerMemberId, + }; + logger.warn( + `task.memberId mismatched with winner memberId. task.memberId is updated to ${winnerMemberId}` + ); } else { - logger.info(`task ${challengeId} has no winner set yet.`) + logger.info(`task ${challengeId} has no winner set yet.`); } } else { - logger.info(`${challengeId} is not a pureV5 challenge or has no winners set yet.`) - } - - data.updated = moment().utc() - data.updatedBy = currentUser.handle || currentUser.sub - const finalMetadata = [...challenge.metadata || []] - _.each(data.metadata || [], (rec) => { - const existingMeta = _.findIndex(finalMetadata, m => m.name === rec.name) - if (existingMeta > -1) { - finalMetadata[existingMeta].value = rec.value - } else { - finalMetadata.push(rec) - } - }) - data.metadata = finalMetadata - const updateDetails = {} - const auditLogs = [] - let phasesHaveBeenModified = false - _.each(data, (value, key) => { - let op - if (key === 'metadata') { - if (_.isUndefined(challenge[key]) || challenge[key].length !== value.length || - _.differenceWith(challenge[key], value, _.isEqual).length !== 0) { - op = '$PUT' - } - } else if (key === 'phases') { - // always consider a modification if the property exists - phasesHaveBeenModified = true - logger.info('update phases') - op = '$PUT' - } else if (key === 'prizeSets') { - if (isDifferentPrizeSets(challenge[key], value)) { - logger.info('update prize sets') - op = '$PUT' - } - } else if (key === 'tags') { - if (_.isUndefined(challenge[key]) || challenge[key].length !== value.length || - _.intersection(challenge[key], value).length !== value.length) { - op = '$PUT' - } - } else if (key === 'attachments') { - const oldIds = _.map(challenge.attachments || [], (a) => a.id) - if (oldIds.length !== value.length || - _.intersection(oldIds, _.map(value, a => a.id)).length !== value.length) { - op = '$PUT' - } - } else if (key === 'groups') { - if (_.isUndefined(challenge[key]) || challenge[key].length !== value.length || - _.intersection(challenge[key], value).length !== value.length) { - op = '$PUT' - } - // } else if (key === 'gitRepoURLs') { - // if (_.isUndefined(challenge[key]) || challenge[key].length !== value.length || - // _.intersection(challenge[key], value).length !== value.length) { - // op = '$PUT' - // } - } else if (key === 'winners') { - if (_.isUndefined(challenge[key]) || challenge[key].length !== value.length || - _.intersectionWith(challenge[key], value, _.isEqual).length !== value.length) { - op = '$PUT' - } - } else if (key === 'terms') { - const oldIds = _.map(challenge.terms || [], (t) => t.id) - const newIds = _.map(value || [], (t) => t.id) - if (oldIds.length !== newIds.length || - _.intersection(oldIds, newIds).length !== value.length) { - op = '$PUT' - } - } else if (key === 'billing' || key === 'legacy') { - // make sure that's always being udpated - op = '$PUT' - } else if (_.isUndefined(challenge[key]) || challenge[key] !== value) { - op = '$PUT' - } else if (_.get(challenge, 'legacy.pureV5Task') && key === 'task') { - // always update task for pureV5 challenges - op = '$PUT' - } - - if (op) { - if (_.isUndefined(updateDetails[op])) { - updateDetails[op] = {} - } - if (key === 'attachments') { - updateDetails[op].attachments = newAttachments - } else if (key === 'terms') { - updateDetails[op].terms = newTermsOfUse - } else { - updateDetails[op][key] = value - } - if (key !== 'updated' && key !== 'updatedBy') { - let oldValue - let newValue - if (key === 'attachments') { - oldValue = challenge.attachments ? JSON.stringify(challenge.attachments) : 'NULL' - newValue = JSON.stringify(newAttachments) - } else if (key === 'terms') { - oldValue = challenge.terms ? JSON.stringify(challenge.terms) : 'NULL' - newValue = JSON.stringify(newTermsOfUse) - } else { - oldValue = challenge[key] ? JSON.stringify(challenge[key]) : 'NULL' - newValue = JSON.stringify(value) - } - // logger.debug(`Audit Log: Key ${key} OldValue: ${oldValue} NewValue: ${newValue}`) - auditLogs.push({ - id: uuid(), - challengeId, - fieldName: key, - oldValue, - newValue, - created: moment().utc(), - createdBy: currentUser.handle || currentUser.sub, - memberId: currentUser.userId || null - }) + logger.info(`${challengeId} is not a pureV5 challenge or has no winners set yet.`); + } + + const { track, type } = await challengeHelper.validateAndGetChallengeTypeAndTrack({ + typeId: challenge.typeId, + trackId: challenge.trackId, + timelineTemplateId: timelineTemplateChanged + ? finalTimelineTemplateId + : challenge.timelineTemplateId, + }); + + if (_.get(type, "isTask")) { + if (!_.isEmpty(_.get(data, "task.memberId"))) { + const registrants = _.filter( + challengeResources, + (r) => r.roleId === config.SUBMITTER_ROLE_ID + ); + if ( + !_.find( + registrants, + (r) => _.toString(r.memberId) === _.toString(_.get(data, "task.memberId")) + ) + ) { + throw new errors.BadRequestError( + `Member ${_.get( + data, + "task.memberId" + )} is not a submitter resource of challenge ${challengeId}` + ); } } - }) - - if (isFull && _.isUndefined(data.metadata) && challenge.metadata) { - updateDetails['$DELETE'] = { metadata: null } - auditLogs.push({ - id: uuid(), - challengeId, - fieldName: 'metadata', - oldValue: JSON.stringify(challenge.metadata), - newValue: 'NULL', - created: moment().utc(), - createdBy: currentUser.handle || currentUser.sub, - memberId: currentUser.userId || null - }) - delete challenge.metadata - // send null to Elasticsearch to clear the field - data.metadata = null - } - if (isFull && _.isUndefined(data.attachments) && challenge.attachments) { - if (!updateDetails['$DELETE']) { - updateDetails['$DELETE'] = {} - } - updateDetails['$DELETE'].attachments = null - auditLogs.push({ - id: uuid(), - challengeId, - fieldName: 'attachments', - oldValue: JSON.stringify(challenge.attachments), - newValue: 'NULL', - created: moment().utc(), - createdBy: currentUser.handle || currentUser.sub, - memberId: currentUser.userId || null - }) - delete challenge.attachments - // send null to Elasticsearch to clear the field - data.attachments = null - } - if (isFull && _.isUndefined(data.groups) && challenge.groups) { - if (!updateDetails['$DELETE']) { - updateDetails['$DELETE'] = {} - } - updateDetails['$DELETE'].groups = null - auditLogs.push({ - id: uuid(), - challengeId, - fieldName: 'groups', - oldValue: JSON.stringify(challenge.groups), - newValue: 'NULL', - created: moment().utc(), - createdBy: currentUser.handle || currentUser.sub, - memberId: currentUser.userId || null - }) - delete challenge.groups - // send null to Elasticsearch to clear the field - data.groups = null - } - // if (isFull && _.isUndefined(data.gitRepoURLs) && challenge.gitRepoURLs) { - // if (!updateDetails['$DELETE']) { - // updateDetails['$DELETE'] = {} - // } - // updateDetails['$DELETE'].gitRepoURLs = null - // auditLogs.push({ - // id: uuid(), - // challengeId, - // fieldName: 'gitRepoURLs', - // oldValue: JSON.stringify(challenge.gitRepoURLs), - // newValue: 'NULL', - // created: moment().utc(), - // createdBy: currentUser.handle || currentUser.sub, - // memberId: currentUser.userId || null - // }) - // delete challenge.gitRepoURLs - // // send null to Elasticsearch to clear the field - // data.gitRepoURLs = null - // } - if (isFull && _.isUndefined(data.legacyId) && challenge.legacyId) { - data.legacyId = challenge.legacyId - } - if (isFull && _.isUndefined(data.winners) && challenge.winners) { - if (!updateDetails['$DELETE']) { - updateDetails['$DELETE'] = {} - } - updateDetails['$DELETE'].winners = null - auditLogs.push({ - id: uuid(), - challengeId, - fieldName: 'winners', - oldValue: JSON.stringify(challenge.winners), - newValue: 'NULL', - created: moment().utc(), - createdBy: currentUser.handle || currentUser.sub, - memberId: currentUser.userId || null - }) - delete challenge.winners - // send null to Elasticsearch to clear the field - data.winners = null } - const { track, type } = await validateChallengeData(_.pick(challenge, ['trackId', 'typeId'])) - - if (_.get(type, 'isTask')) { - if (!_.isEmpty(_.get(data, 'task.memberId'))) { - const challengeResources = await helper.getChallengeResources(challengeId) - const registrants = _.filter(challengeResources, r => r.roleId === config.SUBMITTER_ROLE_ID) - if (!_.find(registrants, r => _.toString(r.memberId) === _.toString(_.get(data, 'task.memberId')))) { - throw new errors.BadRequestError(`Member ${_.get(data, 'task.memberId')} is not a submitter resource of challenge ${challengeId}`) - } - } + if (!_.isUndefined(data.terms)) { + await helper.validateChallengeTerms(data.terms); } - logger.debug(`Challenge.update id: ${challengeId} Details: ${JSON.stringify(updateDetails)}`) - await models.Challenge.update({ id: challengeId }, updateDetails) - - if (auditLogs.length > 0) { - await models.AuditLog.batchPut(auditLogs) - } + if (data.phases && data.phases.length > 0) { + await getPhasesAndPopulate(data); - delete data.attachments - delete data.terms - _.assign(challenge, data) - if (!_.isUndefined(newAttachments)) { - challenge.attachments = newAttachments - data.attachments = newAttachments + if (deepEqual(data.phases, challenge.phases)) { + delete data.phases; + } } - if (!_.isUndefined(newTermsOfUse)) { - challenge.terms = newTermsOfUse - data.terms = newTermsOfUse - } + const updateInput = sanitizeRepeatedFieldsInUpdateRequest(_.omit(data, ["cancelReason"])); + if (!_.isEmpty(updateInput)) { + const grpcMetadata = new GrpcMetadata(); - if (challenge.phases && challenge.phases.length > 0) { - await getPhasesAndPopulate(challenge) - } + grpcMetadata.set("handle", currentUser.handle); + grpcMetadata.set("userId", currentUser.userId); + grpcMetadata.set("token", await getM2MToken()); - // Populate challenge.track and challenge.type based on the track/type IDs + const newPrizeType = challengeHelper.validatePrizeSetsAndGetPrizeType(updateInput.prizeSets); + if (newPrizeType != null && existingPrizeType != null && newPrizeType !== existingPrizeType) { + throw new errors.BadRequestError( + `Cannot change prize type from ${existingPrizeType} to ${newPrizeType}` + ); + } - if (track) { - challenge.track = track.name - } - if (type) { - challenge.type = type.name + await challengeDomain.update( + { + filterCriteria: getScanCriteria({ id: challengeId }), + updateInput, + }, + grpcMetadata + ); } - // post bus event - logger.debug(`Post Bus Event: ${constants.Topics.ChallengeUpdated} ${JSON.stringify(challenge)}`) - const options = {} - if (challenge.status === 'Completed') { - options.key = `${challenge.id}:${challenge.status}` - } - await helper.postBusEvent(constants.Topics.ChallengeUpdated, challenge, options) - if (phasesHaveBeenModified === true && _.get(challenge, 'legacy.useSchedulingAPI')) { - await helper.postBusEvent(config.SCHEDULING_TOPIC, { id: challengeId }) - } - if (challenge.phases && challenge.phases.length > 0) { - challenge.currentPhase = challenge.phases.slice().reverse().find(phase => phase.isOpen) - challenge.endDate = helper.calculateChallengeEndDate(challenge) - const registrationPhase = _.find(challenge.phases, p => p.name === 'Registration') - const submissionPhase = _.find(challenge.phases, p => p.name === 'Submission') - challenge.currentPhaseNames = _.map(_.filter(challenge.phases, p => p.isOpen === true), 'name') - if (registrationPhase) { - challenge.registrationStartDate = registrationPhase.actualStartDate || registrationPhase.scheduledStartDate - challenge.registrationEndDate = registrationPhase.actualEndDate || registrationPhase.scheduledEndDate - } - if (submissionPhase) { - challenge.submissionStartDate = submissionPhase.actualStartDate || submissionPhase.scheduledStartDate - challenge.submissionEndDate = submissionPhase.actualEndDate || submissionPhase.scheduledEndDate - } - } - // Update ES - await esClient.update({ - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), - refresh: config.get('ES.ES_REFRESH'), - id: challengeId, - body: { - doc: challenge - } - }) + const updatedChallenge = await challengeDomain.lookup(getLookupCriteria("id", challengeId)); - if (challenge.legacy.selfService) { - const creator = await helper.getMemberByHandle(challenge.createdBy) + await indexChallengeAndPostToKafka(updatedChallenge, track, type); + + if (updatedChallenge.legacy.selfService) { + const creator = await helper.getMemberByHandle(updatedChallenge.createdBy); if (sendSubmittedEmail) { await helper.sendSelfServiceNotification( constants.SelfServiceNotificationTypes.WORK_REQUEST_SUBMITTED, [{ email: creator.email }], { handle: creator.handle, - workItemName: challenge.name + workItemName: updatedChallenge.name, } - ) + ); } if (sendActivationEmail) { await helper.sendSelfServiceNotification( @@ -1969,10 +1883,10 @@ async function update (currentUser, challengeId, data, isFull) { [{ email: creator.email }], { handle: creator.handle, - workItemName: challenge.name, - workItemUrl: `${config.SELF_SERVICE_APP_URL}/work-items/${challenge.id}` + workItemName: updatedChallenge.name, + workItemUrl: `${config.SELF_SERVICE_APP_URL}/work-items/${updatedChallenge.id}`, } - ) + ); } if (sendCompletedEmail) { await helper.sendSelfServiceNotification( @@ -1980,34 +1894,214 @@ async function update (currentUser, challengeId, data, isFull) { [{ email: creator.email }], { handle: creator.handle, - workItemName: challenge.name, - workItemUrl: `${config.SELF_SERVICE_APP_URL}/work-items/${challenge.id}?tab=solutions` + workItemName: updatedChallenge.name, + workItemUrl: `${config.SELF_SERVICE_APP_URL}/work-items/${updatedChallenge.id}?tab=solutions`, } - ) + ); } - if (sendRejectedEmail || cancelReason) { - logger.debug('Should send redirected email') + if (sendRejectedEmail || data.cancelReason) { + logger.debug("Should send redirected email"); await helper.sendSelfServiceNotification( constants.SelfServiceNotificationTypes.WORK_REQUEST_REDIRECTED, [{ email: creator.email }], { handle: creator.handle, - workItemName: challenge.name + workItemName: updatedChallenge.name, } - ) + ); } } - return challenge + + return updatedChallenge; } +updateChallenge.schema = { + currentUser: Joi.any(), + challengeId: Joi.id(), + data: Joi.object() + .keys({ + legacy: Joi.object() + .keys({ + track: Joi.string(), + subTrack: Joi.string(), + reviewType: Joi.string() + .valid(_.values(constants.reviewTypes)) + .insensitive() + .default(constants.reviewTypes.Internal), + confidentialityType: Joi.string() + .allow(null, "") + .empty(null, "") + .default(config.DEFAULT_CONFIDENTIALITY_TYPE), + directProjectId: Joi.number(), + forumId: Joi.number().integer(), + isTask: Joi.boolean(), + useSchedulingAPI: Joi.boolean(), + pureV5Task: Joi.boolean(), + pureV5: Joi.boolean(), + selfService: Joi.boolean(), + selfServiceCopilot: Joi.string().allow(null), + }) + .unknown(true), + cancelReason: Joi.string().optional(), + task: Joi.object() + .keys({ + isTask: Joi.boolean().default(false), + isAssigned: Joi.boolean().default(false), + memberId: Joi.alternatives().try(Joi.string().allow(null), Joi.number().allow(null)), + }) + .optional(), + billing: Joi.object() + .keys({ + billingAccountId: Joi.string(), + markup: Joi.number().min(0).max(100), + }) + .unknown(true), + trackId: Joi.optionalId(), + typeId: Joi.optionalId(), + name: Joi.string().optional(), + description: Joi.string().optional(), + privateDescription: Joi.string().allow("").optional(), + descriptionFormat: Joi.string().optional(), + metadata: Joi.array() + .items( + Joi.object() + .keys({ + name: Joi.string().required(), + value: Joi.required(), + }) + .unknown(true) + ) + .unique((a, b) => a.name === b.name), + timelineTemplateId: Joi.string().optional(), // changing this to update migrated challenges + phases: Joi.array() + .items( + Joi.object() + .keys({ + phaseId: Joi.id(), + duration: Joi.number().integer().min(0), + isOpen: Joi.boolean(), + actualEndDate: Joi.date().allow(null), + scheduledStartDate: Joi.date().allow(null), + constraints: Joi.array() + .items( + Joi.object() + .keys({ + name: Joi.string(), + value: Joi.number().integer().min(0), + }) + .optional() + ) + .optional(), + }) + .unknown(true) + ) + .min(1) + .optional(), + events: Joi.array().items( + Joi.object() + .keys({ + id: Joi.number().required(), + name: Joi.string(), + key: Joi.string(), + }) + .unknown(true) + .optional() + ), + discussions: Joi.array() + .items( + Joi.object().keys({ + id: Joi.optionalId(), + name: Joi.string().required(), + type: Joi.string().required().valid(_.values(constants.DiscussionTypes)), + provider: Joi.string().required(), + url: Joi.string(), + options: Joi.array().items(Joi.object()), + }) + ) + .optional(), + startDate: Joi.date().iso(), + prizeSets: Joi.array() + .items( + Joi.object() + .keys({ + type: Joi.string().valid(_.values(constants.prizeSetTypes)).required(), + description: Joi.string(), + prizes: Joi.array() + .items( + Joi.object().keys({ + description: Joi.string(), + type: Joi.string().required(), + value: Joi.number().min(0).required(), + }) + ) + .min(1) + .required(), + }) + .unknown(true) + ) + .min(1), + tags: Joi.array().items(Joi.string()), // tag names + projectId: Joi.number().integer().positive(), + legacyId: Joi.number().integer().positive(), + constraints: Joi.object() + .keys({ + allowedRegistrants: Joi.array().items(Joi.string().trim().lowercase()).optional(), + }) + .optional(), + status: Joi.string().valid(_.values(constants.challengeStatuses)), + attachments: Joi.array().items( + Joi.object().keys({ + id: Joi.id(), + challengeId: Joi.id(), + name: Joi.string().required(), + url: Joi.string().uri().required(), + fileSize: Joi.fileSize(), + description: Joi.string(), + }) + ), + groups: Joi.array().items(Joi.optionalId()).unique(), + // gitRepoURLs: Joi.array().items(Joi.string().uri()), + winners: Joi.array() + .items( + Joi.object() + .keys({ + userId: Joi.number().integer().positive().required(), + handle: Joi.string().required(), + placement: Joi.number().integer().positive().required(), + type: Joi.string().valid(_.values(constants.prizeSetTypes)), + }) + .unknown(true) + ) + .optional(), + terms: Joi.array().items( + Joi.object().keys({ + id: Joi.id(), + roleId: Joi.id(), + }) + ), + skills: Joi.array() + .items( + Joi.object() + .keys({ + id: Joi.id(), + }) + .unknown(true) + ) + .optional(), + overview: Joi.any().forbidden() + }) + .unknown(true) + .required(), +}; + /** * Send notifications * @param {Object} currentUser the current use * @param {String} challengeId the challenge id */ -async function sendNotifications (currentUser, challengeId) { - const challenge = await getChallenge(currentUser, challengeId) - const creator = await helper.getMemberByHandle(challenge.createdBy) +async function sendNotifications(currentUser, challengeId) { + const challenge = await getChallenge(currentUser, challengeId); + const creator = await helper.getMemberByHandle(challenge.createdBy); if (challenge.status === constants.challengeStatuses.Completed) { await helper.sendSelfServiceNotification( constants.SelfServiceNotificationTypes.WORK_COMPLETED, @@ -2015,366 +2109,504 @@ async function sendNotifications (currentUser, challengeId) { { handle: creator.handle, workItemName: challenge.name, - workItemUrl: `${config.SELF_SERVICE_APP_URL}/work-items/${challenge.id}?tab=solutions` + workItemUrl: `${config.SELF_SERVICE_APP_URL}/work-items/${challenge.id}?tab=solutions`, } - ) - return { type: constants.SelfServiceNotificationTypes.WORK_COMPLETED } + ); + return { type: constants.SelfServiceNotificationTypes.WORK_COMPLETED }; } } sendNotifications.schema = { currentUser: Joi.any(), - challengeId: Joi.id() -} + challengeId: Joi.id(), +}; /** * Remove unwanted properties from the challenge object * @param {Object} challenge the challenge object */ -function sanitizeChallenge (challenge) { +function sanitizeChallenge(challenge) { const sanitized = _.pick(challenge, [ - 'trackId', - 'typeId', - 'name', - 'description', - 'privateDescription', - 'descriptionFormat', - 'timelineTemplateId', - 'tags', - 'projectId', - 'legacyId', - 'startDate', - 'status', - 'task', - 'groups', - 'cancelReason' - ]) + "trackId", + "typeId", + "name", + "description", + "privateDescription", + "descriptionFormat", + "timelineTemplateId", + "tags", + "projectId", + "legacyId", + "startDate", + "status", + "task", + "groups", + "cancelReason", + "constraints", + "skills", + ]); if (!_.isUndefined(sanitized.name)) { - sanitized.name = xss(sanitized.name) + sanitized.name = xss(sanitized.name); } - if (!_.isUndefined(sanitized.description)) { - sanitized.description = xss(sanitized.description) + // Only Sanitize description if it is in HTML format + // Otherwise, it is in Markdown format and we don't want to sanitize it - a future enhancement can be + // using a markdown sanitizer + if (challenge.descriptionFormat === "html" && !_.isUndefined(sanitized.description)) { + sanitized.description = xss(sanitized.description); } if (challenge.legacy) { sanitized.legacy = _.pick(challenge.legacy, [ - 'track', - 'subTrack', - 'reviewType', - 'confidentialityType', - 'forumId', - 'directProjectId', - 'screeningScorecardId', - 'reviewScorecardId', - 'isTask', - 'useSchedulingAPI', - 'pureV5Task', - 'pureV5', - 'selfService', - 'selfServiceCopilot' - ]) + "track", + "subTrack", + "reviewType", + "confidentialityType", + "forumId", + "directProjectId", + "screeningScorecardId", + "reviewScorecardId", + "isTask", + "useSchedulingAPI", + "pureV5Task", + "pureV5", + "selfService", + "selfServiceCopilot", + ]); } if (challenge.billing) { - sanitized.billing = _.pick(challenge.billing, [ - 'billingAccountId', - 'markup' - ]) + sanitized.billing = _.pick(challenge.billing, ["billingAccountId", "markup"]); } if (challenge.metadata) { - sanitized.metadata = _.map(challenge.metadata, meta => _.pick(meta, ['name', 'value'])) + sanitized.metadata = _.map(challenge.metadata, (meta) => _.pick(meta, ["name", "value"])); } if (challenge.phases) { - sanitized.phases = _.map(challenge.phases, phase => _.pick(phase, ['phaseId', 'duration', 'isOpen', 'actualEndDate', 'scheduledStartDate', 'constraints'])) + sanitized.phases = _.map(challenge.phases, (phase) => + _.pick(phase, ["phaseId", "duration", "scheduledStartDate", "constraints"]) + ); } if (challenge.prizeSets) { - sanitized.prizeSets = _.map(challenge.prizeSets, prizeSet => ({ - ..._.pick(prizeSet, ['type', 'description']), - prizes: _.map(prizeSet.prizes, prize => _.pick(prize, ['description', 'type', 'value'])) - })) + sanitized.prizeSets = _.map(challenge.prizeSets, (prizeSet) => ({ + ..._.pick(prizeSet, ["type", "description"]), + prizes: _.map(prizeSet.prizes, (prize) => _.pick(prize, ["description", "type", "value"])), + })); } if (challenge.events) { - sanitized.events = _.map(challenge.events, event => _.pick(event, ['id', 'name', 'key'])) + sanitized.events = _.map(challenge.events, (event) => _.pick(event, ["id", "name", "key"])); } if (challenge.winners) { - sanitized.winners = _.map(challenge.winners, winner => _.pick(winner, ['userId', 'handle', 'placement', 'type'])) + sanitized.winners = _.map(challenge.winners, (winner) => + _.pick(winner, ["userId", "handle", "placement", "type"]) + ); } if (challenge.discussions) { - sanitized.discussions = _.map(challenge.discussions, discussion => ({ - ..._.pick(discussion, ['id', 'provider', 'name', 'type', 'url', 'options']), - name: _.get(discussion, 'name', '').substring(0, config.FORUM_TITLE_LENGTH_LIMIT) - })) + sanitized.discussions = _.map(challenge.discussions, (discussion) => ({ + ..._.pick(discussion, ["id", "provider", "name", "type", "url", "options"]), + name: _.get(discussion, "name", "").substring(0, config.FORUM_TITLE_LENGTH_LIMIT), + })); } if (challenge.terms) { - sanitized.terms = _.map(challenge.terms, term => _.pick(term, ['id', 'roleId'])) + sanitized.terms = _.map(challenge.terms, (term) => _.pick(term, ["id", "roleId"])); } if (challenge.attachments) { - sanitized.attachments = _.map(challenge.attachments, attachment => _.pick(attachment, ['id', 'name', 'url', 'fileSize', 'description', 'challengeId'])) + sanitized.attachments = _.map(challenge.attachments, (attachment) => + _.pick(attachment, ["id", "name", "url", "fileSize", "description", "challengeId"]) + ); + } + + return sanitized; +} + +function sanitizeData(data, challenge) { + for (const key in data) { + if (key === "phases") continue; + + if (challenge.hasOwnProperty(key)) { + if (key === "skills" && deepEqual(_.map(data.skills, "id"), _.map(challenge.skills, "id"))) { + delete data[key]; + continue; + } + + if ( + (typeof data[key] === "object" || Array.isArray(data[key])) && + deepEqual(data[key], challenge[key]) + ) { + delete data[key]; + } else if ( + typeof data[key] !== "object" && + !Array.isArray(data[key]) && + data[key] === challenge[key] + ) { + delete data[key]; + } + } } - return sanitized + return data; } /** - * Fully update challenge. + * Delete challenge. * @param {Object} currentUser the user who perform operation * @param {String} challengeId the challenge id - * @param {Object} data the challenge data to be updated - * @returns {Object} the updated challenge + * @returns {Object} the deleted challenge */ -async function fullyUpdateChallenge (currentUser, challengeId, data) { - return update(currentUser, challengeId, sanitizeChallenge(data), true) +async function deleteChallenge(currentUser, challengeId) { + const { items } = await challengeDomain.scan({ + criteria: getScanCriteria({ id: challengeId, status: constants.challengeStatuses.New }), + }); + const challenge = _.first(items); + if (!challenge) { + throw new errors.NotFoundError( + `Challenge with id: ${challengeId} doesn't exist or is not in New status` + ); + } + // ensure user can modify challenge + await helper.ensureUserCanModifyChallenge(currentUser, challenge); + // delete DB record + const { items: deletedItems } = await challengeDomain.delete( + getLookupCriteria("id", challengeId) + ); + if (!_.find(deletedItems, { id: challengeId })) { + throw new errors.Internal(`There was an error deleting the challenge with id: ${challengeId}`); + } + // delete ES document + await esClient.delete({ + index: config.get("ES.ES_INDEX"), + refresh: config.get("ES.ES_REFRESH"), + type: config.get("ES.OPENSEARCH") == "false" ? config.get("ES.ES_TYPE") : undefined, + id: challengeId, + }); + await helper.postBusEvent(constants.Topics.ChallengeDeleted, { + id: challengeId, + }); + return challenge; } -fullyUpdateChallenge.schema = { +deleteChallenge.schema = { currentUser: Joi.any(), challengeId: Joi.id(), - data: Joi.object().keys({ - legacy: Joi.object().keys({ - reviewType: Joi.string().valid(_.values(constants.reviewTypes)).insensitive().default(constants.reviewTypes.Internal), - confidentialityType: Joi.string().default(config.DEFAULT_CONFIDENTIALITY_TYPE), - forumId: Joi.number().integer(), - directProjectId: Joi.number().integer(), - screeningScorecardId: Joi.number().integer(), - reviewScorecardId: Joi.number().integer(), - isTask: Joi.boolean(), - useSchedulingAPI: Joi.boolean(), - pureV5Task: Joi.boolean(), - pureV5: Joi.boolean(), - selfService: Joi.boolean(), - selfServiceCopilot: Joi.string().allow(null) - }).unknown(true), - cancelReason: Joi.string(), - billing: Joi.object().keys({ - billingAccountId: Joi.string(), - markup: Joi.number().min(0).max(100) - }).unknown(true), - task: Joi.object().keys({ - isTask: Joi.boolean().default(false), - isAssigned: Joi.boolean().default(false), - memberId: Joi.string().allow(null) - }), - trackId: Joi.optionalId(), - typeId: Joi.optionalId(), - name: Joi.string().required(), - description: Joi.string(), - privateDescription: Joi.string(), - descriptionFormat: Joi.string(), - metadata: Joi.array().items(Joi.object().keys({ - name: Joi.string().required(), - value: Joi.required() - }).unknown(true)).unique((a, b) => a.name === b.name), - timelineTemplateId: Joi.string(), // Joi.optionalId(), - phases: Joi.array().items(Joi.object().keys({ - phaseId: Joi.id(), - duration: Joi.number().integer().min(0), - isOpen: Joi.boolean(), - actualEndDate: Joi.date().allow(null), - scheduledStartDate: Joi.date().allow(null), - constraints: Joi.array().items(Joi.object().keys({ - name: Joi.string(), - value: Joi.number().integer().min(0) - }).optional()).optional() - }).unknown(true)), - prizeSets: Joi.array().items(Joi.object().keys({ - type: Joi.string().valid(_.values(constants.prizeSetTypes)).required(), - description: Joi.string(), - prizes: Joi.array().items(Joi.object().keys({ - description: Joi.string(), - type: Joi.string().required(), - value: Joi.number().min(0).required() - })).min(1).required() - }).unknown(true)), - events: Joi.array().items(Joi.object().keys({ - id: Joi.number().required(), - name: Joi.string(), - key: Joi.string() - }).unknown(true)), - discussions: Joi.array().items(Joi.object().keys({ - id: Joi.optionalId(), - name: Joi.string().required(), - type: Joi.string().required().valid(_.values(constants.DiscussionTypes)), - provider: Joi.string().required(), - url: Joi.string(), - options: Joi.array().items(Joi.object()) - })), - tags: Joi.array().items(Joi.string()), // tag names - projectId: Joi.number().integer().positive().required(), - legacyId: Joi.number().integer().positive(), - startDate: Joi.date(), - status: Joi.string().valid(_.values(constants.challengeStatuses)).required(), - attachments: Joi.array().items(Joi.object().keys({ - id: Joi.id(), - challengeId: Joi.id(), - name: Joi.string().required(), - url: Joi.string().uri().required(), - fileSize: Joi.fileSize(), - description: Joi.string() - })), - groups: Joi.array().items(Joi.optionalId()), - // gitRepoURLs: Joi.array().items(Joi.string().uri()), - winners: Joi.array().items(Joi.object().keys({ - userId: Joi.number().integer().positive().required(), - handle: Joi.string().required(), - placement: Joi.number().integer().positive().required(), - type: Joi.string().valid(_.values(constants.prizeSetTypes)).default(constants.prizeSetTypes.ChallengePrizes) - }).unknown(true)).min(1), - terms: Joi.array().items(Joi.object().keys({ - id: Joi.id(), - roleId: Joi.id() - }).unknown(true)).optional().allow([]), - overview: Joi.any().forbidden() - }).unknown(true).required() +}; + +async function advancePhase(currentUser, challengeId, data) { + logger.info(`Advance Phase Request - ${challengeId} - ${JSON.stringify(data)}`); + if (currentUser && (currentUser.isMachine || hasAdminRole(currentUser))) { + const challenge = await challengeDomain.lookup(getLookupCriteria("id", challengeId)); + + if (!challenge) { + throw new errors.NotFoundError(`Challenge with id: ${challengeId} doesn't exist.`); + } + if (challenge.status !== constants.challengeStatuses.Active) { + throw new errors.BadRequestError( + `Challenge with id: ${challengeId} is not in Active status.` + ); + } + + const phaseAdvancerResult = await phaseAdvancer.advancePhase( + challenge.id, + challenge.legacyId, + challenge.phases, + data.operation, + data.phase + ); + + if (phaseAdvancerResult.success) { + const grpcMetadata = new GrpcMetadata(); + + grpcMetadata.set("handle", currentUser.handle); + grpcMetadata.set("userId", currentUser.userId); + + await challengeDomain.update( + { + filterCriteria: getScanCriteria({ id: challengeId }), + updateInput: { + phaseUpdate: { + phases: phaseAdvancerResult.updatedPhases, + }, + }, + }, + grpcMetadata + ); + + const updatedChallenge = await challengeDomain.lookup(getLookupCriteria("id", challengeId)); + await indexChallengeAndPostToKafka(updatedChallenge); + + // TODO: This is a temporary solution to update the challenge status to Completed; We currently do not have a way to get winner list using v5 data + // TODO: With the implementation of v5 review API we'll develop a mechanism to maintain the winner list in v5 data that challenge-api can use to create the winners list + if (phaseAdvancerResult.hasWinningSubmission === true) { + await challengeDomain.update( + { + filterCriteria: getScanCriteria({ id: challengeId }), + updateInput: { + status: constants.challengeStatuses.Completed, + }, + }, + grpcMetadata + ); + // Indexing in Kafka is not necessary here since domain-challenge will do it + } + + return { + success: true, + message: phaseAdvancerResult.message, + next: phaseAdvancerResult.next, + }; + } + + return phaseAdvancerResult; + } + + throw new errors.ForbiddenError( + `Admin role or an M2M token is required to advance the challenge phase.` + ); } -/** - * Partially update challenge. - * @param {Object} currentUser the user who perform operation - * @param {String} challengeId the challenge id - * @param {Object} data the challenge data to be updated - * @returns {Object} the updated challenge - */ -async function partiallyUpdateChallenge (currentUser, challengeId, data) { - return update(currentUser, challengeId, sanitizeChallenge(data)) +advancePhase.schema = { + currentUser: Joi.any(), + challengeId: Joi.id(), + data: Joi.object() + .keys({ + phase: Joi.string().required(), + operation: Joi.string().lowercase().valid("open", "close").required(), + }) + .required(), +}; + +async function indexChallengeAndPostToKafka(updatedChallenge, track, type) { + const prizeType = challengeHelper.validatePrizeSetsAndGetPrizeType(updatedChallenge.prizeSets); + + if (prizeType === constants.prizeTypes.USD) { + convertPrizeSetValuesToDollars(updatedChallenge.prizeSets, updatedChallenge.overview); + } + + if (track == null || type == null) { + const trackAndTypeData = await challengeHelper.validateAndGetChallengeTypeAndTrack({ + typeId: updatedChallenge.typeId, + trackId: updatedChallenge.trackId, + timelineTemplateId: updatedChallenge.timelineTemplateId, + }); + + if (trackAndTypeData != null) { + track = trackAndTypeData.track; + type = trackAndTypeData.type; + } + } + + // post bus event + logger.debug( + `Post Bus Event: ${constants.Topics.ChallengeUpdated} ${JSON.stringify(updatedChallenge)}` + ); + + enrichChallengeForResponse(updatedChallenge, track, type); + + await helper.postBusEvent(constants.Topics.ChallengeUpdated, updatedChallenge, { + key: + updatedChallenge.status === "Completed" + ? `${updatedChallenge.id}:${updatedChallenge.status}` + : undefined, + }); + + // Update ES + await esClient.update({ + index: config.get("ES.ES_INDEX"), + type: config.get("ES.OPENSEARCH") == "false" ? config.get("ES.ES_TYPE") : undefined, + refresh: config.get("ES.ES_REFRESH"), + id: updatedChallenge.id, + body: { + doc: updatedChallenge, + }, + }); } -partiallyUpdateChallenge.schema = { +async function updateLegacyPayout(currentUser, challengeId, data) { + console.log(`Update legacy payment data for challenge: ${challengeId} with data: `, data); + const challenge = await challengeDomain.lookup(getLookupCriteria("id", challengeId)); + + // SQL qurey to fetch the payment and payment_detail record + let sql = `SELECT * FROM informixoltp:payment p + INNER JOIN informixoltp:payment_detail pd ON p.most_recent_detail_id = pd.payment_detail_id + WHERE p.user_id = ${data.userId} AND`; + + if (challenge.legacyId != null) { + sql += ` pd.component_project_id = ${challenge.legacyId}`; + } else { + sql += ` pd.jira_issue_id = \'${challengeId}\'`; + } + + sql += " ORDER BY pd.payment_detail_id ASC"; + + console.log("Fetch legacy payment detail: ", sql); + + const result = await aclQueryDomain.rawQuery({ sql }); + let updateClauses = [`date_modified = current`]; + + const statusMap = { + Paid: 53, + OnHold: 55, + OnHoldAdmin: 55, + Owed: 56, + Cancelled: 65, + EnteredIntoPaymentSystem: 70, + }; + + if (data.status != null) { + updateClauses.push(`payment_status_id = ${statusMap[data.status]}`); + if (data.status === "Paid") { + updateClauses.push(`date_paid = '${data.datePaid}'`); + } else { + updateClauses.push("date_paid = null"); + } + } + + if (data.releaseDate != null) { + updateClauses.push(`date_due = '${data.releaseDate}'`); + } + + const paymentDetailIds = result.rows.map( + (row) => row.fields.find((field) => field.key === "payment_detail_id").value + ); + + if (data.amount != null) { + updateClauses.push(`total_amount = ${data.amount}`); + if (paymentDetailIds.length === 1) { + updateClauses.push(`net_amount = ${data.amount}`); + updateClauses.push(`gross_amount = ${data.amount}`); + } + } + + if (paymentDetailIds.length === 0) { + return { + success: false, + message: "No payment detail record found", + }; + } + + const whereClause = [`payment_detail_id IN (${paymentDetailIds.join(",")})`]; + + const updateQuery = `UPDATE informixoltp:payment_detail SET ${updateClauses.join( + ", " + )} WHERE ${whereClause.join(" AND ")}`; + + console.log("Update Clauses", updateClauses); + console.log("Update Query", updateQuery); + + await aclQueryDomain.rawQuery({ sql: updateQuery }); + + if (data.amount != null) { + if (paymentDetailIds.length > 1) { + const amountInCents = data.amount * 100; + + const split1Cents = Math.round(amountInCents * 0.75); + const split2Cents = amountInCents - split1Cents; + + const split1Dollars = Number((split1Cents / 100).toFixed(2)); + const split2Dollars = Number((split2Cents / 100).toFixed(2)); + + const paymentUpdateQueries = paymentDetailIds.map((paymentDetailId, index) => { + let amt = 0; + if (index === 0) { + amt = split1Dollars; + } + if (index === 1) { + amt = split2Dollars; + } + + return `UPDATE informixoltp:payment_detail SET date_modified = CURRENT, net_amount = ${amt}, gross_amount = ${amt} WHERE payment_detail_id = ${paymentDetailId}`; + }); + + console.log("Payment Update Queries", paymentUpdateQueries); + + await Promise.all( + paymentUpdateQueries.map((query) => aclQueryDomain.rawQuery({ sql: query })) + ); + } + } + + return { + success: true, + message: "Successfully updated legacy payout", + }; +} +updateLegacyPayout.schema = { currentUser: Joi.any(), challengeId: Joi.id(), - data: Joi.object().keys({ - legacy: Joi.object().keys({ - track: Joi.string(), - subTrack: Joi.string(), - reviewType: Joi.string().valid(_.values(constants.reviewTypes)).insensitive().default(constants.reviewTypes.Internal), - confidentialityType: Joi.string().default(config.DEFAULT_CONFIDENTIALITY_TYPE), - directProjectId: Joi.number(), - forumId: Joi.number().integer(), - isTask: Joi.boolean(), - useSchedulingAPI: Joi.boolean(), - pureV5Task: Joi.boolean(), - pureV5: Joi.boolean(), - selfService: Joi.boolean(), - selfServiceCopilot: Joi.string().allow(null) - }).unknown(true), - cancelReason: Joi.string(), - task: Joi.object().keys({ - isTask: Joi.boolean().default(false), - isAssigned: Joi.boolean().default(false), - memberId: Joi.string().allow(null) - }), - billing: Joi.object().keys({ - billingAccountId: Joi.string(), - markup: Joi.number().min(0).max(100) - }).unknown(true), - trackId: Joi.optionalId(), - typeId: Joi.optionalId(), - name: Joi.string(), - description: Joi.string(), - privateDescription: Joi.string(), - descriptionFormat: Joi.string(), - metadata: Joi.array().items(Joi.object().keys({ - name: Joi.string().required(), - value: Joi.required() - }).unknown(true)).unique((a, b) => a.name === b.name), - timelineTemplateId: Joi.string(), // changing this to update migrated challenges - phases: Joi.array().items(Joi.object().keys({ - phaseId: Joi.id(), - duration: Joi.number().integer().min(0), - isOpen: Joi.boolean(), - actualEndDate: Joi.date().allow(null), - scheduledStartDate: Joi.date().allow(null), - constraints: Joi.array().items(Joi.object().keys({ - name: Joi.string(), - value: Joi.number().integer().min(0) - }).optional()).optional() - }).unknown(true)).min(1), - events: Joi.array().items(Joi.object().keys({ - id: Joi.number().required(), - name: Joi.string(), - key: Joi.string() - }).unknown(true)), - discussions: Joi.array().items(Joi.object().keys({ - id: Joi.optionalId(), - name: Joi.string().required(), - type: Joi.string().required().valid(_.values(constants.DiscussionTypes)), - provider: Joi.string().required(), - url: Joi.string(), - options: Joi.array().items(Joi.object()) - })), - startDate: Joi.date(), - prizeSets: Joi.array().items(Joi.object().keys({ - type: Joi.string().valid(_.values(constants.prizeSetTypes)).required(), - description: Joi.string(), - prizes: Joi.array().items(Joi.object().keys({ - description: Joi.string(), - type: Joi.string().required(), - value: Joi.number().min(0).required() - })).min(1).required() - }).unknown(true)).min(1), - tags: Joi.array().items(Joi.string().required()).min(1), // tag names - projectId: Joi.number().integer().positive(), - legacyId: Joi.number().integer().positive(), - status: Joi.string().valid(_.values(constants.challengeStatuses)), - attachments: Joi.array().items(Joi.object().keys({ - id: Joi.id(), - challengeId: Joi.id(), - name: Joi.string().required(), - url: Joi.string().uri().required(), - fileSize: Joi.fileSize(), - description: Joi.string() - })), - groups: Joi.array().items(Joi.id()), // group names - // gitRepoURLs: Joi.array().items(Joi.string().uri()), - winners: Joi.array().items(Joi.object().keys({ + data: Joi.object() + .keys({ userId: Joi.number().integer().positive().required(), - handle: Joi.string().required(), - placement: Joi.number().integer().positive().required(), - type: Joi.string().valid(_.values(constants.prizeSetTypes)).default(constants.prizeSetTypes.ChallengePrizes) - }).unknown(true)).min(1), - terms: Joi.array().items(Joi.id().optional()).optional().allow([]), - overview: Joi.any().forbidden() - }).unknown(true).required() + amount: Joi.number().allow(null), + status: Joi.string().allow(null), + datePaid: Joi.string().allow(null), + releaseDate: Joi.string().allow(null), + }) +}; + +/** + * Get SRM Schedule + * @param {Object} criteria the criteria + */ +async function getSRMSchedule(criteria = {}) { + const sql = getSRMScheduleQuery(criteria); + const result = await aclQueryDomain.rawQuery({ sql }); + return convertSRMScheduleQueryOutput(result); } +getSRMSchedule.schema = { + criteria: Joi.object().keys({ + registrationStartTimeAfter: Joi.date().default(new Date()), + registrationStartTimeBefore: Joi.date(), + statuses: Joi.array() + .items(Joi.string().valid(["A", "F", "P"])) + .default(["A", "F", "P"]), + sortBy: Joi.string() + .valid(["registrationStartTime", "codingStartTime", "challengeStartTime"]) + .default("registrationStartTime"), + sortOrder: Joi.string().valid(["asc", "desc"]).default("asc"), + page: Joi.page(), + perPage: Joi.perPage(), + }), +}; + /** - * Delete challenge. + * Get SRM Schedule * @param {Object} currentUser the user who perform operation - * @param {String} challengeId the challenge id - * @returns {Object} the deleted challenge + * @param {Object} criteria the criteria */ -async function deleteChallenge (currentUser, challengeId) { - const challenge = await helper.getById('Challenge', challengeId) - if (challenge.status !== constants.challengeStatuses.New) { - throw new errors.BadRequestError(`Challenge with status other than "${constants.challengeStatuses.New}" cannot be removed`) - } - // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge) - // check if user are allowed to delete the challenge - await ensureAccessibleForChallenge(currentUser, challenge) - // delete DB record - await models.Challenge.delete(challenge) - // delete ES document - await esClient.delete({ - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), - refresh: config.get('ES.ES_REFRESH'), - id: challengeId - }) - await helper.postBusEvent(constants.Topics.ChallengeDeleted, { id: challengeId }) - return challenge +async function getPracticeProblems(currentUser, criteria = {}) { + criteria.userId = currentUser.userId; + const { query, countQuery } = getPracticeProblemsQuery(criteria); + const resultOutput = await aclQueryDomain.rawQuery({ sql: query }); + const countOutput = await aclQueryDomain.rawQuery({ sql: countQuery }); + const result = convertPracticeProblemsQueryOutput(resultOutput); + const total = countOutput.rows[0].fields[0].value; + return { total, page: criteria.page, perPage: criteria.perPage, result }; } -deleteChallenge.schema = { +getPracticeProblems.schema = { currentUser: Joi.any(), - challengeId: Joi.id() -} + criteria: Joi.object().keys({ + sortBy: Joi.string() + .valid(["problemName", "problemType", "points", "difficulty", "status", "myPoints"]) + .default("problemId"), + sortOrder: Joi.string().valid(["asc", "desc"]).default("desc"), + page: Joi.page(), + perPage: Joi.perPage(), + difficulty: Joi.string().valid(["easy", "medium", "hard"]), + status: Joi.string().valid(["new", "viewed", "solved"]), + pointsLowerBound: Joi.number().integer(), + pointsUpperBound: Joi.number().integer(), + problemName: Joi.string(), + }), +}; module.exports = { searchChallenges, createChallenge, getChallenge, - fullyUpdateChallenge, - partiallyUpdateChallenge, + updateChallenge, deleteChallenge, + updateLegacyPayout, getChallengeStatistics, - sendNotifications -} + sendNotifications, + advancePhase, + getSRMSchedule, + getPracticeProblems, +}; -logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/src/services/ChallengeTimelineTemplateService.js b/src/services/ChallengeTimelineTemplateService.js index a7484ef2..c0c1fd24 100644 --- a/src/services/ChallengeTimelineTemplateService.js +++ b/src/services/ChallengeTimelineTemplateService.js @@ -2,31 +2,52 @@ * This service provides operations of challenge type timeline template. */ -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const helper = require('../common/helper') -// const logger = require('../common/logger') -const errors = require('../common/errors') -const constants = require('../../app-constants') +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; + +const _ = require("lodash"); +const { + DomainHelper: { getScanCriteria, getLookupCriteria }, +} = require("@topcoder-framework/lib-common"); + +const { ChallengeTimelineTemplateDomain } = require("@topcoder-framework/domain-challenge"); + +const Joi = require("joi"); + +const helper = require("../common/helper"); +const errors = require("../common/errors"); + +const constants = require("../../app-constants"); +const logger = require("../common/logger"); + +const challengeTrackService = require("./ChallengeTrackService"); +const challengeTypeService = require("./ChallengeTypeService"); +const timelineTemplateService = require("./TimelineTemplateService"); + +const challengeTimelineTemplateDomain = new ChallengeTimelineTemplateDomain( + GRPC_CHALLENGE_SERVER_HOST, + GRPC_CHALLENGE_SERVER_PORT +); /** * Search challenge type timeline templates. * @param {Object} criteria the search criteria - * @returns {Array} the search result + * @returns {Promise} the search result */ -async function searchChallengeTimelineTemplates (criteria) { - let records = await helper.scanAll('ChallengeTimelineTemplate') - if (criteria.typeId) records = _.filter(records, e => (criteria.typeId === e.typeId)) - if (criteria.trackId) records = _.filter(records, e => (criteria.trackId === e.trackId)) - if (criteria.timelineTemplateId) records = _.filter(records, e => (criteria.timelineTemplateId === e.timelineTemplateId)) - if (!_.isUndefined(criteria.isDefault)) records = _.filter(records, e => (e.isDefault === (_.toLower(_.toString(criteria.isDefault)) === 'true'))) +async function searchChallengeTimelineTemplates(criteria) { + const scanCriteria = getScanCriteria(_.omit(criteria, ["page", "perPage"])); + + const { items } = await challengeTimelineTemplateDomain.scan({ + criteria: scanCriteria, + }); + + const nRecords = items.length; + return { - total: records.length, + total: nRecords, page: 1, - perPage: Math.max(records.length, 10), - result: records - } + perPage: Math.max(nRecords, 10), + result: items, + }; } searchChallengeTimelineTemplates.schema = { @@ -34,22 +55,31 @@ searchChallengeTimelineTemplates.schema = { typeId: Joi.optionalId(), trackId: Joi.optionalId(), timelineTemplateId: Joi.optionalId(), - isDefault: Joi.boolean() - }) -} + isDefault: Joi.boolean(), + page: Joi.page(), + perPage: Joi.perPage(), + }), +}; /** * Unset existing default timeline template in order to create a new one * @param {String} typeId the type ID * @param {String} trackId the track ID */ -async function unsetDefaultTimelineTemplate (typeId, trackId) { - const records = await searchChallengeTimelineTemplates({ typeId, trackId, isDefault: true }) +async function unsetDefaultTimelineTemplate(typeId, trackId) { + const records = await searchChallengeTimelineTemplates({ + typeId, + trackId, + isDefault: true, + }); if (records.total === 0) { - return + return; } for (const record of records.result) { - await fullyUpdateChallengeTimelineTemplate(record.id, { ...record, isDefault: false }) + await fullyUpdateChallengeTimelineTemplate(record.id, { + ...record, + isDefault: false, + }); } } @@ -58,48 +88,53 @@ async function unsetDefaultTimelineTemplate (typeId, trackId) { * @param {Object} data the data to create challenge type timeline template * @returns {Object} the created challenge type timeline template */ -async function createChallengeTimelineTemplate (data) { +async function createChallengeTimelineTemplate(data) { // check duplicate - const records = await searchChallengeTimelineTemplates(data) + const records = await searchChallengeTimelineTemplates(data); if (records.total > 0) { - throw new errors.ConflictError('The challenge type timeline template is already defined.') + throw new errors.ConflictError("The challenge type timeline template is already defined."); } - // check exists - await helper.getById('ChallengeType', data.typeId) - await helper.getById('ChallengeTrack', data.trackId) - await helper.getById('TimelineTemplate', data.timelineTemplateId) + + await challengeTypeService.getChallengeType(data.typeId); + await challengeTrackService.getChallengeTrack(data.trackId); + await timelineTemplateService.getTimelineTemplate(data.timelineTemplateId); if (data.isDefault) { - await unsetDefaultTimelineTemplate(data.typeId, data.trackId) + await unsetDefaultTimelineTemplate(data.typeId, data.trackId); } - const ret = await helper.create('ChallengeTimelineTemplate', _.assign({ id: uuid() }, data)) + const template = await challengeTimelineTemplateDomain.create(data); + // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTimelineTemplateCreated, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTimelineTemplateCreated, template); + return template; } createChallengeTimelineTemplate.schema = { - data: Joi.object().keys({ - typeId: Joi.id(), - trackId: Joi.id(), - timelineTemplateId: Joi.id(), - isDefault: Joi.boolean().default(false).required() - }).required() -} + data: Joi.object() + .keys({ + typeId: Joi.id(), + trackId: Joi.id(), + timelineTemplateId: Joi.id(), + isDefault: Joi.boolean().default(false).required(), + }) + .required(), +}; /** * Get challenge type timeline template. * @param {String} challengeTimelineTemplateId the challenge type timeline template id - * @returns {Object} the challenge type timeline template with given id + * @returns {Promise} the challenge type timeline template with given id */ -async function getChallengeTimelineTemplate (challengeTimelineTemplateId) { - return helper.getById('ChallengeTimelineTemplate', challengeTimelineTemplateId) +async function getChallengeTimelineTemplate(challengeTimelineTemplateId) { + return challengeTimelineTemplateDomain.lookup( + getLookupCriteria("id", challengeTimelineTemplateId) + ); } getChallengeTimelineTemplate.schema = { - challengeTimelineTemplateId: Joi.id() -} + challengeTimelineTemplateId: Joi.id(), +}; /** * Fully update challenge type timeline template. @@ -107,64 +142,88 @@ getChallengeTimelineTemplate.schema = { * @param {Object} data the challenge type timeline template data to be updated * @returns {Object} the updated challenge type timeline template */ -async function fullyUpdateChallengeTimelineTemplate (challengeTimelineTemplateId, data) { - const record = await helper.getById('ChallengeTimelineTemplate', challengeTimelineTemplateId) - if (record.typeId === data.typeId && +async function fullyUpdateChallengeTimelineTemplate(challengeTimelineTemplateId, data) { + const record = await getChallengeTimelineTemplate(challengeTimelineTemplateId); + if ( + record.typeId === data.typeId && record.trackId === data.trackId && record.timelineTemplateId === data.timelineTemplateId && - record.isDefault === data.isDefault) { - // no change - return record + record.isDefault === data.isDefault + ) { + return record; } // check duplicate - const records = await searchChallengeTimelineTemplates(data) + const records = await searchChallengeTimelineTemplates(data); if (records.total > 0) { - throw new errors.ConflictError('The challenge type timeline template is already defined.') + throw new errors.ConflictError( + `A challenge type timeline template with typeId: ${data.typeId}, trackId: ${data.trackId}, timelineTemplateId: ${data.timelineTemplateId} already exists.` + ); } - // check exists - await helper.getById('ChallengeType', data.typeId) - await helper.getById('ChallengeTrack', data.trackId) - await helper.getById('TimelineTemplate', data.timelineTemplateId) + + await challengeTypeService.getChallengeType(data.typeId); + await challengeTrackService.getChallengeTrack(data.trackId); + await timelineTemplateService.getTimelineTemplate(data.timelineTemplateId); if (data.isDefault) { - await unsetDefaultTimelineTemplate(data.typeId, data.trackId) + await unsetDefaultTimelineTemplate(data.typeId, data.trackId); } - const ret = await helper.update(record, data) - // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTimelineTemplateUpdated, ret) - return ret + const updateInput = { + filterCriteria: getScanCriteria({ + id: challengeTimelineTemplateId, + }), + updateInput: data, + }; + + const { items } = await challengeTimelineTemplateDomain.update(updateInput); + if (items.length > 0) { + // post bus event + await helper.postBusEvent(constants.Topics.ChallengeTimelineTemplateUpdated, items[0]); + return items[0]; + } else { + throw new errors.NotFoundError( + `A challenge type timeline template with id: ${challengeTimelineTemplateId} not found.` + ); + } } fullyUpdateChallengeTimelineTemplate.schema = { challengeTimelineTemplateId: Joi.id(), - data: createChallengeTimelineTemplate.schema.data -} + data: createChallengeTimelineTemplate.schema.data, +}; /** * Delete challenge type timeline template. * @param {String} challengeTimelineTemplateId the challenge type timeline template id * @returns {Object} the deleted challenge type timeline template */ -async function deleteChallengeTimelineTemplate (challengeTimelineTemplateId) { - const ret = await helper.getById('ChallengeTimelineTemplate', challengeTimelineTemplateId) - await ret.delete() +async function deleteChallengeTimelineTemplate(challengeTimelineTemplateId) { + const { items: templates } = await challengeTimelineTemplateDomain.delete( + getLookupCriteria("id", challengeTimelineTemplateId) + ); + + if (templates.length === 0) { + throw new errors.NotFoundError( + `A challenge type timeline template with id: ${challengeTimelineTemplateId} not found.` + ); + } + // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTimelineTemplateDeleted, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTimelineTemplateDeleted, templates[0]); + return templates[0]; } deleteChallengeTimelineTemplate.schema = { - challengeTimelineTemplateId: Joi.id() -} + challengeTimelineTemplateId: Joi.id(), +}; module.exports = { searchChallengeTimelineTemplates, createChallengeTimelineTemplate, getChallengeTimelineTemplate, fullyUpdateChallengeTimelineTemplate, - deleteChallengeTimelineTemplate -} + deleteChallengeTimelineTemplate, +}; -// logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/src/services/ChallengeTrackService.js b/src/services/ChallengeTrackService.js index 3e9905e6..1529ec1a 100644 --- a/src/services/ChallengeTrackService.js +++ b/src/services/ChallengeTrackService.js @@ -2,40 +2,50 @@ * This service provides operations of challenge types. */ -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const helper = require('../common/helper') -// const logger = require('../common/logger') -const constants = require('../../app-constants') +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; + +const { + DomainHelper: { getScanCriteria, getLookupCriteria }, +} = require("@topcoder-framework/lib-common"); + +const { ChallengeTrackDomain } = require("@topcoder-framework/domain-challenge"); + +const _ = require("lodash"); +const Joi = require("joi"); +const helper = require("../common/helper"); +const logger = require("../common/logger"); +const errors = require("../common/errors"); +const constants = require("../../app-constants"); + +const challengeTrackDomain = new ChallengeTrackDomain( + GRPC_CHALLENGE_SERVER_HOST, + GRPC_CHALLENGE_SERVER_PORT +); /** * Search challenge types * @param {Object} criteria the search criteria - * @returns {Object} the search result + * @returns {Promise} the search result */ -async function searchChallengeTracks (criteria) { - // TODO - move this to ES - let records = helper.getFromInternalCache('ChallengeTrack') - if (records == null) { - records = await helper.scanAll('ChallengeTrack') - helper.setToInternalCache('ChallengeTrack', records) - } +async function searchChallengeTracks(criteria) { + const scanCriteria = getScanCriteria(_.omit(criteria, ["page", "perPage"])); + const page = criteria.page || 1; + const perPage = criteria.perPage || 50; - const page = criteria.page || 1 - const perPage = criteria.perPage || 50 + const cacheKey = `ChallengeTrack${page}_${perPage}_${JSON.stringify(criteria)}`; - if (criteria.name) records = _.filter(records, e => helper.partialMatch(criteria.name, e.name)) - if (criteria.description) records = _.filter(records, e => helper.partialMatch(criteria.description, e.description)) - if (criteria.track) records = _.filter(records, e => _.toLower(criteria.track) === _.toLower(e.track)) - if (criteria.abbreviation) records = _.filter(records, e => helper.partialMatch(criteria.abbreviation, e.abbreviation)) - if (!_.isUndefined(criteria.isActive)) records = _.filter(records, e => (e.isActive === (criteria.isActive === 'true'))) - // if (criteria.legacyId) records = _.filter(records, e => (e.legacyId === criteria.legacyId)) + // TODO - move this to ES + let records = helper.getFromInternalCache(cacheKey); + if (records == null || records.length == 0) { + const { items } = await challengeTrackDomain.scan({ criteria: scanCriteria }); + records = items; + helper.setToInternalCache(cacheKey, records); + } - const total = records.length - const result = records.slice((page - 1) * perPage, page * perPage) + const total = records.length; + const result = records.slice((page - 1) * perPage, page * perPage); - return { total, page, perPage, result } + return { total, page, perPage, result }; } searchChallengeTracks.schema = { @@ -47,51 +57,61 @@ searchChallengeTracks.schema = { isActive: Joi.boolean(), abbreviation: Joi.string(), legacyId: Joi.number().integer().positive(), - track: Joi.string().valid(_.values(constants.challengeTracks)) - }) -} + track: Joi.string().valid(_.values(constants.challengeTracks)), + }), +}; /** * Create challenge type. * @param {Object} type the challenge type to created * @returns {Object} the created challenge type */ -async function createChallengeTrack (type) { - await helper.validateDuplicate('ChallengeTrack', 'name', type.name) - await helper.validateDuplicate('ChallengeTrack', 'abbreviation', type.abbreviation) - if (type.legacyId) { - await helper.validateDuplicate('ChallengeTrack', 'legacyId', type.legacyId) - } - const ret = await helper.create('ChallengeTrack', _.assign({ id: uuid() }, type)) +async function createChallengeTrack(type) { + const { items: existingByName } = await challengeTrackDomain.scan({ + criteria: getScanCriteria({ name: type.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Challenge Type with name ${type.name} already exists`); + const { items: existingByAbbr } = await challengeTrackDomain.scan({ + criteria: getScanCriteria({ abbreviation: type.abbreviation }), + }); + if (existingByAbbr.length > 0) + throw new errors.ConflictError( + `Challenge Type with abbreviation ${type.abbreviation} already exists` + ); + + const ret = await challengeTrackDomain.create(type); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTrackCreated, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTrackCreated, ret); + return ret; } createChallengeTrack.schema = { - type: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isActive: Joi.boolean().required(), - abbreviation: Joi.string().required(), - legacyId: Joi.number().integer().positive(), - track: Joi.string().valid(_.values(constants.challengeTracks)) - }).required() -} + type: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isActive: Joi.boolean().required(), + abbreviation: Joi.string().required(), + legacyId: Joi.number().integer().positive(), + track: Joi.string().valid(_.values(constants.challengeTracks)), + }) + .required(), +}; /** * Get challenge type. * @param {String} id the challenge type id * @returns {Object} the challenge type with given id */ -async function getChallengeTrack (id) { - const ret = await helper.getById('ChallengeTrack', id) - return ret +async function getChallengeTrack(id) { + return challengeTrackDomain.lookup(getLookupCriteria("id", id)); } getChallengeTrack.schema = { - id: Joi.id() -} + id: Joi.id(), +}; /** * Fully update challenge type. @@ -99,43 +119,56 @@ getChallengeTrack.schema = { * @param {Object} data the challenge type data to be updated * @returns {Object} the updated challenge type */ -async function fullyUpdateChallengeTrack (id, data) { - const type = await helper.getById('ChallengeTrack', id) +async function fullyUpdateChallengeTrack(id, data) { + const type = await getChallengeTrack(id); if (type.name.toLowerCase() !== data.name.toLowerCase()) { - await helper.validateDuplicate('ChallengeTrack', 'name', data.name) + const { items: existingByName } = await challengeTrackDomain.scan({ + criteria: getScanCriteria({ name: data.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Challenge Track with name ${data.name} already exists`); } if (type.abbreviation.toLowerCase() !== data.abbreviation.toLowerCase()) { - await helper.validateDuplicate('ChallengeTrack', 'abbreviation', data.abbreviation) - } - if (data.legacyId && type.legacyId !== data.legacyId) { - await helper.validateDuplicate('ChallengeTrack', 'legacyId', data.legacyId) + const { items: existingByAbbr } = await challengeTrackDomain.scan({ + criteria: getScanCriteria({ abbreviation: data.abbreviation }), + }); + if (existingByAbbr.length > 0) + throw new errors.ConflictError( + `Challenge Type with abbreviation ${data.abbreviation} already exists` + ); } if (_.isUndefined(data.description)) { - type.description = undefined + type.description = undefined; } if (_.isUndefined(data.legacyId)) { - type.legacyId = undefined + type.legacyId = undefined; } if (_.isUndefined(data.track)) { - type.track = undefined + type.track = undefined; } - const ret = await helper.update(type, data) + const { items } = await challengeTrackDomain.update({ + filterCriteria: getScanCriteria({ id }), + updateInput: _.extend(type, data), + }); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTrackUpdated, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTrackUpdated, items[0]); + return items[0]; } fullyUpdateChallengeTrack.schema = { id: Joi.id(), - data: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isActive: Joi.boolean().required(), - abbreviation: Joi.string().required(), - legacyId: Joi.number().integer().positive(), - track: Joi.string().valid(_.values(constants.challengeTracks)) - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isActive: Joi.boolean().required(), + abbreviation: Joi.string().required(), + legacyId: Joi.number().integer().positive(), + track: Joi.string().valid(_.values(constants.challengeTracks)), + }) + .required(), +}; /** * Partially update challenge type. @@ -143,51 +176,66 @@ fullyUpdateChallengeTrack.schema = { * @param {Object} data the challenge type data to be updated * @returns {Object} the updated challenge type */ -async function partiallyUpdateChallengeTrack (id, data) { - const type = await helper.getById('ChallengeTrack', id) +async function partiallyUpdateChallengeTrack(id, data) { + const type = await getChallengeTrack(id); if (data.name && type.name.toLowerCase() !== data.name.toLowerCase()) { - await helper.validateDuplicate('ChallengeTrack', 'name', data.name) + const { items: existingByName } = await challengeTrackDomain.scan({ + criteria: getScanCriteria({ name: data.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Challenge Track with name ${data.name} already exists`); } if (data.abbreviation && type.abbreviation.toLowerCase() !== data.abbreviation.toLowerCase()) { - await helper.validateDuplicate('ChallengeTrack', 'abbreviation', data.abbreviation) - } - if (data.legacyId && type.legacyId !== data.legacyId) { - await helper.validateDuplicate('ChallengeTrack', 'legacyId', data.legacyId) + const { items: existingByAbbr } = await challengeTrackDomain.scan({ + criteria: getScanCriteria({ abbreviation: data.abbreviation }), + }); + if (existingByAbbr.length > 0) + throw new errors.ConflictError( + `Challenge Type with abbreviation ${data.abbreviation} already exists` + ); } - const ret = await helper.update(type, data) + + const { items } = await challengeTrackDomain.update({ + filterCriteria: getScanCriteria({ id }), + updateInput: _.extend(type, data), + }); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTrackUpdated, _.assignIn({ id }, data)) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTrackUpdated, _.assignIn({ id }, data)); + return items[0]; } partiallyUpdateChallengeTrack.schema = { id: Joi.id(), - data: Joi.object().keys({ - name: Joi.string(), - description: Joi.string(), - isActive: Joi.boolean(), - abbreviation: Joi.string(), - legacyId: Joi.number().integer().positive(), - track: Joi.string().valid(_.values(constants.challengeTracks)) - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string(), + description: Joi.string(), + isActive: Joi.boolean(), + abbreviation: Joi.string(), + legacyId: Joi.number().integer().positive(), + track: Joi.string().valid(_.values(constants.challengeTracks)), + }) + .required(), +}; /** * Delete challenge track. * @param {String} id the challenge track id * @return {Object} the deleted challenge track */ -async function deleteChallengeTrack (id) { - const record = await helper.getById('ChallengeTrack', id) - await record.delete() +async function deleteChallengeTrack(id) { + const { items } = await challengeTrackDomain.delete(getLookupCriteria("id", id)); + helper.flushInternalCache(); + // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTrackDeleted, record) - return record + await helper.postBusEvent(constants.Topics.ChallengeTypeDeleted, items[0]); + return items[0]; } deleteChallengeTrack.schema = { - id: Joi.id() -} + id: Joi.id(), +}; module.exports = { searchChallengeTracks, @@ -195,7 +243,7 @@ module.exports = { getChallengeTrack, fullyUpdateChallengeTrack, partiallyUpdateChallengeTrack, - deleteChallengeTrack -} + deleteChallengeTrack, +}; -// logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/src/services/ChallengeTypeService.js b/src/services/ChallengeTypeService.js index 71072694..454fbf4e 100644 --- a/src/services/ChallengeTypeService.js +++ b/src/services/ChallengeTypeService.js @@ -2,37 +2,50 @@ * This service provides operations of challenge tracks. */ -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const helper = require('../common/helper') -const constants = require('../../app-constants') +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; + +const { + DomainHelper: { getLookupCriteria, getScanCriteria }, +} = require("@topcoder-framework/lib-common"); + +const { ChallengeTypeDomain } = require("@topcoder-framework/domain-challenge"); + +const _ = require("lodash"); +const Joi = require("joi"); +const helper = require("../common/helper"); +const constants = require("../../app-constants"); +const errors = require("../common/errors"); + +const challengeTypeDomain = new ChallengeTypeDomain( + GRPC_CHALLENGE_SERVER_HOST, + GRPC_CHALLENGE_SERVER_PORT +); /** * Search challenge types * @param {Object} criteria the search criteria - * @returns {Object} the search result + * @returns {Promise} the search result */ -async function searchChallengeTypes (criteria) { +async function searchChallengeTypes(criteria) { + const scanCriteria = getScanCriteria(_.omit(criteria, ["page", "perPage"])); + + const page = criteria.page || 1; + const perPage = criteria.perPage || 50; + + const cacheKey = `ChallengeType_${page}_${perPage}_${JSON.stringify(criteria)}`; + // TODO - move this to ES - let records = helper.getFromInternalCache('ChallengeType') - if (records == null) { - records = await helper.scanAll('ChallengeType') - helper.setToInternalCache('ChallengeType', records) + let records = helper.getFromInternalCache(cacheKey); + if (records == null || records.length === 0) { + const { items } = await challengeTypeDomain.scan({ criteria: scanCriteria }); + records = items; + helper.setToInternalCache(cacheKey, records); } - const page = criteria.page || 1 - const perPage = criteria.perPage || 50 - if (criteria.name) records = _.filter(records, e => helper.partialMatch(criteria.name, e.name)) - if (criteria.description) records = _.filter(records, e => helper.partialMatch(criteria.description, e.description)) - if (criteria.abbreviation) records = _.filter(records, e => helper.partialMatch(criteria.abbreviation, e.abbreviation)) - if (!_.isUndefined(criteria.isActive)) records = _.filter(records, e => (e.isActive === (criteria.isActive === 'true'))) - if (!_.isUndefined(criteria.isTask)) records = _.filter(records, e => (e.isTask === (criteria.isTask === 'true'))) + const total = records.length; + const result = records.slice((page - 1) * perPage, page * perPage); - const total = records.length - const result = records.slice((page - 1) * perPage, page * perPage) - - return { total, page, perPage, result } + return { total, page, perPage, result }; } searchChallengeTypes.schema = { @@ -43,47 +56,59 @@ searchChallengeTypes.schema = { description: Joi.string(), isActive: Joi.boolean(), isTask: Joi.boolean().default(false), - abbreviation: Joi.string() - }) -} + abbreviation: Joi.string(), + }), +}; /** * Create challenge type. * @param {Object} type the challenge type to created * @returns {Object} the created challenge type */ -async function createChallengeType (type) { - await helper.validateDuplicate('ChallengeType', 'name', type.name) - await helper.validateDuplicate('ChallengeType', 'abbreviation', type.abbreviation) - const ret = await helper.create('ChallengeType', _.assign({ id: uuid() }, type)) +async function createChallengeType(type) { + const { items: existingByName } = await challengeTypeDomain.scan({ + criteria: getScanCriteria({ name: type.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Challenge Type with name ${type.name} already exists`); + const { items: existingByAbbr } = await challengeTypeDomain.scan({ + criteria: getScanCriteria({ abbreviation: type.abbreviation }), + }); + if (existingByAbbr.length > 0) + throw new errors.ConflictError( + `Challenge Type with abbreviation ${type.abbreviation} already exists` + ); + const ret = await challengeTypeDomain.create(type); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTypeCreated, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTypeCreated, ret); + return ret; } createChallengeType.schema = { - type: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isActive: Joi.boolean().required(), - isTask: Joi.boolean().default(false), - abbreviation: Joi.string().required() - }).required() -} + type: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isActive: Joi.boolean().required(), + isTask: Joi.boolean().default(false), + abbreviation: Joi.string().required(), + }) + .required(), +}; /** * Get challenge type. * @param {String} id the challenge type id * @returns {Object} the challenge type with given id */ -async function getChallengeType (id) { - const ret = await helper.getById('ChallengeType', id) - return ret +async function getChallengeType(id) { + return await challengeTypeDomain.lookup(getLookupCriteria("id", id)); } getChallengeType.schema = { - id: Joi.id() -} + id: Joi.id(), +}; /** * Fully update challenge type. @@ -91,33 +116,48 @@ getChallengeType.schema = { * @param {Object} data the challenge type data to be updated * @returns {Object} the updated challenge type */ -async function fullyUpdateChallengeType (id, data) { - const type = await helper.getById('ChallengeType', id) +async function fullyUpdateChallengeType(id, data) { + const type = await getChallengeType(id); if (type.name.toLowerCase() !== data.name.toLowerCase()) { - await helper.validateDuplicate('ChallengeType', 'name', data.name) + const { items: existingByName } = await challengeTypeDomain.scan({ + criteria: getScanCriteria({ name: data.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Challenge Type with name ${data.name} already exists`); } if (type.abbreviation.toLowerCase() !== data.abbreviation.toLowerCase()) { - await helper.validateDuplicate('ChallengeType', 'abbreviation', data.abbreviation) + const { items: existingByAbbr } = await challengeTypeDomain.scan({ + criteria: getScanCriteria({ abbreviation: data.abbreviation }), + }); + if (existingByAbbr.length > 0) + throw new errors.ConflictError( + `Challenge Type with abbreviation ${data.abbreviation} already exists` + ); } if (_.isUndefined(data.description)) { - type.description = undefined + type.description = undefined; } - const ret = await helper.update(type, data) + const { items } = await challengeTypeDomain.update({ + filterCriteria: getScanCriteria({ id }), + updateInput: data, + }); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTypeUpdated, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTypeUpdated, items[0]); + return items[0]; } - fullyUpdateChallengeType.schema = { id: Joi.id(), - data: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isActive: Joi.boolean().required(), - isTask: Joi.boolean().default(false), - abbreviation: Joi.string().required() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isActive: Joi.boolean().required(), + isTask: Joi.boolean().default(false), + abbreviation: Joi.string().required(), + }) + .required(), +}; /** * Partially update challenge type. @@ -125,47 +165,63 @@ fullyUpdateChallengeType.schema = { * @param {Object} data the challenge type data to be updated * @returns {Object} the updated challenge type */ -async function partiallyUpdateChallengeType (id, data) { - const type = await helper.getById('ChallengeType', id) +async function partiallyUpdateChallengeType(id, data) { + const type = await getChallengeType(id); if (data.name && type.name.toLowerCase() !== data.name.toLowerCase()) { - await helper.validateDuplicate('ChallengeType', 'name', data.name) + const { items: existingByName } = await challengeTypeDomain.scan({ + criteria: getScanCriteria({ name: data.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Challenge Type with name ${data.name} already exists`); } if (data.abbreviation && type.abbreviation.toLowerCase() !== data.abbreviation.toLowerCase()) { - await helper.validateDuplicate('ChallengeType', 'abbreviation', data.abbreviation) + const { items: existingByAbbr } = await challengeTypeDomain.scan({ + criteria: getScanCriteria({ abbreviation: data.abbreviation }), + }); + if (existingByAbbr.length > 0) + throw new errors.ConflictError( + `Challenge Type with abbreviation ${data.abbreviation} already exists` + ); } - const ret = await helper.update(type, data) + const { items } = await challengeTypeDomain.update({ + filterCriteria: getScanCriteria({ id }), + updateInput: _.extend(type, data), + }); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTypeUpdated, _.assignIn({ id }, data)) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTypeUpdated, _.assignIn({ id }, data)); + return items[0]; } partiallyUpdateChallengeType.schema = { id: Joi.id(), - data: Joi.object().keys({ - name: Joi.string(), - description: Joi.string(), - isActive: Joi.boolean(), - isTask: Joi.boolean().default(false), - abbreviation: Joi.string() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string(), + description: Joi.string(), + isActive: Joi.boolean(), + isTask: Joi.boolean().default(false), + abbreviation: Joi.string(), + }) + .required(), +}; /** * Delete challenge type. * @param {String} id the challenge type id * @returns {Object} the deleted challenge type */ -async function deleteChallengeType (id) { - const ret = await helper.getById('ChallengeType', id) - await ret.delete() +async function deleteChallengeType(id) { + const { items } = await challengeTypeDomain.delete(getLookupCriteria("id", id)); + helper.flushInternalCache(); // post bus event - await helper.postBusEvent(constants.Topics.ChallengeTypeDeleted, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengeTypeDeleted, items[0]); + return items[0]; } deleteChallengeType.schema = { - id: Joi.id() -} + id: Joi.id(), +}; module.exports = { searchChallengeTypes, @@ -173,7 +229,11 @@ module.exports = { getChallengeType, fullyUpdateChallengeType, partiallyUpdateChallengeType, - deleteChallengeType -} - -// logger.buildService(module.exports) + deleteChallengeType, +}; + +// logger.buildService(module.exports, { +// tracing: { +// enabled: true +// } +// }) diff --git a/src/services/PhaseService.js b/src/services/PhaseService.js index f39bb70d..ac954192 100644 --- a/src/services/PhaseService.js +++ b/src/services/PhaseService.js @@ -1,72 +1,92 @@ /** * This service provides operations of phases. */ +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const helper = require('../common/helper') -// const logger = require('../common/logger') -const constants = require('../../app-constants') +const { PhaseDomain } = require("@topcoder-framework/domain-challenge"); + +const { + DomainHelper: { getScanCriteria, getLookupCriteria }, +} = require("@topcoder-framework/lib-common"); + +const _ = require("lodash"); +const Joi = require("joi"); +const helper = require("../common/helper"); +const logger = require("../common/logger"); +const constants = require("../../app-constants"); +const errors = require("../common/errors"); + +const phaseDomain = new PhaseDomain(GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT); /** * Search phases * @param {Object} criteria the search criteria * @returns {Object} the search result */ -async function searchPhases (criteria) { - const page = criteria.page || 1 - const perPage = criteria.perPage || 50 - const list = await helper.scanAll('Phase') - const records = _.filter(list, e => helper.partialMatch(criteria.name, e.name)) - const total = records.length - const result = records.slice((page - 1) * perPage, page * perPage) - - return { total, page, perPage, result } +async function searchPhases(criteria = {}) { + const scanCriteria = getScanCriteria(_.omit(criteria, ["page", "perPage"])); + + const page = criteria.page || 1; + const perPage = criteria.perPage || 50; + + const { items: list } = await phaseDomain.scan({ + criteria: scanCriteria, + }); + + const total = list.length; + const result = list.slice((page - 1) * perPage, page * perPage); + + return { total, page, perPage, result }; } searchPhases.schema = { criteria: Joi.object().keys({ page: Joi.page(), perPage: Joi.perPage().default(100), - name: Joi.string() - }) -} + name: Joi.string(), + }), +}; /** * Create phase. * @param {Object} phase the phase to created * @returns {Object} the created phase */ -async function createPhase (phase) { - await helper.validateDuplicate('Phase', 'name', phase.name) - const ret = await helper.create('Phase', _.assign({ id: uuid() }, phase)) +async function createPhase(phase) { + const { items: existingByName } = await phaseDomain.scan({ + criteria: getScanCriteria({ name: phase.name }), + }); + if (existingByName.length > 0) + throw new errors.ConflictError(`Phase with name ${phase.name} already exists`); + const ret = await phaseDomain.create(phase); // post bus event - await helper.postBusEvent(constants.Topics.ChallengePhaseCreated, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengePhaseCreated, ret); + return ret; } createPhase.schema = { - phase: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isOpen: Joi.boolean().required(), - duration: Joi.number().positive().required() - }).required() -} + phase: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isOpen: Joi.boolean().required(), + duration: Joi.number().positive().required(), + }) + .required(), +}; /** * Get phase * @param {String} phaseId the phase id * @returns {Object} the phase with given id */ -async function getPhase (phaseId) { - return helper.getById('Phase', phaseId) +async function getPhase(phaseId) { + return phaseDomain.lookup(getLookupCriteria("id", phaseId)); } getPhase.schema = { - phaseId: Joi.id() -} + phaseId: Joi.id(), +}; /** * Update phase. @@ -75,23 +95,31 @@ getPhase.schema = { * @param {Boolean} isFull the flag indicate it is a fully update operation. * @returns {Object} the updated phase */ -async function update (phaseId, data, isFull) { - const phase = await helper.getById('Phase', phaseId) +async function update(phaseId, data, isFull) { + const phase = await getPhase(phaseId); if (data.name && data.name.toLowerCase() !== phase.name.toLowerCase()) { - await helper.validateDuplicate('Phase', 'name', data.name) + const { items } = await phaseDomain.scan({ + criteria: getScanCriteria({ name: data.name }), + }); + if (items.length > 0) + throw new errors.ConflictError(`Phase with name ${phase.name} already exists`); } if (isFull) { // description is optional field, can be undefined - phase.description = data.description + phase.description = data.description; } - - const ret = await helper.update(phase, data) + const { items } = await phaseDomain.update({ + filterCriteria: getScanCriteria({ id: phaseId }), + updateInput: data, + }); // post bus event - await helper.postBusEvent(constants.Topics.ChallengePhaseUpdated, - isFull ? ret : _.assignIn({ id: phaseId }, data)) - return ret + await helper.postBusEvent( + constants.Topics.ChallengePhaseUpdated, + isFull ? items[0] : _.assignIn({ id: phaseId }, data) + ); + return items[0]; } /** @@ -100,19 +128,21 @@ async function update (phaseId, data, isFull) { * @param {Object} data the phase data to be updated * @returns {Object} the updated phase */ -async function fullyUpdatePhase (phaseId, data) { - return update(phaseId, data, true) +async function fullyUpdatePhase(phaseId, data) { + return update(phaseId, data, true); } fullyUpdatePhase.schema = { phaseId: Joi.id(), - data: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isOpen: Joi.boolean().required(), - duration: Joi.number().positive().required() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isOpen: Joi.boolean().required(), + duration: Joi.number().positive().required(), + }) + .required(), +}; /** * Partially update phase. @@ -120,44 +150,64 @@ fullyUpdatePhase.schema = { * @param {Object} data the phase data to be updated * @returns {Object} the updated phase */ -async function partiallyUpdatePhase (phaseId, data) { - return update(phaseId, data) +async function partiallyUpdatePhase(phaseId, data) { + return update(phaseId, data); } partiallyUpdatePhase.schema = { phaseId: Joi.id(), - data: Joi.object().keys({ - name: Joi.string(), - description: Joi.string(), - isOpen: Joi.boolean(), - duration: Joi.number().positive() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string(), + description: Joi.string(), + isOpen: Joi.boolean(), + duration: Joi.number().positive(), + }) + .required(), +}; /** * Delete phase. * @param {String} phaseId the phase id * @returns {Object} the deleted phase */ -async function deletePhase (phaseId) { - const ret = await helper.getById('Phase', phaseId) - await ret.delete() +async function deletePhase(phaseId) { + const { items } = await phaseDomain.delete(getLookupCriteria("id", phaseId)); // post bus event - await helper.postBusEvent(constants.Topics.ChallengePhaseDeleted, ret) - return ret + await helper.postBusEvent(constants.Topics.ChallengePhaseDeleted, items[0]); + return items[0]; } deletePhase.schema = { - phaseId: Joi.id() + phaseId: Joi.id(), +}; + +async function validatePhases(phases) { + if (!phases || phases.length === 0) { + return; + } + const searchPhasesResult = await searchPhases(); // get all phases + const records = searchPhasesResult.result; + const map = new Map(); + _.each(records, (r) => { + map.set(r.id, r); + }); + const invalidPhases = _.filter(phases, (p) => !map.has(p.phaseId)); + if (invalidPhases.length > 0) { + throw new errors.BadRequestError( + `The following phases are invalid: ${toString(invalidPhases)}` + ); + } } module.exports = { searchPhases, createPhase, + validatePhases, getPhase, fullyUpdatePhase, partiallyUpdatePhase, - deletePhase -} + deletePhase, +}; -// logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/src/services/SupportService.js b/src/services/SupportService.js index c245cee8..bdf6a62f 100644 --- a/src/services/SupportService.js +++ b/src/services/SupportService.js @@ -2,11 +2,11 @@ * This service provides operations of support requests. */ -const _ = require('lodash') -const Joi = require('joi') -const config = require('config') -const helper = require('../common/helper') -const logger = require('../common/logger') +const _ = require("lodash"); +const Joi = require("joi"); +const config = require("config"); +const helper = require("../common/helper"); +const logger = require("../common/logger"); /** * Create a request in zendesk @@ -14,35 +14,37 @@ const logger = require('../common/logger') * @param {Object} request the request * @returns {Object} the search result */ -async function createRequest (currentUser, request) { - let subject +async function createRequest(currentUser, request) { + let subject; if (request.isSelfService) { - subject += 'Self-Service customer support request' + subject += "Self-Service customer support request"; } else { - subject = 'General support request' + subject = "General support request"; } if (request.challengeId) { - subject += ` for Challenge ID: ${request.challengeId}` + subject += ` for Challenge ID: ${request.challengeId}`; } - return await helper.submitZendeskRequest({ + return helper.submitZendeskRequest({ requester: { name: `${request.firstName} ${request.lastName}`, - email: request.email + email: request.email, }, subject, comment: { - body: request.question + body: request.question, }, priority: config.ZENDESK_DEFAULT_PRIORITY, - ...(request.isSelfService && config.ZENDESK_CUSTOM_FIELD_TAG_ID ? { - custom_fields: [ - { - id: _.toNumber(config.ZENDESK_CUSTOM_FIELD_TAG_ID), - value: 'self_service' + ...(request.isSelfService && config.ZENDESK_CUSTOM_FIELD_TAG_ID + ? { + custom_fields: [ + { + id: _.toNumber(config.ZENDESK_CUSTOM_FIELD_TAG_ID), + value: "self_service", + }, + ], } - ] - } : {}) - }) + : {}), + }); } createRequest.schema = { @@ -53,12 +55,12 @@ createRequest.schema = { firstName: Joi.string().required(), lastName: Joi.string().required(), isSelfService: Joi.boolean(), - question: Joi.string().required() - }) -} + question: Joi.string().required(), + }), +}; module.exports = { - createRequest -} + createRequest, +}; -logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/src/services/TimelineTemplateService.js b/src/services/TimelineTemplateService.js index 65479518..a58b5a97 100644 --- a/src/services/TimelineTemplateService.js +++ b/src/services/TimelineTemplateService.js @@ -2,78 +2,117 @@ * This service provides operations of timeline template. */ -const _ = require('lodash') -const Joi = require('joi') -const uuid = require('uuid/v4') -const helper = require('../common/helper') -const phaseHelper = require('../common/phase-helper') -// const logger = require('../common/logger') -const constants = require('../../app-constants') +const { GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT } = process.env; + +const { + DomainHelper: { getScanCriteria, getLookupCriteria }, +} = require("@topcoder-framework/lib-common"); + +const { TimelineTemplateDomain } = require("@topcoder-framework/domain-challenge"); + +const _ = require("lodash"); +const Joi = require("joi"); +const helper = require("../common/helper"); +const logger = require("../common/logger"); +const constants = require("../../app-constants"); +const errors = require("../common/errors"); + +const PhaseService = require("./PhaseService"); + +const timelineTemplateDomain = new TimelineTemplateDomain( + GRPC_CHALLENGE_SERVER_HOST, + GRPC_CHALLENGE_SERVER_PORT +); + +module.exports = {}; /** * Search timeline templates. * @param {Object} criteria the search criteria - * @returns {Object} the search result + * @returns {Promise} the search result */ -async function searchTimelineTemplates (criteria) { - const page = criteria.page || 1 - const perPage = criteria.perPage || 50 - const list = await helper.scanAll('TimelineTemplate') - const records = _.filter(list, e => helper.partialMatch(criteria.name, e.name)) - const total = records.length - const result = records.slice((page - 1) * perPage, page * perPage) - - return { total, page, perPage, result } +async function searchTimelineTemplates(criteria) { + const scanCriteria = getScanCriteria(_.omit(criteria, ["page", "perPage"])); + + const page = criteria.page || 1; + const perPage = criteria.perPage || 50; + const { items } = await timelineTemplateDomain.scan({ + criteria: scanCriteria, + }); + + const total = items.length; + const result = items.slice((page - 1) * perPage, page * perPage); + + return { total, page, perPage, result }; } searchTimelineTemplates.schema = { criteria: Joi.object().keys({ page: Joi.page(), perPage: Joi.perPage(), - name: Joi.string() - }) -} + name: Joi.string(), + }), +}; /** * Create timeline template. * @param {Object} timelineTemplate the timeline template to created * @returns {Object} the created timeline template */ -async function createTimelineTemplate (timelineTemplate) { - await helper.validateDuplicate('TimelineTemplate', 'name', timelineTemplate.name) - await phaseHelper.validatePhases(timelineTemplate.phases) +async function createTimelineTemplate(timelineTemplate) { + const scanCriteria = getScanCriteria({ + name: timelineTemplate.name, + }); + + const existing = await timelineTemplateDomain.scan({ criteria: scanCriteria }); - const ret = await helper.create('TimelineTemplate', _.assign({ id: uuid() }, timelineTemplate)) + if (existing && existing.items.length) { + throw new errors.ConflictError( + `Timeline template with name ${timelineTemplate.name} already exists` + ); + } + + // Do not validate phases for now + // await phaseHelper.validatePhases(timelineTemplate.phases); + + const ret = await timelineTemplateDomain.create(timelineTemplate); // post bus event - await helper.postBusEvent(constants.Topics.TimelineTemplateCreated, ret) - return ret + await helper.postBusEvent(constants.Topics.TimelineTemplateCreated, ret); + return ret; } createTimelineTemplate.schema = { - timelineTemplate: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isActive: Joi.boolean().required(), - phases: Joi.array().items(Joi.object().keys({ - phaseId: Joi.id(), - predecessor: Joi.optionalId(), - defaultDuration: Joi.number().positive().required() - })).min(1).required() - }).required() -} + timelineTemplate: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isActive: Joi.boolean().required(), + phases: Joi.array() + .items( + Joi.object().keys({ + phaseId: Joi.id(), + predecessor: Joi.optionalId(), + defaultDuration: Joi.number().positive().required(), + }) + ) + .min(1) + .required(), + }) + .required(), +}; /** * Get timeline template. * @param {String} timelineTemplateId the timeline template id * @returns {Object} the timeline template with given id */ -async function getTimelineTemplate (timelineTemplateId) { - return helper.getById('TimelineTemplate', timelineTemplateId) +async function getTimelineTemplate(timelineTemplateId) { + return timelineTemplateDomain.lookup(getLookupCriteria("id", timelineTemplateId)); } getTimelineTemplate.schema = { - timelineTemplateId: Joi.id() -} + timelineTemplateId: Joi.id(), +}; /** * Update timeline template. @@ -82,27 +121,42 @@ getTimelineTemplate.schema = { * @param {Boolean} isFull the flag indicate it is a fully update operation. * @returns {Object} the updated timeline template */ -async function update (timelineTemplateId, data, isFull) { - const timelineTemplate = await helper.getById('TimelineTemplate', timelineTemplateId) +async function update(timelineTemplateId, data, isFull) { + const timelineTemplate = await getTimelineTemplate(timelineTemplateId); if (data.name && data.name.toLowerCase() !== timelineTemplate.name.toLowerCase()) { - await helper.validateDuplicate('TimelineTemplate', 'name', data.name) + const { items: existingByName } = await timelineTemplateDomain.scan({ + criteria: getScanCriteria({ name: data.name }), + }); + + if (existingByName.length > 1) + throw new errors.ConflictError(`Timeline template with name ${data.name} already exists`); + else if (existingByName.length === 1 && existingByName[0].id !== timelineTemplateId) + throw new errors.ConflictError(`Timeline template with name ${data.name} already exists`); } if (data.phases) { - await phaseHelper.validatePhases(data.phases) + await PhaseService.validatePhases(data.phases); } if (isFull) { // description is optional field, can be undefined - timelineTemplate.description = data.description + timelineTemplate.description = data.description; + } else { + data = { ...timelineTemplate, ...data }; } - const ret = await helper.update(timelineTemplate, data) + const { items } = await timelineTemplateDomain.update({ + filterCriteria: getScanCriteria({ id: timelineTemplateId }), + updateInput: data, + }); + // post bus event - await helper.postBusEvent(constants.Topics.TimelineTemplateUpdated, - isFull ? ret : _.assignIn({ id: timelineTemplateId }, data)) - return ret + await helper.postBusEvent( + constants.Topics.TimelineTemplateUpdated, + isFull ? items[0] : _.assignIn({ id: timelineTemplateId }, data) + ); + return items[0]; } /** @@ -111,23 +165,30 @@ async function update (timelineTemplateId, data, isFull) { * @param {Object} data the timeline template data to be updated * @returns {Object} the updated timeline template */ -async function fullyUpdateTimelineTemplate (timelineTemplateId, data) { - return update(timelineTemplateId, data, true) +async function fullyUpdateTimelineTemplate(timelineTemplateId, data) { + return update(timelineTemplateId, data, true); } fullyUpdateTimelineTemplate.schema = { timelineTemplateId: Joi.id(), - data: Joi.object().keys({ - name: Joi.string().required(), - description: Joi.string(), - isActive: Joi.boolean().required(), - phases: Joi.array().items(Joi.object().keys({ - phaseId: Joi.id(), - predecessor: Joi.optionalId(), - defaultDuration: Joi.number().positive().required() - })).min(1).required() - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string().required(), + description: Joi.string(), + isActive: Joi.boolean().required(), + phases: Joi.array() + .items( + Joi.object().keys({ + phaseId: Joi.id(), + predecessor: Joi.optionalId(), + defaultDuration: Joi.number().positive().required(), + }) + ) + .min(1) + .required(), + }) + .required(), +}; /** * Partially update timeline template. @@ -135,48 +196,53 @@ fullyUpdateTimelineTemplate.schema = { * @param {Object} data the timeline template data to be updated * @returns {Object} the updated timeline template */ -async function partiallyUpdateTimelineTemplate (timelineTemplateId, data) { - return update(timelineTemplateId, data) +async function partiallyUpdateTimelineTemplate(timelineTemplateId, data) { + return update(timelineTemplateId, data); } partiallyUpdateTimelineTemplate.schema = { timelineTemplateId: Joi.id(), - data: Joi.object().keys({ - name: Joi.string(), - description: Joi.string(), - isActive: Joi.boolean(), - phases: Joi.array().items(Joi.object().keys({ - phaseId: Joi.id(), - predecessor: Joi.optionalId(), - defaultDuration: Joi.number().positive().required() - })).min(1) - }).required() -} + data: Joi.object() + .keys({ + name: Joi.string(), + description: Joi.string(), + isActive: Joi.boolean(), + phases: Joi.array() + .items( + Joi.object().keys({ + phaseId: Joi.id(), + predecessor: Joi.optionalId(), + defaultDuration: Joi.number().positive().required(), + }) + ) + .min(1), + }) + .required(), +}; /** * Delete timeline template. * @param {String} timelineTemplateId the timeline template id * @returns {Object} the deleted timeline template */ -async function deleteTimelineTemplate (timelineTemplateId) { - const ret = await helper.getById('TimelineTemplate', timelineTemplateId) - await ret.delete() +async function deleteTimelineTemplate(timelineTemplateId) { + const { items } = await timelineTemplateDomain.delete( + getLookupCriteria("id", timelineTemplateId) + ); // post bus event - await helper.postBusEvent(constants.Topics.TimelineTemplateDeleted, ret) - return ret + await helper.postBusEvent(constants.Topics.TimelineTemplateDeleted, items[0]); + return items[0]; } deleteTimelineTemplate.schema = { - timelineTemplateId: Joi.id() -} + timelineTemplateId: Joi.id(), +}; -module.exports = { - searchTimelineTemplates, - createTimelineTemplate, - getTimelineTemplate, - fullyUpdateTimelineTemplate, - partiallyUpdateTimelineTemplate, - deleteTimelineTemplate -} +module.exports.searchTimelineTemplates = searchTimelineTemplates; +module.exports.createTimelineTemplate = createTimelineTemplate; +module.exports.getTimelineTemplate = getTimelineTemplate; +module.exports.fullyUpdateTimelineTemplate = fullyUpdateTimelineTemplate; +module.exports.partiallyUpdateTimelineTemplate = partiallyUpdateTimelineTemplate; +module.exports.deleteTimelineTemplate = deleteTimelineTemplate; -// logger.buildService(module.exports) +logger.buildService(module.exports); diff --git a/test/testHelper.js b/test/testHelper.js index 129fb545..7e637316 100644 --- a/test/testHelper.js +++ b/test/testHelper.js @@ -1,20 +1,20 @@ /** * This file defines common helper methods used for tests */ -const _ = require('lodash') -const uuid = require('uuid/v4') -const config = require('config') -const helper = require('../src/common/helper') -const constants = require('../app-constants') +const _ = require("lodash"); +const uuid = require("uuid/v4"); +const config = require("config"); +const helper = require("../src/common/helper"); +const constants = require("../app-constants"); -const esClient = helper.getESClient() +const esClient = helper.getESClient(); -let challengeType -let phase -let phase2 -let timelineTemplate -let challenge -let completedChallenge +let challengeType; +let phase; +let phase2; +let timelineTemplate; +let challenge; +let completedChallenge; /** * function to deeply compare arrays regardeless of the order @@ -24,164 +24,173 @@ let completedChallenge * @returns {Boolean} The flag indicating whether the arrays have the same content regardless of the order */ const deepCompareArrays = (arr1, arr2) => { - return _(arr1).xorWith(arr2, _.isEqual).isEmpty() -} + return _(arr1).xorWith(arr2, _.isEqual).isEmpty(); +}; /** * Create test data */ -async function createData () { - challengeType = await helper.create('ChallengeType', { +async function createData() { + challengeType = await helper.create("ChallengeType", { id: uuid(), name: `type-${new Date().getTime()}`, - description: 'desc', + description: "desc", isActive: true, - abbreviation: 'abbr', - legacyId: 123 - }) - phase = await helper.create('Phase', { + abbreviation: "abbr", + legacyId: 123, + }); + phase = await helper.create("Phase", { id: uuid(), name: `phase-${new Date().getTime()}`, - description: 'desc', + description: "desc", isOpen: true, - duration: 123 - }) - phase2 = await helper.create('Phase', { + duration: 123, + }); + phase2 = await helper.create("Phase", { id: uuid(), name: `phase2-${new Date().getTime()}`, - description: 'desc', + description: "desc", isOpen: true, - duration: 432 - }) - timelineTemplate = await helper.create('TimelineTemplate', { + duration: 432, + }); + timelineTemplate = await helper.create("TimelineTemplate", { id: uuid(), name: `tt-${new Date().getTime()}`, - description: 'desc', + description: "desc", isActive: true, - phases: [{ - phaseId: phase.id, - defaultDuration: 10000 - }, { - phaseId: phase2.id, - predecessor: phase.id, - defaultDuration: 20000 - }] - }) - const nm = `a B c challenge${new Date().getTime()}` + phases: [ + { + phaseId: phase.id, + defaultDuration: 10000, + }, + { + phaseId: phase2.id, + predecessor: phase.id, + defaultDuration: 20000, + }, + ], + }); + const nm = `a B c challenge${new Date().getTime()}`; const challengeData = { id: uuid(), typeId: challengeType.id, name: nm, - description: 'desc', - metadata: [{ name: nm, value: 'value' }], + description: "desc", + metadata: [{ name: nm, value: "value" }], timelineTemplateId: timelineTemplate.id, phases: [phase], - prizeSets: [{ - type: constants.prizeSetTypes.ChallengePrizes, - description: 'ddd', - prizes: [{ - description: 'some prize', - type: 'type', - value: 800 - }] - }], - tags: ['tag1'], + prizeSets: [ + { + type: constants.prizeSetTypes.ChallengePrizes, + description: "ddd", + prizes: [ + { + description: "some prize", + type: "type", + value: 800, + }, + ], + }, + ], + tags: ["tag1"], projectId: 111, legacy: { - track: 'track', - reviewType: 'Virus Scan', - forumId: 123456 + track: "track", + reviewType: "Virus Scan", + forumId: 123456, }, legacyId: 222, startDate: new Date(), status: constants.challengeStatuses.Active, - groups: ['group1'], - gitRepoURLs: ['https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'], + groups: ["group1"], + gitRepoURLs: ["https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"], created: new Date(), - createdBy: 'admin' - } + createdBy: "admin", + }; - challenge = await helper.create('Challenge', challengeData) - completedChallenge = await helper.create('Challenge', _.assign(challengeData, { id: uuid(), status: constants.challengeStatuses.Completed })) + challenge = await helper.create("Challenge", challengeData); + completedChallenge = await helper.create( + "Challenge", + _.assign(challengeData, { id: uuid(), status: constants.challengeStatuses.Completed }) + ); // create challenge in Elasticsearch await esClient.create({ index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, id: challenge.id, body: _.assignIn({ numOfSubmissions: 0, numOfRegistrants: 0 }, challenge.originalItem()), - refresh: 'true' // refresh ES so that it is visible for read operations instantly - }) + refresh: "true", // refresh ES so that it is visible for read operations instantly + }); // create completedChallenge in Elasticsearch await esClient.create({ index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, id: completedChallenge.id, - body: _.assignIn({ numOfSubmissions: 0, numOfRegistrants: 0 }, completedChallenge.originalItem()), - refresh: 'true' // refresh ES so that it is visible for read operations instantly - }) + body: _.assignIn( + { numOfSubmissions: 0, numOfRegistrants: 0 }, + completedChallenge.originalItem() + ), + refresh: "true", // refresh ES so that it is visible for read operations instantly + }); } const defaultProjectTerms = [ { - id: '0fcb41d1-ec7c-44bb-8f3b-f017a61cd708', - title: 'Competition Non-Disclosure Agreement', - url: '', - text: 'docusign NDA', - docusignTemplateId: '0c5b7081-1fff-4484-a20f-824c97a03b9b', - agreeabilityType: 'DocuSignable' + id: "0fcb41d1-ec7c-44bb-8f3b-f017a61cd708", + title: "Competition Non-Disclosure Agreement", + url: "", + text: "docusign NDA", + docusignTemplateId: "0c5b7081-1fff-4484-a20f-824c97a03b9b", + agreeabilityType: "DocuSignable", }, { - id: 'be0652ae-8b28-4e91-9b42-8ad00b31e9cb', - title: 'Subcontractor Services Agreement 2009-09-02', - url: 'http://www.topcoder.com/i/terms/Subcontractor+Services+Agreement+2009-09-02.pdf', - text: 'Subcontractor Services Agreement 2009-09-02. This agreement is unavailable in text format. Please download the PDF to read its contents', - agreeabilityType: 'Non-electronically-agreeable' - } -] + id: "be0652ae-8b28-4e91-9b42-8ad00b31e9cb", + title: "Subcontractor Services Agreement 2009-09-02", + url: "http://www.topcoder.com/i/terms/Subcontractor+Services+Agreement+2009-09-02.pdf", + text: "Subcontractor Services Agreement 2009-09-02. This agreement is unavailable in text format. Please download the PDF to read its contents", + agreeabilityType: "Non-electronically-agreeable", + }, +]; -const mockTerms = ['8a0207fc-ac9b-47e7-af1b-81d1ccaf0afc', '453c7c5c-c872-4672-9e78-5162d70903d3'] +const mockTerms = ["8a0207fc-ac9b-47e7-af1b-81d1ccaf0afc", "453c7c5c-c872-4672-9e78-5162d70903d3"]; const additionalTerm = { - id: '28841de8-2f42-486f-beac-21d46a832ab6', - agreeabilityType: 'Electronically-agreeable', - title: '2008 TCO Marathon Match Competition Official Rules', - url: 'http://topcoder.com/mm-terms' -} + id: "28841de8-2f42-486f-beac-21d46a832ab6", + agreeabilityType: "Electronically-agreeable", + title: "2008 TCO Marathon Match Competition Official Rules", + url: "http://topcoder.com/mm-terms", +}; /** * Clear test data */ -async function clearData () { +async function clearData() { // remove challenge in Elasticsearch await esClient.delete({ index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, id: challenge.id, - refresh: 'true' // refresh ES so that it is effective for read operations instantly - }) + refresh: "true", // refresh ES so that it is effective for read operations instantly + }); // remove completedChallenge in Elasticsearch await esClient.delete({ index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, id: completedChallenge.id, - refresh: 'true' // refresh ES so that it is effective for read operations instantly - }) - - await challenge.delete() - await completedChallenge.delete() - await timelineTemplate.delete() - await phase.delete() - await phase2.delete() - await challengeType.delete() + refresh: "true", // refresh ES so that it is effective for read operations instantly + }); + + await challenge.delete(); + await completedChallenge.delete(); + await timelineTemplate.delete(); + await phase.delete(); + await phase2.delete(); + await challengeType.delete(); } /** * Get created test data. */ -function getData () { +function getData() { return { challengeType: challengeType.originalItem(), phase: phase.originalItem(), @@ -191,15 +200,15 @@ function getData () { completedChallenge: completedChallenge.originalItem(), defaultProjectTerms, additionalTerm, - mockTerms - } + mockTerms, + }; } /** * Get dates difference in milliseconds */ -function getDatesDiff (d1, d2) { - return new Date(d1).getTime() - new Date(d2).getTime() +function getDatesDiff(d1, d2) { + return new Date(d1).getTime() - new Date(d2).getTime(); } module.exports = { @@ -207,5 +216,5 @@ module.exports = { clearData, getData, getDatesDiff, - deepCompareArrays -} + deepCompareArrays, +}; diff --git a/yarn.lock b/yarn.lock index fceae5d0..93941524 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,123 +2,125 @@ # yarn lockfile v1 -"@aws-sdk/service-error-classification@^3.4.1": - version "3.254.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/service-error-classification/-/service-error-classification-3.254.0.tgz#1cf2a2e79fd73d48e751207e25527988dd6716d1" - integrity sha512-8GHqMJBBF9yoMBG/Nf9PusUSMFjG8ygps/cSJPlgcG2vbFn8BCdBZVc4ptXqICZUnBB/6lrxy8nCmNUaru48jg== - -"@aws-sdk/types@^3.4.1": - version "3.254.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.254.0.tgz#760b4a876efa2edcec191dd8b18b989fa717a42e" - integrity sha512-xDEDk6ZAGFO0URPgB6R2mvQANYlojHLjLC9zzOzl07F+uqYS30yZDIg4UFcqPt/x48v7mxlKZpbaZgYI2ZLgGA== - dependencies: - tslib "^2.3.1" +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" -"@babel/generator@^7.20.7", "@babel/generator@^7.4.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" - integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== +"@babel/generator@^7.23.6", "@babel/generator@^7.4.0": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.23.6" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.20.13", "@babel/parser@^7.20.7", "@babel/parser@^7.4.3": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088" - integrity sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw== +"@babel/parser@^7.23.9", "@babel/parser@^7.4.3": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" + integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== -"@babel/template@^7.18.10", "@babel/template@^7.4.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== +"@babel/runtime@^7.15.4": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" + integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.15", "@babel/template@^7.4.0": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" + integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" "@babel/traverse@^7.4.3": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" - debug "^4.1.0" + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" + integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.7", "@babel/types@^7.4.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.4.0": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" + integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== "@dabh/diagnostics@^2.0.2": version "2.0.3" @@ -129,74 +131,268 @@ enabled "2.0.x" kuler "^2.0.0" +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== + +"@grpc/grpc-js@^1.8.0", "@grpc/grpc-js@^1.8.12", "@grpc/grpc-js@^1.8.7": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.10.0.tgz#23b5defd6a021a20924b4baca2dd1b3a3ecc3716" + integrity sha512-tx+eoEsqkMkLCHR4OOplwNIaJ7SVZWzeVKzEMBz8VR+TbssgBYOP4a0P+KQiQ6LaTG4SGaIEu7YTS8xOmkOWLA== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.10" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.10.tgz#6bf26742b1b54d0a473067743da5d3189d06d720" + integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" + +"@humanwhocodes/config-array@^0.11.13": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== + "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@opensearch-project/opensearch@^2.2.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-2.5.0.tgz#0fff1fc5c6aa94b0cf87d132573a14eeb5e1df7f" + integrity sha512-RY5J6Jt/Jbbr2F9XByGY9LJr0VNmXJjgVvvntpKE4NtZa/r9ak3o8YtGK1iey1yHgzMzze25598qq7ZYFk42DA== + dependencies: + aws4 "^1.11.0" + debug "^4.3.1" + hpagent "^1.2.0" + ms "^2.1.3" + secure-json-parse "^2.4.0" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@topcoder-framework/client-relational@^0.24.1": + version "0.24.1" + resolved "https://topcoder-409275337247.d.codeartifact.us-east-1.amazonaws.com/npm/topcoder-framework/@topcoder-framework/client-relational/-/client-relational-0.24.1.tgz#a01c4bb5a6117c38d5b61e1a79ded58abeb5b7c5" + integrity sha512-yZJS2N6l1YT/wadWRgMhMzrtFNfPgCBMVLUPO2ORHq182xXSIBkpXLRvX99z2XVPRGpjCVkfumh+jANQKX8AXw== + dependencies: + "@grpc/grpc-js" "^1.8.0" + "@topcoder-framework/lib-common" "^0.24.1" + topcoder-proto-registry "0.2.0" + tslib "^2.4.1" + +"@topcoder-framework/domain-acl@^0.24.0": + version "0.24.1" + resolved "https://topcoder-409275337247.d.codeartifact.us-east-1.amazonaws.com/npm/topcoder-framework/@topcoder-framework/domain-acl/-/domain-acl-0.24.1.tgz#61435da8d28756a319bc31bfd4f5b4458614f64f" + integrity sha512-7+uvCxkfURuXsBIwQBvn9aBobBheZCKlKQWzH4Dk5sCBxyhbjQ0B+TkFnfn2+65NzblwuvYEAEnJdLyAYu4RPA== + dependencies: + "@grpc/grpc-js" "^1.8.7" + "@topcoder-framework/client-relational" "^0.24.1" + "@topcoder-framework/lib-common" "^0.24.1" + topcoder-proto-registry "0.2.0" + tslib "^2.4.1" + +"@topcoder-framework/domain-challenge@^0.24.1": + version "0.24.1" + resolved "https://topcoder-409275337247.d.codeartifact.us-east-1.amazonaws.com/npm/topcoder-framework/@topcoder-framework/domain-challenge/-/domain-challenge-0.24.1.tgz#912cb9c7903236345ee036574e2fe82fe810b659" + integrity sha512-Zp7YEqCMxBel+VHFsmg9C7ZoZwkq5xMB3V0e2OxRdd0QZwyzXHll66NsajPNpkkEAAaMmzMmZTbKxF6KTJ97GQ== + dependencies: + "@grpc/grpc-js" "^1.8.0" + "@topcoder-framework/client-relational" "^0.24.1" + "@topcoder-framework/lib-common" "^0.24.1" + topcoder-proto-registry "0.2.0" + tslib "^2.4.1" + +"@topcoder-framework/lib-common@^0.24.1": + version "0.24.1" + resolved "https://topcoder-409275337247.d.codeartifact.us-east-1.amazonaws.com/npm/topcoder-framework/@topcoder-framework/lib-common/-/lib-common-0.24.1.tgz#fc69af0f3deb263d347bfb8ac014065c5a7ceeec" + integrity sha512-Av/v5YybzyrJlhxANFxy+uJR938OWzd4vkcBZvAWmY4wX9D8UOiBA1nF2EMZ5+9xhY+PD3O/yuqnfqUs/4qT+g== + dependencies: + "@grpc/grpc-js" "^1.8.0" + rimraf "^3.0.2" + topcoder-proto-registry "0.2.0" + tslib "^2.4.1" + "@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== dependencies: "@types/connect" "*" "@types/node" "*" "@types/chai@4": - version "4.3.4" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" - integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== - -"@types/cls-hooked@^4.3.3": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@types/cls-hooked/-/cls-hooked-4.3.3.tgz#c09e2f8dc62198522eaa18a5b6b873053154bd00" - integrity sha512-gNstDTb/ty5h6gJd6YpSPgsLX9LmRpaKJqGFp7MRlYxhwp4vXXKlJ9+bt1TZ9KbVNXE+Mbxy2AYXcpY21DDtJw== - dependencies: - "@types/node" "*" + version "4.3.11" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c" + integrity sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ== "@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" "@types/cookiejar@*": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" - integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" + integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== "@types/express-jwt@0.0.42": version "0.0.42" @@ -206,14 +402,15 @@ "@types/express" "*" "@types/express-unless" "*" -"@types/express-serve-static-core@^4.17.31": - version "4.17.32" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" - integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA== +"@types/express-serve-static-core@^4.17.33": + version "4.17.43" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz#10d8444be560cb789c4735aea5eac6e5af45df54" + integrity sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" + "@types/send" "*" "@types/express-unless@*": version "2.0.1" @@ -223,72 +420,97 @@ express-unless "*" "@types/express@*": - version "4.17.15" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff" - integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ== + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.31" + "@types/express-serve-static-core" "^4.17.33" "@types/qs" "*" "@types/serve-static" "*" -"@types/mime@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== -"@types/mysql@*": - version "2.15.21" - resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.21.tgz#7516cba7f9d077f980100c85fd500c8210bd5e45" - integrity sha512-NPotx5CVful7yB+qZbWtXL2fA4e7aEHkihHLjklc6ID8aq7bhguHgeIoC1EmSNTAuCgI6ZXrjt2ZSaXnYX0EUg== - dependencies: - "@types/node" "*" +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*": - version "18.11.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" - integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== +"@types/mime@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" + integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== -"@types/node@11.11.0": - version "11.11.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.0.tgz#070e9ce7c90e727aca0e0c14e470f9a93ffe9390" - integrity sha512-D5Rt+HXgEywr3RQJcGlZUCTCx1qVbCZpVk3/tOOA6spLNZdGm8BU+zRgdRYDoF1pO3RuXLxADzMrF903JlQXqg== +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== -"@types/pg@*": - version "8.6.6" - resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.6.tgz#21cdf873a3e345a6e78f394677e3b3b1b543cb80" - integrity sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw== +"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "20.11.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.17.tgz#cdd642d0e62ef3a861f88ddbc2b61e32578a9292" + integrity sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw== dependencies: - "@types/node" "*" - pg-protocol "*" - pg-types "^2.2.0" + undici-types "~5.26.4" "@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + version "6.9.11" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" + integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" "@types/serve-static@*": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" - integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + version "1.15.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" + integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== dependencies: + "@types/http-errors" "*" "@types/mime" "*" "@types/node" "*" -"@types/superagent@^3.8.3": - version "3.8.7" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.8.7.tgz#1f1ed44634d5459b3a672eb7235a8e7cfd97704c" - integrity sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA== +"@types/superagent@4.1.13": + version "4.1.13" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.13.tgz#0aaa3f4ff9404b94932d1dcdfb7f3d39d23997a0" + integrity sha512-YIGelp3ZyMiH0/A09PMAORO0EBGlF5xIKfDpK74wdYvWUs2o96b5CItJcWPdH409b7SAXIIG6p8NdU/4U2Maww== dependencies: "@types/cookiejar" "*" "@types/node" "*" +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -297,15 +519,28 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-jsx@^5.0.0: +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^6.0.2: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +acorn-walk@^8.0.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +acorn@^8.1.0, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== agent-base@6: version "6.0.2" @@ -321,12 +556,7 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -ajv-keywords@^3.0.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^6.0.1, ajv@^6.12.3, ajv@^6.5.0: +ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -341,11 +571,6 @@ ansi-colors@3.2.3: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -361,6 +586,11 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -373,6 +603,21 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -392,33 +637,118 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-includes@^3.0.3, array-includes@^3.1.1: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" -array.prototype.reduce@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" - integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== +array.prototype.filter@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz#423771edeb417ff5914111fff4277ea0624c0d0e" + integrity sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +array.prototype.findlastindex@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz#d1c50f0b3a9da191981ff8942a0aedd82794404f" + integrity sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.reduce@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.6.tgz#63149931808c5fc1e1354814923d92d45f7d96d5" + integrity sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +array.prototype.tosorted@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8" + integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.1.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + asn1@~0.2.3: version "0.2.6" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" @@ -436,52 +766,32 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -async-hook-jl@^1.7.6: - version "1.7.6" - resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" - integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== - dependencies: - stack-chain "^1.3.7" - async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== + dependencies: + has-symbols "^1.0.3" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -atomic-batcher@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/atomic-batcher/-/atomic-batcher-1.0.2.tgz#d16901d10ccec59516c197b9ccd8930689b813b4" - integrity sha512-EFGCRj4kLX1dHv1cDzTk+xbjBFj1GnJDpui52YmEcxxHHEWjYyT6l51U7n6WQ28osZH4S9gSybxe56Vm7vB61Q== - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - -aws-sdk@2.395.0: - version "2.395.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.395.0.tgz#637e5fa06d69bfb923b17bde24a8bd2a74dedab3" - integrity sha512-ldTTjctniZT4E2lq2z3D8Y2u+vpkp+laoEnDkXgjKXTKbiJ0QEtfWsUdx/IQ7awCt8stoxyqZK47DJOxIbRNoA== - dependencies: - buffer "4.9.1" - events "1.1.1" - ieee754 "1.1.8" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" +available-typed-arrays@^1.0.5, available-typed-arrays@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" + integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== aws-sdk@^2.1145.0: - version "2.1299.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1299.0.tgz#493254104af5b9aaee0d7cd9150611d137e4f205" - integrity sha512-xTh6pmCUEJljkFfTM3sE8UozDxal80uX/5WZl8GcjQ+NbrGeQEdvL6wFWBwEEVbhR0VBVuU37cKPuQlfENbRYA== + version "2.1555.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1555.0.tgz#7949469e60bde38a7f0662fd6c585e274a061ece" + integrity sha512-hjYs1MQkJxdHnoZm8hypqGy4PQKWVUs19McdXRXWNXr97V0il4xcUpIfvjHQ9x9EjP0p/jyIx9/BtyrR68jnUQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -492,61 +802,26 @@ aws-sdk@^2.1145.0: url "0.10.3" util "^0.12.4" uuid "8.0.0" - xml2js "0.4.19" + xml2js "0.6.2" aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== -aws-xray-sdk-core@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/aws-xray-sdk-core/-/aws-xray-sdk-core-3.4.1.tgz#25592b7e4ed3ebd25ece9c1f258d3f68558e1ab6" - integrity sha512-HPQ+9GMF+yhDDXNKPp6HGAIv2yVapKTI7WRs2aN3TT+RxQkbmgO+3JlcMvRL2/lu0RqTVWoYCIhmF9/H/zRL1A== - dependencies: - "@aws-sdk/service-error-classification" "^3.4.1" - "@aws-sdk/types" "^3.4.1" - "@types/cls-hooked" "^4.3.3" - atomic-batcher "^1.0.2" - cls-hooked "^4.2.2" - semver "^5.3.0" - -aws-xray-sdk-express@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/aws-xray-sdk-express/-/aws-xray-sdk-express-3.4.1.tgz#e958976db0631b93feaea5db38979f461baea707" - integrity sha512-c8efb0ywsvzcqXT0PLbaAG0nMgpbzGu9FOagm569Ic/GMJwglACVAxsHJ3e3fYlRDbq2q6U0QhadqGb4MOa9YQ== - dependencies: - "@types/express" "*" - -aws-xray-sdk-mysql@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/aws-xray-sdk-mysql/-/aws-xray-sdk-mysql-3.4.1.tgz#8f2eab61f020953742d09a42a96994e08028ba75" - integrity sha512-k2UczelWpBqNZfnG4+czoLDzhxzcvvZXBjvriVTokdXcNknCRG4lBCnSOh/RdvLq62SDaCwmv5c67B3NObfXbg== - dependencies: - "@types/mysql" "*" - -aws-xray-sdk-postgres@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/aws-xray-sdk-postgres/-/aws-xray-sdk-postgres-3.4.1.tgz#5a24e36d6520186ab9b2e6af5724ee463d2117ea" - integrity sha512-3+qfZD8BttjOQ4jzsuRYJzDPOu4dl4nXZPPijAlLUZC978gFIOAWMqJZE6cDmOjXs2BVYdCW8wfiJ1NPYbesBw== - dependencies: - "@types/pg" "*" - -aws-xray-sdk@^3.3.8: - version "3.4.1" - resolved "https://registry.yarnpkg.com/aws-xray-sdk/-/aws-xray-sdk-3.4.1.tgz#e8991e71ce0b7c89d3c3a54c445209eee27a3c71" - integrity sha512-7iz+svMi47EBW3hCGLoMvXYDTG065gg+pdgk/CYXfx0EW9N+uCovKCQodfIwFAMSQ6P4bMxw6jD30h+XtMeTgQ== - dependencies: - aws-xray-sdk-core "3.4.1" - aws-xray-sdk-express "3.4.1" - aws-xray-sdk-mysql "3.4.1" - aws-xray-sdk-postgres "3.4.1" - -aws4@^1.8.0: +aws4@^1.11.0, aws4@^1.8.0: version "1.12.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== +axios-retry@^3.4.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.9.1.tgz#c8924a8781c8e0a2c5244abf773deb7566b3830d" + integrity sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w== + dependencies: + "@babel/runtime" "^7.15.4" + is-retry-allowed "^2.2.0" + axios@^0.19.0: version "0.19.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" @@ -561,15 +836,6 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" -babel-code-frame@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g== - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - babel-runtime@6.6.1: version "6.6.1" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.6.1.tgz#788b94b6f634e25b91bd6c5df72d467457afb000" @@ -601,12 +867,17 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bluebird@^3.5.1: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@1.20.1, body-parser@^1.15.1: +body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== @@ -624,6 +895,24 @@ body-parser@1.20.1, body-parser@^1.15.1: type-is "~1.6.18" unpipe "1.0.0" +body-parser@^1.15.1: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -632,6 +921,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -642,15 +938,6 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== -buffer@4.9.1: - version "4.9.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" - integrity sha512-DNK4ruAqtyHaN8Zne7PkBTO+dD1Lr0YfTduMqlIyjvQIoztBkUxrvL+hKeLW8NXFKHOq/2upkxuoS9znQ9bW9A== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@4.9.2: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" @@ -660,6 +947,13 @@ buffer@4.9.2: ieee754 "^1.1.4" isarray "^1.0.0" +builtins@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" + bunyan@^1.8.12: version "1.8.15" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" @@ -692,25 +986,20 @@ caching-transform@^3.0.2: package-hash "^3.0.0" write-file-atomic "^2.4.2" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g== +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.6.tgz#6c46675fc7a5e9de82d75a233d586c8b7ac0d931" + integrity sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg== dependencies: - callsites "^0.2.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.3" + set-function-length "^1.2.0" -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A== +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^5.0.0: version "5.3.1" @@ -723,32 +1012,33 @@ caseless@~0.12.0: integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== chai-http@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/chai-http/-/chai-http-4.3.0.tgz#3c37c675c1f4fe685185a307e345de7599337c1a" - integrity sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg== + version "4.4.0" + resolved "https://registry.yarnpkg.com/chai-http/-/chai-http-4.4.0.tgz#bb8c346caa25b3c76118c68f7a7cecc0493669b8" + integrity sha512-uswN3rZpawlRaa5NiDUHcDZ3v2dw5QgLyAwnQ2tnVNuP7CwIsOFuYJ0xR1WiR7ymD4roBnJIzOUep7w9jQMFJA== dependencies: "@types/chai" "4" - "@types/superagent" "^3.8.3" - cookiejar "^2.1.1" + "@types/superagent" "4.1.13" + charset "^1.0.1" + cookiejar "^2.1.4" is-ip "^2.0.0" methods "^1.1.2" - qs "^6.5.1" - superagent "^3.7.0" + qs "^6.11.2" + superagent "^8.0.9" chai@^4.2.0: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== @@ -759,7 +1049,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: +chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -768,32 +1058,40 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg== - -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== +charset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/charset/-/charset-1.0.1.tgz#8d59546c355be61049a8fa9164747793319852bd" + integrity sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg== -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== dependencies: - restore-cursor "^2.0.0" + get-func-name "^2.0.2" -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== +chokidar@^3.5.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" cliui@^5.0.0: version "5.0.0" @@ -804,20 +1102,20 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -clone@2.x: +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone@2.x, clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== -cls-hooked@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" - integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== - dependencies: - async-hook-jl "^1.7.6" - emitter-listener "^1.0.1" - semver "^5.4.1" - codependency@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/codependency/-/codependency-0.1.4.tgz#d1763ab7264bd70c91d9626e98862d3792bf8d4a" @@ -832,12 +1130,19 @@ color-convert@^1.9.0, color-convert@^1.9.3: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.0.0: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -866,7 +1171,7 @@ colorspace@1.1.x: color "^3.1.3" text-hex "1.0.x" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -883,10 +1188,15 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -component-emitter@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compare-versions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.0.tgz#3f2131e3ae93577df111dba133e6db876ffe127a" + integrity sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg== + +component-emitter@^1.2.0, component-emitter@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== concat-map@0.0.1: version "0.0.1" @@ -894,17 +1204,12 @@ concat-map@0.0.1: integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== config@^3.0.1: - version "3.3.9" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.9.tgz#27fae95b43e0e1d5723e54143c090954d8e49572" - integrity sha512-G17nfe+cY7kR0wVpc49NCYvNtelm/pPy8czHoFkAgtV1lkmcp7DHtWCdDu+C9Z7gb2WVqa9Tm3uF9aKaPbCfhg== + version "3.3.11" + resolved "https://registry.yarnpkg.com/config/-/config-3.3.11.tgz#009e802631363e456d4cf57767a65c1549069ba2" + integrity sha512-Dhn63ZoWCW5EMg4P0Sl/XNsj/7RLiUIA1x1npCy+m2cRwRHzLnt3UtYtxRDMZW/6oOMdWhCzaGYkOcajGgrAOA== dependencies: json5 "^2.2.3" -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg== - content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -912,10 +1217,10 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== convert-source-map@^1.6.0: version "1.9.0" @@ -932,7 +1237,7 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookiejar@^2.1.0, cookiejar@^2.1.1: +cookiejar@^2.1.0, cookiejar@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== @@ -952,7 +1257,7 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@^2.7.1: +cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -979,22 +1284,27 @@ cross-spawn@^4: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" cssfilter@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== + dependencies: + rrweb-cssom "^0.6.0" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1002,12 +1312,16 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug-log@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" - integrity sha512-gV/pe1YIaKNgLYnd1g9VNW80tcb7oV5qvNUxG7NM8rbDpnl6RGunzlAtlGSb0wEs3nesu2vHNiX9TSsZ+Y+RjA== +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" -debug@2.6.9, debug@^2.2.0, debug@^2.6.8: +debug@2.6.9, debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1021,20 +1335,13 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - debug@=3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -1054,19 +1361,43 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -deep-eql@^4.1.2: +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +deep-eql@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" -deep-equal@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== +deep-equal@^2.2.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" -deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -1078,25 +1409,24 @@ default-require-extensions@^2.0.0: dependencies: strip-bom "^3.0.0" -define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== +define-data-property@^1.0.1, define-data-property@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.2.tgz#f3c33b4f0102360cd7c0f5f28700f5678510b63a" + integrity sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g== dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" -deglob@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/deglob/-/deglob-2.1.1.tgz#d268e168727799862e8eac07042e165957c1f3be" - integrity sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw== +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: - find-root "^1.0.0" - glob "^7.0.5" - ignore "^3.0.9" - pkg-config "^1.1.0" - run-parallel "^1.1.2" - uniq "^1.0.1" + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" delayed-stream@~1.0.0: version "1.0.0" @@ -1113,19 +1443,19 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg== - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -1133,6 +1463,25 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + +dompurify@^3.0.2: + version "3.0.8" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437" + integrity sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ== + dotenv@^8.2.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" @@ -1145,19 +1494,6 @@ dtrace-provider@~0.8: dependencies: nan "^2.14.0" -dynamoose@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/dynamoose/-/dynamoose-1.11.1.tgz#6b9a0356b5a9a9dda8a042ee5e8caf60ba659705" - integrity sha512-73T0GOeSh2FQDdK5V+cN9yowFU4uVt2OsOvknCB4Xdb+nSL/hBaecPY/meJaWZApVx73Hl1DcBSCGp/VWMvfRA== - dependencies: - "@types/node" "11.11.0" - aws-sdk "2.395.0" - debug "4.1.1" - deep-equal "1.0.1" - hooks "0.3.2" - object-path "0.11.4" - q "1.5.1" - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -1178,7 +1514,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -elasticsearch@^16.1.1: +elasticsearch@^16.7.3: version "16.7.3" resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-16.7.3.tgz#bf0e1cc129ab2e0f06911953a1b1f3c740715fab" integrity sha512-e9kUNhwnIlu47fGAr4W6yZJbkpsgQJB0TqNK8rCANe1J4P65B1sGnbCFTgcKY3/dRgCWnuP1AJ4obvzW604xEQ== @@ -1187,18 +1523,16 @@ elasticsearch@^16.1.1: chalk "^1.0.0" lodash "^4.17.10" -emitter-listener@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" - integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== - dependencies: - shimmer "^1.2.0" - emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + enabled@2.0.x: version "2.0.0" resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" @@ -1209,65 +1543,129 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -error-ex@^1.2.0, error-ex@^1.3.1: +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +entities@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== +es-abstract@^1.22.1, es-abstract@^1.22.3: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.5" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" + is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.13" es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== -es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== +es-errors@^1.0.0, es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== dependencies: + call-bind "^1.0.2" get-intrinsic "^1.1.3" - has "^1.0.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.16.tgz#495756d38dd5f9cc8e3091e913ee790d353e6f28" + integrity sha512-CREG2A9Vq7bpDRnldhFcMKuKArvkZtsH6Y0DHOHVg49qhf+LD8uEdUM3OkOAICv0EziGtDEnQtqY2/mfBILpFw== + dependencies: + asynciterator.prototype "^1.0.0" + call-bind "^1.0.6" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + has-property-descriptors "^1.0.1" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.2" + safe-array-concat "^1.1.0" + +es-set-tostringtag@^2.0.1, es-set-tostringtag@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== + dependencies: + get-intrinsic "^1.2.2" has-tostringtag "^1.0.0" + hasown "^2.0.0" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -1283,6 +1681,11 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1293,187 +1696,230 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -eslint-config-standard-jsx@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz#90c9aa16ac2c4f8970c13fc7efc608bacd02da70" - integrity sha512-D+YWAoXw+2GIdbMBRAzWwr1ZtvnSf4n4yL0gKGg7ShUOGXkSOLerI17K4F6LdQMJPNMoWYqepzQD/fKY+tXNSg== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-standard-jsx@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-11.0.0.tgz#70852d395731a96704a592be5b0bfaccfeded239" + integrity sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ== -eslint-config-standard@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" - integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ== +eslint-config-standard@17.1.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" + integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== -eslint-import-resolver-node@^0.3.1: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" -eslint-module-utils@^2.2.0: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" -eslint-plugin-es@^1.3.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" - integrity sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA== - dependencies: - eslint-utils "^1.4.2" - regexpp "^2.0.1" - -eslint-plugin-import@~2.14.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8" - integrity sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g== - dependencies: - contains-path "^0.1.0" - debug "^2.6.8" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.2.0" - has "^1.0.1" - lodash "^4.17.4" - minimatch "^3.0.3" - read-pkg-up "^2.0.0" - resolve "^1.6.0" - -eslint-plugin-node@~7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz#a6e054e50199b2edd85518b89b4e7b323c9f36db" - integrity sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw== +eslint-plugin-es@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9" + integrity sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ== dependencies: - eslint-plugin-es "^1.3.1" - eslint-utils "^1.3.1" - ignore "^4.0.2" - minimatch "^3.0.4" - resolve "^1.8.1" - semver "^5.5.0" + eslint-utils "^2.0.0" + regexpp "^3.0.0" -eslint-plugin-promise@~4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2" - integrity sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg== - -eslint-plugin-react@~7.11.1: - version "7.11.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz#c01a7af6f17519457d6116aa94fc6d2ccad5443c" - integrity sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw== +eslint-plugin-import@^2.27.5: + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== dependencies: - array-includes "^3.0.3" + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" doctrine "^2.1.0" - has "^1.0.3" - jsx-ast-utils "^2.0.1" - prop-types "^15.6.2" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-n@^15.7.0: + version "15.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz#e29221d8f5174f84d18f2eb94765f2eeea033b90" + integrity sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q== + dependencies: + builtins "^5.0.1" + eslint-plugin-es "^4.1.0" + eslint-utils "^3.0.0" + ignore "^5.1.1" + is-core-module "^2.11.0" + minimatch "^3.1.2" + resolve "^1.22.1" + semver "^7.3.8" -eslint-plugin-standard@~4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.2.tgz#021211a9f077e63a6847e7bb9ab4247327ac8e0c" - integrity sha512-nKptN8l7jksXkwFk++PhJB3cCDTcXOEyhISIN86Ue2feJ1LFyY3PrY3/xT2keXlJSY5bpmbiTG0f885/YKAvTA== +eslint-plugin-promise@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== -eslint-scope@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== +eslint-plugin-react@^7.32.2: + version "7.33.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.1" + string.prototype.matchall "^4.0.8" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint-utils@^1.3.1, eslint-utils@^1.4.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== +eslint-utils@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@~5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.4.0.tgz#d068ec03006bb9e06b429dc85f7e46c1b69fac62" - integrity sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg== - dependencies: - ajv "^6.5.0" - babel-code-frame "^6.26.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^3.1.0" - doctrine "^2.1.0" - eslint-scope "^4.0.0" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^4.0.0" - esquery "^1.0.1" +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.41.0: + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.56.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" imurmurhash "^0.1.4" - inquirer "^5.2.0" - is-resolvable "^1.1.0" - js-yaml "^3.11.0" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.5" - minimatch "^3.0.4" - mkdirp "^0.5.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - regexpp "^2.0.0" - require-uncached "^1.0.3" - semver "^5.5.0" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^4.0.3" + optionator "^0.9.3" + strip-ansi "^6.0.1" text-table "^0.2.0" -espree@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f" - integrity sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^6.0.2" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0: +esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -1488,15 +1934,20 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +eventemitter2@^6.4.4: + version "6.4.9" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125" + integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg== + events@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== express-fileupload@^1.1.6: - version "1.4.0" - resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.4.0.tgz#be9d70a881d6c2b1ce668df86e4f89ddbf238ec7" - integrity sha512-RjzLCHxkv3umDeZKeFeMg8w7qe0V09w3B7oGZprr/oO2H/ISCgNzuqzn7gV3HRWb37GjRk429CCpSLS2KNTqMQ== + version "1.4.3" + resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.4.3.tgz#32cbb405ba12f541d445838464e081defba4ef61" + integrity sha512-vRzZo2YELm68DfR/CX8RMXgeK9BTAANxigrKACPjCXFGEzkCt/QWbqaIXP3W61uaX/hLj0CAo3/EVelpSQXkqA== dependencies: busboy "^1.6.0" @@ -1554,15 +2005,6 @@ extend@^3.0.0, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -1573,7 +2015,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -1583,30 +2025,41 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-safe-stringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + fecha@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: - escape-string-regexp "^1.0.5" + flat-cache "^3.0.4" -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - integrity sha512-uXP/zGzxxFvFfcZGgBIwotm+Tdc55ddPAzF7iHshP4YGaXMww7rSF9peD9D1sui5ebONg5UobsZv+FfgEpGv/w== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" + to-regex-range "^5.0.1" finalhandler@1.2.0: version "1.2.0" @@ -1630,11 +2083,6 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-root@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" - integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== - find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -1642,22 +2090,22 @@ find-up@3.0.0, find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: - locate-path "^2.0.0" + locate-path "^6.0.0" + path-exists "^4.0.0" -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" flat@^4.1.0: version "4.1.1" @@ -1666,6 +2114,11 @@ flat@^4.1.0: dependencies: is-buffer "~2.0.3" +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" @@ -1679,9 +2132,9 @@ follow-redirects@1.5.10: debug "=3.1.0" follow-redirects@^1.14.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== for-each@^0.3.3: version "0.3.3" @@ -1712,6 +2165,15 @@ form-data@^2.3.1: combined-stream "^1.0.6" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -1726,6 +2188,16 @@ formidable@^1.2.0: resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168" integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ== +formidable@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" + integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + qs "^6.11.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -1741,74 +2213,91 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" -functions-have-names@^1.2.2: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== +get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-parameter-names@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/get-parameter-names/-/get-parameter-names-0.3.0.tgz#2d2237cd592e6c5b859ab2efdab435f008e5bb97" integrity sha512-KkR1dX7U1TynXFkqveVE/XoRn9qRAsM2q4Eu2WsGTFzoaSdnNQEfxbcK+LMv8DcFoQQT9BFjNL+bf9ZyTLkWpg== -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: - assert-plus "^1.0.0" + assert-plus "^1.0.0" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" glob@7.1.3: version "7.1.3" @@ -1833,7 +2322,7 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.5, glob@^7.1.2, glob@^7.1.3: +glob@^7.0.5, glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1845,11 +2334,18 @@ glob@^7.0.5, glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.1.0, globals@^11.7.0: +globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" @@ -1865,9 +2361,14 @@ gopd@^1.0.1: get-intrinsic "^1.1.3" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== growl@1.10.5: version "1.10.5" @@ -1904,12 +2405,17 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" @@ -1921,19 +2427,17 @@ has-symbols@^1.0.0, has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +has-tostringtag@^1.0.0, has-tostringtag@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - has-symbols "^1.0.2" + has-symbols "^1.0.3" -has@^1.0.1, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" +hash-it@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hash-it/-/hash-it-6.0.0.tgz#188df5a8ca2f8e036690e35f2ef88bd9417ff334" + integrity sha512-KHzmSFx1KwyMPw0kXeeUD752q/Kfbzhy6dAZrjXV9kAIXGqzGvv8vhkUqj+2MGZldTo0IBpw6v7iWE7uxsvH0w== hasha@^3.0.0: version "3.0.0" @@ -1942,11 +2446,23 @@ hasha@^3.0.0: dependencies: is-stream "^1.0.1" +hasown@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" + integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== + dependencies: + function-bind "^1.1.2" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hexoid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" + integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== + hoek@5.x.x: version "5.0.4" resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.4.tgz#0f7fa270a1cafeb364a4b2ddfaa33f864e4157da" @@ -1957,16 +2473,23 @@ hoek@6.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c" integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ== -hooks@0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/hooks/-/hooks-0.3.2.tgz#a31f060c2026cea6cf1ca3eb178430e718e1c4a3" - integrity sha512-TqeFzUf12rSzcbm5lUls81jimUC8TmXZ4ANPxxeeMou09hrjBcHYhAQ0WgyN5YqNCXOzz7L6xVNl/+ctFuSeOw== - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hpagent@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== + +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -1997,6 +2520,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2011,7 +2543,7 @@ http-status-codes@^1.3.0: resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477" integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ== -https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -2026,37 +2558,47 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17: +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@1.1.13: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ieee754@1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" - integrity sha512-/aoyv2Nt7mGLnCAWzE0C1WH9Xd8ZsqR0f4Pjwxputi1JNm01+InyAYQotF4N+ulEIjbEsJo22NOHr+U/XEZ1Pw== - ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^3.0.9: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== -ignore@^4.0.2: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.1, ignore@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" imurmurhash@^0.1.4: version "0.1.4" @@ -2076,32 +2618,13 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" - integrity sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ== - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.1.0" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^5.5.2" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - -internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== +internal-slot@^1.0.4, internal-slot@^1.0.5, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" + es-errors "^1.3.0" + hasown "^2.0.0" side-channel "^1.0.4" ip-regex@^2.0.0: @@ -2114,7 +2637,7 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-arguments@^1.0.4: +is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== @@ -2122,14 +2645,13 @@ is-arguments@^1.0.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-typed-array "^1.1.10" + get-intrinsic "^1.2.1" is-arrayish@^0.2.1: version "0.2.1" @@ -2141,6 +2663,13 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -2148,6 +2677,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" @@ -2166,32 +2702,56 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== -is-generator-function@^1.0.7: +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.10, is-generator-function@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== dependencies: has-tostringtag "^1.0.0" +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab" @@ -2199,6 +2759,11 @@ is-ip@^2.0.0: dependencies: ip-regex "^2.0.0" +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -2211,6 +2776,21 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -2219,10 +2799,15 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-resolvable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== +is-retry-allowed@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" + integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== + +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== is-shared-array-buffer@^1.0.2: version "1.0.2" @@ -2255,22 +2840,23 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.13, is-typed-array@^1.1.3, is-typed-array@^1.1.9: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.14" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -2278,11 +2864,24 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isemail@3.x.x: version "3.2.0" resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.2.0.tgz#59310a021931a9fb06bbb51e155ce0b3f236832c" @@ -2352,10 +2951,16 @@ istanbul-reports@^2.2.4: dependencies: html-escaper "^2.0.0" -jmespath@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" - integrity sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w== +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" jmespath@0.16.0: version "0.16.0" @@ -2385,11 +2990,6 @@ joi@^14.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg== - js-yaml@3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" @@ -2398,7 +2998,7 @@ js-yaml@3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.11.0, js-yaml@^3.13.1: +js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -2406,21 +3006,76 @@ js-yaml@^3.11.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +jsdom@^21.1.2: + version "21.1.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.2.tgz#6433f751b8718248d646af1cdf6662dc8a1ca7f9" + integrity sha512-sCpFmK2jv+1sjff4u7fzft+pUh2KSUbUrEHYHyfSIbGTIcmnjyp83qg6qLwdJ/I3LpTXx33ACxeRL7Lsyc6lGQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.2" + acorn-globals "^7.0.0" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-rules-engine@^6.1.2: + version "6.5.0" + resolved "https://registry.yarnpkg.com/json-rules-engine/-/json-rules-engine-6.5.0.tgz#dead167b2171b2cd9c4925285fe3971f9aa6c887" + integrity sha512-W8SLmnfQRDNG1Nh3Agz3c9AZzhiZ/cUtjAhyfhujFzVFNBv7cSHm9WaLoRjOdRr/9je7RgLtmbYXFViL3CekPA== + dependencies: + clone "^2.1.2" + eventemitter2 "^6.4.4" + hash-it "^6.0.0" + jsonpath-plus "^7.2.0" + lodash.isobjectlike "^4.0.0" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -2441,11 +3096,23 @@ json-stringify-safe@5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonpath-plus@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz#7ad94e147b3ed42f7939c315d2b9ce490c5a3899" + integrity sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA== + jsonwebtoken@^8.3.0, jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" @@ -2472,13 +3139,15 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" -jsx-ast-utils@^2.0.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" - integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w== +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== dependencies: - array-includes "^3.1.1" - object.assign "^4.1.0" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" jwa@^1.4.1: version "1.4.1" @@ -2513,33 +3182,37 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kuler@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + prelude-ls "^1.2.1" + type-check "~0.4.0" limiter@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ== +linkify-it@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" + integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" + uc.micro "^1.0.1" load-json-file@^4.0.0: version "4.0.0" @@ -2551,13 +3224,16 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== +load-json-file@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" + integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" + graceful-fs "^4.1.15" + parse-json "^4.0.0" + pify "^4.0.1" + strip-bom "^3.0.0" + type-fest "^0.3.0" locate-path@^3.0.0: version "3.0.0" @@ -2567,6 +3243,18 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -2597,6 +3285,11 @@ lodash.isnumber@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== +lodash.isobjectlike@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lodash.isobjectlike/-/lodash.isobjectlike-4.0.0.tgz#742c5fc65add27924d3d24191681aa9a17b2b60d" + integrity sha512-bbRt0Dief0yqjkTgpvzisSxnsmY3ZgVJvokHL30UE+ytsvnpNfiNaCJL4XBEWek8koQmrwZidBHb7coXC5vXlA== + lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -2607,6 +3300,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -2617,7 +3315,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: +lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -2630,16 +3328,22 @@ log-symbols@2.2.0: chalk "^2.0.1" logform@^2.3.2, logform@^2.4.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c" - integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw== + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== dependencies: - "@colors/colors" "1.5.0" + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" fecha "^4.2.0" ms "^2.1.1" safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" +long@^5.0.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -2647,12 +3351,12 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -loupe@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== dependencies: - get-func-name "^2.0.0" + get-func-name "^2.0.1" lru-cache@^4.0.1: version "4.1.5" @@ -2662,6 +3366,13 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-cache@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" @@ -2671,9 +3382,9 @@ lru-cache@~4.0.0: yallist "^2.0.0" lru-memoizer@^2.1.2: - version "2.1.4" - resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.1.4.tgz#b864d92b557f00b1eeb322156a0409cb06dafac6" - integrity sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.2.0.tgz#b9d90c91637b4b1a423ef76f3156566691293df8" + integrity sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw== dependencies: lodash.clonedeep "^4.5.0" lru-cache "~4.0.0" @@ -2686,6 +3397,22 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +markdown-it@^13.0.1: + version "13.0.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -2730,12 +3457,12 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mime@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== -"minimatch@2 || 3", minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.1.1: +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2749,10 +3476,10 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== mkdirp@0.5.4: version "0.5.4" @@ -2761,7 +3488,7 @@ mkdirp@0.5.4: dependencies: minimist "^1.2.5" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@^0.5.0, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -2803,9 +3530,9 @@ mocha@^6.1.4: yargs-unparser "1.6.0" moment@^2.19.3, moment@^2.24.0: - version "2.29.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== ms@2.0.0: version "2.0.0" @@ -2822,16 +3549,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== - mv@~2: version "2.1.1" resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" @@ -2842,9 +3564,9 @@ mv@~2: rimraf "~2.4.0" nan@^2.14.0: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + version "2.18.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" + integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== natural-compare@^1.4.0: version "1.4.0" @@ -2866,11 +3588,6 @@ nested-error-stacks@^2.0.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" @@ -2886,6 +3603,29 @@ node-environment-flags@1.0.5: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" +nodemon@^2.0.20: + version "2.0.22" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258" + integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^5.7.1" + simple-update-notifier "^1.0.7" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== + dependencies: + abbrev "1" + normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -2896,6 +3636,16 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +nwsapi@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== + nyc@^14.0.0: version "14.1.1" resolved "https://registry.yarnpkg.com/nyc/-/nyc-14.1.1.tgz#151d64a6a9f9f5908a1b73233931e4a0a3075eeb" @@ -2932,26 +3682,29 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" object-keys@^1.0.11, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-path@0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" - integrity sha512-ICbQN+aw/eAASDtaC7+SJXSAruz7fvvNjxMFfS3mTdvZaaiuuw81XXYu+9CSJeUVrS3YpRhTr862YGywMQUOWg== - object.assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" @@ -2962,25 +3715,72 @@ object.assign@4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.assign@^4.1.0, object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" + call-bind "^1.0.5" + define-properties "^1.2.1" has-symbols "^1.0.3" object-keys "^1.1.1" +object.entries@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131" + integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.fromentries@^2.0.6, object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + object.getownpropertydescriptors@^2.0.3: - version "2.1.5" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" - integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== + version "2.1.7" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz#7a466a356cd7da4ba8b9e94ff6d35c3eeab5d56a" + integrity sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g== + dependencies: + array.prototype.reduce "^1.0.6" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + safe-array-concat "^1.0.0" + +object.groupby@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.2.tgz#494800ff5bab78fd0eff2835ec859066e00192ec" + integrity sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw== + dependencies: + array.prototype.filter "^1.0.3" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.0.0" + +object.hasown@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae" + integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA== + dependencies: + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.values@^1.1.6, object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== dependencies: - array.prototype.reduce "^1.0.5" call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" on-finished@2.4.1: version "2.4.1" @@ -2989,7 +3789,7 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -once@^1.3.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -3003,42 +3803,23 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== - dependencies: - mimic-fn "^1.0.0" - -optionator@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3046,12 +3827,12 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: - p-limit "^1.1.0" + yocto-queue "^0.1.0" p-locate@^3.0.0: version "3.0.0" @@ -3060,10 +3841,12 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" p-try@^2.0.0: version "2.2.0" @@ -3080,12 +3863,12 @@ package-hash@^3.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: - error-ex "^1.2.0" + callsites "^3.0.0" parse-json@^4.0.0: version "4.0.0" @@ -3095,6 +3878,13 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -3105,20 +3895,20 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== - -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" @@ -3130,13 +3920,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ== - dependencies: - pify "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -3154,31 +3937,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -pg-int8@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" - integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== - -pg-protocol@*: - version "1.5.0" - resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0" - integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ== - -pg-types@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" - integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== - dependencies: - pg-int8 "1.0.1" - postgres-array "~2.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.4" - postgres-interval "^1.1.0" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^3.0.0: version "3.0.0" @@ -3190,22 +3952,13 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pkg-conf@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-2.1.0.tgz#2126514ca6f2abfebd168596df18ba57867f0058" - integrity sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g== - dependencies: - find-up "^2.0.0" - load-json-file "^4.0.0" - -pkg-config@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pkg-config/-/pkg-config-1.1.1.tgz#557ef22d73da3c8837107766c52eadabde298fe4" - integrity sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw== +pkg-conf@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-3.1.0.tgz#d9f9c75ea1bae0e77938cde045b276dac7cc69ae" + integrity sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ== dependencies: - debug-log "^1.0.0" - find-root "^1.0.0" - xtend "^4.0.1" + find-up "^3.0.0" + load-json-file "^5.2.0" pkg-dir@^3.0.0: version "3.0.0" @@ -3214,54 +3967,27 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" - integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== - -postgres-array@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" - integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== - -postgres-bytea@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" - integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== - -postgres-date@~1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" - integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== - -postgres-interval@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" - integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== - dependencies: - xtend "^4.0.0" - precond@0.2: version "0.2.3" resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" integrity sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^2.8.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -prop-types@^15.6.2: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -3270,6 +3996,24 @@ prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.13.1" +protobufjs@^7.2.4: + version "7.2.6" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" + integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== + 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/node" ">=13.7.0" + long "^5.0.0" + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -3288,33 +4032,40 @@ pseudomap@^1.0.1, pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -q@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== +punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -qs@6.11.0, qs@^6.5.1: +qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" +qs@^6.11.0, qs@^6.11.2, qs@^6.5.1: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -3325,6 +4076,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -3357,19 +4113,21 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w== - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - read-pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" @@ -3378,15 +4136,6 @@ read-pkg-up@^4.0.0: find-up "^3.0.0" read-pkg "^3.0.0" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA== - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -3397,9 +4146,9 @@ read-pkg@^3.0.0: path-type "^3.0.0" readable-stream@^2.3.5: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -3410,14 +4159,21 @@ readable-stream@^2.3.5: util-deprecate "~1.0.1" readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + reconnect-core@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/reconnect-core/-/reconnect-core-1.3.0.tgz#fbae52919a7877d844e3246d01a2f26701c833c8" @@ -3425,19 +4181,38 @@ reconnect-core@1.3.0: dependencies: backoff "~2.5.0" -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== +reflect.getprototypeof@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz#e0bd28b597518f16edaf9c0e292c631eb13e0674" + integrity sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.0.0" + get-intrinsic "^1.2.3" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" -regexpp@^2.0.0, regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpp@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== release-zalgo@^1.0.0: version "1.0.0" @@ -3482,40 +4257,38 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w== - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.22.1, resolve@^1.6.0, resolve@^1.8.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.10.0, resolve@^1.22.1, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== +resolve@^2.0.0-next.4: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" @@ -3524,6 +4297,13 @@ rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@~2.4.0: version "2.4.5" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" @@ -3531,31 +4311,27 @@ rimraf@~2.4.0: dependencies: glob "^6.0.1" -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -run-async@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== -run-parallel@^1.1.2: +run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" -rxjs@^5.5.2: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== +safe-array-concat@^1.0.0, safe-array-concat@^1.0.1, safe-array-concat@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" + integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== dependencies: - symbol-observable "1.0.1" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" + has-symbols "^1.0.3" + isarray "^2.0.5" safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" @@ -3573,20 +4349,20 @@ safe-json-stringify@~1: integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" + call-bind "^1.0.6" + es-errors "^1.3.0" is-regex "^1.1.4" safe-stable-stringify@^2.3.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz#ec7b037768098bf65310d1d64370de0dc02353aa" - integrity sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA== + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -3597,14 +4373,26 @@ sax@1.2.1: integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + +"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@5.0.1: version "5.0.1" @@ -3616,10 +4404,22 @@ semver@5.1.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.0.tgz#85f2cf8550465c4df000cf7d86f6b054106ab9e5" integrity sha512-sfKXKhcz5XVyfUZa2V4RbjK0xjOJCMLNF9H4p4v0UCo9wNHM/lH9RDuyDbGEtxWLMDlPBc8xI7AbbVLKXty+rQ== -semver@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.0.0, semver@^7.3.8: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +semver@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== send@0.18.0: version "0.18.0" @@ -3655,36 +4455,53 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" + integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== + dependencies: + define-data-property "^1.1.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +set-function-name@^2.0.0, set-function-name@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + shebang-regex "^3.0.0" -shimmer@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" - integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" + integrity sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.6" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.7" @@ -3698,14 +4515,14 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" - integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== +simple-update-notifier@^1.0.7: + version "1.1.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" + integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== dependencies: - is-fullwidth-code-point "^2.0.0" + semver "~7.0.0" -source-map@^0.6.1: +source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -3723,17 +4540,17 @@ spawn-wrap@^1.4.2: which "^1.3.0" spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + version "2.4.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" + integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -3744,9 +4561,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.17" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c" + integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== sprintf-js@~1.0.2: version "1.0.3" @@ -3754,9 +4571,9 @@ sprintf-js@~1.0.2: integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -3768,52 +4585,54 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stack-chain@^1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" - integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== - stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== -standard-engine@~9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-9.0.0.tgz#d3a3d74c4c1b91f51a1e66362465261ca7610316" - integrity sha512-ZfNfCWZ2Xq67VNvKMPiVMKHnMdvxYzvZkf1AH8/cw2NLDBm5LRsxMqvEJpsjLI/dUosZ3Z1d6JlHDp5rAvvk2w== +standard-engine@^15.0.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-15.1.0.tgz#717409a002edd13cd57f6554fdd3464d9a22a774" + integrity sha512-VHysfoyxFu/ukT+9v49d4BRXIokFRZuH3z1VRxzFArZdjSCFpro6rEIU3ji7e4AoAtuSfKBkiOmsrDqKW5ZSRw== dependencies: - deglob "^2.1.0" - get-stdin "^6.0.0" - minimist "^1.1.0" - pkg-conf "^2.0.0" - -standard@^12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/standard/-/standard-12.0.1.tgz#0fc5a8aa6c34c546c5562aae644242b24dae2e61" - integrity sha512-UqdHjh87OG2gUrNCSM4QRLF5n9h3TFPwrCNyVlkqu31Hej0L/rc8hzKqVvkb2W3x0WMq7PzZdkLfEcBhVOR6lg== - dependencies: - eslint "~5.4.0" - eslint-config-standard "12.0.0" - eslint-config-standard-jsx "6.0.2" - eslint-plugin-import "~2.14.0" - eslint-plugin-node "~7.0.1" - eslint-plugin-promise "~4.0.0" - eslint-plugin-react "~7.11.1" - eslint-plugin-standard "~4.0.0" - standard-engine "~9.0.0" + get-stdin "^8.0.0" + minimist "^1.2.6" + pkg-conf "^3.1.0" + xdg-basedir "^4.0.0" + +standard@^17.1.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/standard/-/standard-17.1.0.tgz#829eeeb3139ad50714294d3531592d60ad1286af" + integrity sha512-jaDqlNSzLtWYW4lvQmU0EnxWMUGQiwHasZl5ZEIwx3S/ijZDjZOzs1y1QqKwKs5vqnFpGtizo4NOYX2s0Voq/g== + dependencies: + eslint "^8.41.0" + eslint-config-standard "17.1.0" + eslint-config-standard-jsx "^11.0.0" + eslint-plugin-import "^2.27.5" + eslint-plugin-n "^15.7.0" + eslint-plugin-promise "^6.1.1" + eslint-plugin-react "^7.32.2" + standard-engine "^15.0.0" + version-guard "^1.1.1" statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -3830,23 +4649,56 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.matchall@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" string_decoder@^1.1.1: version "1.3.0" @@ -3883,17 +4735,29 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-json-comments@2.0.1, strip-json-comments@^2.0.1: +strip-json-comments@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -superagent@^3.7.0, superagent@^3.8.3: +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +superagent@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA== @@ -3909,6 +4773,22 @@ superagent@^3.7.0, superagent@^3.8.3: qs "^6.5.1" readable-stream "^2.3.5" +superagent@^8.0.9: + version "8.1.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" + integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.4" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^2.1.2" + methods "^1.1.2" + mime "2.6.0" + qs "^6.11.0" + semver "^7.3.8" + supports-color@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" @@ -3921,7 +4801,7 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== -supports-color@^5.3.0: +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -3935,39 +4815,34 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== swagger-ui-dist@>=4.11.0: - version "4.15.5" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-4.15.5.tgz#cda226a79db2a9192579cc1f37ec839398a62638" - integrity sha512-V3eIa28lwB6gg7/wfNvAbjwJYmDXy1Jo1POjyTzlB6wPcHiGlRxq39TSjYGVjQrUSAzpv+a7nzp7mDxgNy57xA== + version "5.11.3" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.11.3.tgz#64c96e90b6a352e7b20a55b73b91fc0e0bed4f0a" + integrity sha512-vQ+Pe73xt7vMVbX40L6nHu4sDmNCM6A+eMVJPGvKrifHQ4LO3smH0jCiiefKzsVl7OlOcVEnrZ9IFzYwElfMkA== swagger-ui-express@^4.1.3: - version "4.6.0" - resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-4.6.0.tgz#fc297d80c614c80f5d7def3dab50b56428cfe1c9" - integrity sha512-ZxpQFp1JR2RF8Ar++CyJzEDdvufa08ujNUJgMVTMWPi86CuQeVdBtvaeO/ysrz6dJAYXf9kbVNhWD7JWocwqsA== + version "4.6.3" + resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-4.6.3.tgz#870d0892654fe80e6970a2d680e22521acd2dc19" + integrity sha512-CDje4PndhTD2HkgyKH3pab+LKspDeB/NhPN2OF1j+piYIamQqBYwAXWESOT1Yju2xFg51bRW9sUng2WxDjzArw== dependencies: swagger-ui-dist ">=4.11.0" -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw== - -table@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" - integrity sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg== - dependencies: - ajv "^6.0.1" - ajv-keywords "^3.0.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tc-core-library-js@appirio-tech/tc-core-library-js.git#v2.6.4: version "2.4.1" @@ -4002,23 +4877,18 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -4033,6 +4903,11 @@ topcoder-bus-api-wrapper@topcoder-platform/tc-bus-api-wrapper.git: superagent "^3.8.3" tc-core-library-js appirio-tech/tc-core-library-js.git#v2.6.4 +topcoder-proto-registry@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/topcoder-proto-registry/-/topcoder-proto-registry-0.2.0.tgz#703d636d2581b7b3903fe299f6c3d572c5f728c0" + integrity sha512-qmoAY0jb25A4S4bunUagj+wP++d1Db0iZqMc0SaMFjzW33dXjay7TpJDBbNZuVk4He7kUhYXrn2CDikbPM3TFw== + topo@3.x.x: version "3.0.3" resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.3.tgz#d5a67fb2e69307ebeeb08402ec2a2a6f5f7ad95c" @@ -4040,6 +4915,23 @@ topo@3.x.x: dependencies: hoek "6.x.x" +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + +tough-cookie@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -4048,15 +4940,32 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== + dependencies: + punycode "^2.3.0" + triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== -tslib@^2.3.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.4.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tunnel-agent@^0.6.0: version "0.6.0" @@ -4070,18 +4979,28 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: - prelude-ls "~1.1.2" + prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -4090,6 +5009,36 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz#0608ffe6bca71bf15a45bff0ca2604107a1325f5" + integrity sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -4099,6 +5048,11 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -4109,10 +5063,20 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" @@ -4126,6 +5090,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" @@ -4155,11 +5127,6 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - uuid@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" @@ -4192,6 +5159,43 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +version-guard@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/version-guard/-/version-guard-1.1.1.tgz#7a6e87a1babff1b43d6a7b0fd239731e278262fa" + integrity sha512-MGQLX89UxmYHgDvcXyjBI0cbmoW+t/dANDppNPrno64rYr8nH4SHSuElQuSYdXGEs0mUzdQe1BY+FhVPNsAmJQ== + +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -4203,22 +5207,49 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.2, which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.2, which-typed-array@^1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06" + integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + available-typed-arrays "^1.0.6" + call-bind "^1.0.5" for-each "^0.3.3" gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" + has-tostringtag "^1.0.1" which@1.3.1, which@^1.2.9, which@^1.3.0: version "1.3.1" @@ -4227,6 +5258,13 @@ which@1.3.1, which@^1.2.9, which@^1.3.0: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -4235,20 +5273,20 @@ wide-align@1.1.3: string-width "^1.0.2 || 2" winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== dependencies: logform "^2.3.2" readable-stream "^3.6.0" triple-beam "^1.3.0" winston@^3.8.2: - version "3.8.2" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" - integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew== + version "3.11.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.11.0.tgz#2d50b0a695a2758bb1c95279f0a88e858163ed91" + integrity sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g== dependencies: - "@colors/colors" "1.5.0" + "@colors/colors" "^1.6.0" "@dabh/diagnostics" "^2.0.2" async "^3.2.3" is-stream "^2.0.0" @@ -4260,11 +5298,6 @@ winston@^3.8.2: triple-beam "^1.3.0" winston-transport "^4.5.0" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -4274,6 +5307,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -4288,25 +5330,38 @@ write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA== - dependencies: - mkdirp "^0.5.1" +ws@^8.13.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xml2js@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== +xml2js@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== dependencies: sax ">=0.6.0" - xmlbuilder "~9.0.1" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlbuilder@~9.0.1: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xss@^1.0.8: version "1.0.14" @@ -4316,21 +5371,26 @@ xss@^1.0.8: commander "^2.20.3" cssfilter "0.0.10" -xtend@^4.0.0, xtend@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^2.0.0, yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yamljs@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" @@ -4347,6 +5407,11 @@ yargs-parser@13.1.2, yargs-parser@^13.0.0, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-unparser@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" @@ -4371,3 +5436,21 @@ yargs@13.3.2, yargs@^13.2.2, yargs@^13.3.0: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.2" + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==