diff --git a/.env b/.env new file mode 100644 index 0000000..c7b14f7 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +AUTH0_DOMAIN=lesleychard.auth0.com +AUTH0_CLIENT_ID=bnyMiWB15Rz5CwsxTNnrN9v5ftHcdabj +AUTH0_MGMT_CLIENT_ID=mxNP8B5JnQ4v049Vt2mR80z9rvov5yfj +AUTH0_CLAIMS_NAMESPACE=https://lesleychard diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..68b4a48 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,52 @@ +name: Deploy to Google Cloud Run +on: + release: + types: [published] +jobs: + build: + environment: "GC Run" + name: Build image + runs-on: ubuntu-latest + env: + HASH: $(git rev-parse --short "$GITHUB_SHA") + BRANCH: ${GITHUB_REF##*/} + SERVICE_NAME: ${{ secrets.SERVICE_NAME }} + PROJECT_ID: ${{ secrets.PROJECT_ID }} + AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }} + AUTH0_MGMT_CLIENT_SECRET: ${{ secrets.AUTH0_MGMT_CLIENT_SECRET }} + AUTH0_DOMAIN: ${{ secrets.AUTH0_DOMAIN }} + AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }} + AUTH0_MGMT_CLIENT_ID: ${{ secrets.AUTH0_MGMT_CLIENT_ID }} + AUTH0_CLAIMS_NAMESPACE: ${{ secrets.AUTH0_CLAIMS_NAMESPACE }} + steps: + - name: Checkout + uses: actions/checkout@v2 + # Setup gcloud CLI + - uses: google-github-actions/github-actions/setup-gcloud@master + with: + service_account_key: ${{ secrets.GCR_DEVOPS_SERVICE_ACCOUNT_KEY }} + project_id: ${{ secrets.PROJECT_ID }} + export_default_credentials: true + - run: | + ls -la + # Build docker image + - name: Build Docker Image + run: |- + docker build -t northamerica-northeast1-docker.pkg.dev/ranlab-mvp-295423/ranlab-api-mvp/ranlab-api-mvp:latest . + # Configure docker to use the gcloud command-line tool as a credential helper + - run: | + gcloud auth configure-docker -q northamerica-northeast1-docker.pkg.dev + # Push image to Google Container Registry + - name: Push Image to GCR + run: |- + docker push northamerica-northeast1-docker.pkg.dev/ranlab-mvp-295423/ranlab-api-mvp/ranlab-api-mvp:latest + - name: Deploy Container + run: |- + gcloud run \ + deploy ranlab-api-mvp \ + --quiet \ + --platform=managed \ + --region=northamerica-northeast1 \ + --allow-unauthenticated \ + --image northamerica-northeast1-docker.pkg.dev/ranlab-mvp-295423/ranlab-api-mvp/ranlab-api-mvp:latest \ + --set-env-vars AUTH0_MGMT_CLIENT_SECRET=$AUTH0_MGMT_CLIENT_SECRET,AUTH0_CLIENT_SECRET=$AUTH0_CLIENT_SECRET,AUTH0_DOMAIN=$AUTH0_DOMAIN,AUTH0_CLIENT_ID=$AUTH0_CLIENT_ID,AUTH0_MGMT_CLIENT_ID=$AUTH0_MGMT_CLIENT_ID,AUTH0_CLAIMS_NAMESPACE=$AUTH0_CLAIMS_NAMESPACE diff --git a/.gitignore b/.gitignore index 4601041..49a9b0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ yarn-error.log +.idea/ node_modules/ *.tsbuildinfo diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5853b65 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:15 + +# Create and change to the app directory. +WORKDIR /usr/src/app + +# Copy application dependency manifests to the container image. +# A wildcard is used to ensure both package.json AND package-lock.json are copied. +# Copying this separately prevents re-running npm install on every code change. +COPY package*.json ./ + +# Install production dependencies. +RUN yarn install --only=production + +# Copy local code to the container image. +COPY . . + +RUN yarn build + +# Run the web service on container startup. +CMD [ "yarn", "start" ] diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..db20d00 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + + '@babel/preset-typescript', + ], +}; diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..9f87d4a --- /dev/null +++ b/deploy.sh @@ -0,0 +1,10 @@ +# Prerequisite Setup Steps: +# gcloud artifacts repositories create ranlab-api-mvp --repository-format docker --location northamerica-northeast1 # create the artifact repo +# gcloud auth configure-docker northamerica-northeast1-docker.pkg.dev + +docker build -t ranlab-mvp-api:latest . +docker tag ranlab-mvp-api:latest northamerica-northeast1-docker.pkg.dev/ranlab-mvp-295423/ranlab-api-mvp/ranlab-api-mvp:latest +gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://gcr.io +docker push northamerica-northeast1-docker.pkg.dev/ranlab-mvp-295423/ranlab-api-mvp/ranlab-api-mvp:latest +gcloud run --platform=managed --region=northamerica-northeast1 deploy ranlab-api-mvp --image northamerica-northeast1-docker.pkg.dev/ranlab-mvp-295423/ranlab-api-mvp/ranlab-api-mvp:latest + diff --git a/dev/test b/dev/test new file mode 100755 index 0000000..2c030f3 --- /dev/null +++ b/dev/test @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -u +set -o pipefail + +readonly all_files=$( find src -type f -name '*.test.js' ) + +for f in ${1:-$all_files} +do + output=$(node "$f") + status="$?" + if [[ "$status" -eq 0 ]] + then + printf '%s' "${output}" + else + printf '%s\n' "${output}" + exit "$status" + fi +done diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..cabb177 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testPathIgnorePatterns: ["/node_modules/","/build/"] +}; diff --git a/jsdoc.json b/jsdoc.json new file mode 100644 index 0000000..fcb2bb1 --- /dev/null +++ b/jsdoc.json @@ -0,0 +1,15 @@ +{ + "opts": { + "template": "node_modules/better-docs" + }, + "tags": { + "allowUnknownTags": ["optional", "category"] + }, + "plugins": [ + "node_modules/better-docs/typescript", + "node_modules/better-docs/category" + ], + "source": { + "includePattern": ".+\\.(jsx|js|ts|tsx)$" + } +} diff --git a/package.json b/package.json index 4a92f67..2dd2783 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "@ranlab/api", - "version": "0.0.0", + "version": "0.1.0", "license": "UNLICENSED", "private": true, "description": "API for the RAnLab app", - "main": "src/index.js", + "main": "build/src/index.js", "scripts": { "build": "tsc --project .", - "test": ":", + "test": "dev/test", + "jest": "jest", "start": "node ." }, "repository": { @@ -22,10 +23,28 @@ "node": ">=12" }, "dependencies": { - "fastify": "^3.5.1" + "@auth0/auth0-spa-js": "^1.13.6", + "@google-cloud/firestore": "^4.9.9", + "@types/node-fetch": "^2.5.8", + "body-parser": "^1.19.0", + "fastify": "^3.5.1", + "fastify-auth0-verify": "^0.4.2", + "fastify-authz-jwks": "^1.1.11", + "fastify-cors": "^5.1.0", + "fastify-jwt": "^2.1.3", + "fastify-sensible": "^3.1.0", + "fastify-swagger": "^4.0.1", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^1.12.0", + "jwt-decode": "^3.1.2", + "node-fetch": "^2.6.1" }, "devDependencies": { + "@types/jest": "^26.0.15", "@types/node": "^12.12.67", + "baretest": "^2.0.0", + "jest": "^26.6.3", + "ts-jest": "^26.4.4", "typescript": "^4.0.3" } } diff --git a/src/auth0.ts b/src/auth0.ts new file mode 100644 index 0000000..2c1c9ab --- /dev/null +++ b/src/auth0.ts @@ -0,0 +1,165 @@ +import { FastifyRequest} from "fastify"; +import fetch from "node-fetch"; + +let moduleAdminToken: string; + +async function getAdminToken(refresh : boolean) { + if(refresh || !moduleAdminToken) { + let mgmtDomain = process.env.AUTH0_DOMAIN; + + let refreshResponse = await fetch(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, { + "method": "post", + "headers": { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + "grant_type": "client_credentials", + "client_id": process.env.AUTH0_MGMT_CLIENT_ID, + "client_secret": process.env.AUTH0_MGMT_CLIENT_SECRET, + "audience": `https://${mgmtDomain}/api/v2/`, + "scope": "read:users update:users read:users_app_metadata update:users_app_metadata" + }) + }); + let refreshJson = await refreshResponse.json(); + moduleAdminToken = refreshJson.access_token + } + return moduleAdminToken; +} + +export interface Auth0UserInfo { + user_id: string, // Should be URL encoded since it may contain characters that do not work well in a URL. + username?: string, + email?: string + email_verified?: boolean, + phone_number?: string + phone_verified?: boolean, + created_at?: string, + updated_at?: string, + identities?: [ + { + connection?: string, + user_id: string, + provider: string, + isSocial?: boolean + } + ], + app_metadata?: any, + user_metadata?: any, + picture?: string, + name?: string, + nickname?: string, + multifactor?: string[], + last_ip?: string, + last_login?: string, + logins_count?: number, + blocked?: boolean, + given_name?: string, + family_name?: string +} + +export interface UserInfoPatch { + blocked?: boolean, + email_verified?: boolean, + email?: string, + phone_number?: string, + phone_verified?: boolean, + user_metadata?: any, + app_metadata?: any, + given_name?: string, + family_name?: string, + name?: string, + nickname?: string, + picture?: string, + verify_email?: boolean, + verify_phone_number?: boolean, + password?: string, + connection?: string, + client_id?: string, + username?: string +} + +async function getUserRole(userId: string) : Promise<{role:string}> { + let app_metadata = (await getUserById(userId)).app_metadata; + return {role: app_metadata.role}; +} + +/* + Get user by ID: https://auth0.com/docs/api/management/v2#!/Users/get_users_by_id + Get all users: https://auth0.com/docs/api/management/v2#!/Users/get_users + Update a user: https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id + */ + +export async function getUserById(userId: string) : Promise { + userId = userId.startsWith("auth0|") ? userId : `auth0|${userId}`; + return callManagementApi(`/users/${userId}`); +} + +export async function getAllUsers(per_page? : number, page?: number) { + let querystring = ""; + if(!!per_page || !!page) { + querystring = "?"; + let perPageClause = !per_page ? "" : `per_page=${per_page}` + let pageClause = !page ? "" : `page=${page}`; + querystring = `?${perPageClause}&${pageClause}`; + } + return callManagementApi(`/users${querystring}`); +} + +export async function updateUser(userId: string, userInfoPatch: UserInfoPatch) { + userId = userId.startsWith("auth0|") ? userId : `auth0|${userId}`; + return callManagementApi(`/users/${userId}`, "PATCH", JSON.stringify(userInfoPatch)); +} + +async function callManagementApi(path: string, method = "GET", body = "", refresh = false) : Promise { + let mgmtDomain = process.env.AUTH0_DOMAIN; + let adminToken = await getAdminToken(refresh); + let url = encodeURI(`https://${mgmtDomain}/api/v2${path}`); + let options : any = { + method, + "headers": { + "Authorization": `Bearer ${adminToken}` + } + }; + if(!!body) { + options.headers["Content-Type"] = "application/json"; + options.body = body; + } + let metaResponse = await fetch(url, options); + if(metaResponse.status === 200) { + let json = await metaResponse.json() + return json; + } else if (!refresh && metaResponse.status === 401) { + return await callManagementApi(path, method, body,true); + } else { + throw new Error(JSON.stringify(metaResponse)); + } +} + + +export async function getUserInfo(authHeader: string) { + try { + let userResponse = await fetch(`https://${process.env.AUTH0_DOMAIN}/userinfo`, { + "method": "GET", + "headers": {"Authorization": authHeader} + }); + let payload: any = await userResponse.json(); + let userId : string = !payload.sub ? "" : payload.sub; + return {userId}; + } catch (e) { + throw e; + } +} + +export type Auth0JwtVerifier = (request: FastifyRequest) => Promise<{userAppId: string, admin: boolean, role: string}>; +export async function verifyJwt(request: FastifyRequest) { + let authHeader = !request.headers.authorization ? "" : request.headers.authorization + if(!authHeader) { + return {userAppId: "", admin: false, role: ""}; + } else { + let {userId} = await getUserInfo(authHeader); + let {role} = await getUserRole(userId); + let admin: boolean = role === "admin" + let userAppId = userId.indexOf("|") > 0 ? userId.split("|")[1] : userId; + return {userAppId, admin, role}; + } +} diff --git a/src/cors.ts b/src/cors.ts new file mode 100644 index 0000000..8ceb892 --- /dev/null +++ b/src/cors.ts @@ -0,0 +1,10 @@ +import {FastifyInstance} from "fastify"; +import fastifyCors from "fastify-cors"; + +export function registerCorsHandler(server: FastifyInstance) { + server.register(fastifyCors, { + origin: [/^https?:\/\/localhost/, /^https?:\/\/ranlab-app-phzez.ondigitalocean.app/], + credentials: true, + strictPreflight: true + }); +} diff --git a/src/database/firestore.ts b/src/database/firestore.ts new file mode 100644 index 0000000..45d73fd --- /dev/null +++ b/src/database/firestore.ts @@ -0,0 +1,3 @@ +import {Firestore} from "@google-cloud/firestore"; + +export const productionFirestore = new Firestore(); diff --git a/src/database/productionDataLayer.ts b/src/database/productionDataLayer.ts new file mode 100644 index 0000000..e611965 --- /dev/null +++ b/src/database/productionDataLayer.ts @@ -0,0 +1,354 @@ +import {Business, CHUNK_SIZE} from "../endpoints/businesses"; +import {EditRequest} from "../endpoints/editRequest"; +import { + DocumentData, DocumentSnapshot, + FieldValue, + Firestore, + Query, + QueryDocumentSnapshot, + Timestamp, + Transaction +} from "@google-cloud/firestore"; + +export interface IdObject { + id: string +} + +interface RegionFilters { + years?: {year: number, count: number}[] | undefined, + industries?: {industry: string, count: number}[] | undefined +} + +export interface Region { + id?: string | undefined, + name: string, + manager: string, + filters?: RegionFilters +} + +export interface Filters { + years?: number[] | undefined, + industries?: string[] +} + +export interface DataLayer { + getBusinessById(id: string): Promise; + getAllBusinesses(afterId?: string): Promise; + getBusinessesByRegion(region: string): Promise; + setBusiness(business: Business) : Promise; + deleteBusiness(id: string): Promise; + getFilters(regionId?: string) : Promise; + getRegionsManagedBy(managerId: string) : Promise; + getAllRegions(): Promise; + setRegion(region: Region): Promise; + deleteRegion(regionId: string): Promise; + getEditRequestById(id: string): Promise; + getAllEditRequests(pageSize: number, afterId?: string): Promise; + getEditRequestsForRegion(regionId: string, pageSize: number, afterId?: string): Promise; + getEditRequestsByStatus(status: string, pageSize: number, afterId?: string): Promise; + getEditRequestsByUser(userAppId: string, pageSize: number, afterId?: string): Promise; + createEditRequest(add: EditRequest): Promise; + updateEditRequest(body: EditRequest): Promise; + addIndustries(industries: string[]): Promise; + deleteIndustries(industries: string[]) : Promise; +} + +export class ProductionDataLayer implements DataLayer { + firestore: Firestore; + constructor(firestore: Firestore) { + this.firestore = firestore; + } + + async getBusinessById(id: string): Promise { + let businessSnapshot = await this.firestore.collection("businesses").doc(id).get(); + return {...businessSnapshot.data(), id: businessSnapshot.id}; + } + + async getBusinessesByRegion(regionId: string) : Promise { + let businessSnapshot = await this.firestore.collection("businesses").where("regionId", "==", regionId).get(); + return businessSnapshot.docs.map((b) => ({...b.data(), id: b.id})); + } + + async getAllBusinesses(afterId?: string): Promise { + let query = this.firestore.collection("businesses") + .orderBy("name") + .limit(CHUNK_SIZE); + if(!!afterId) { + let afterBiz = await this.firestore.collection("businesses").doc(afterId).get(); + if(afterBiz) { + query = query.startAfter(afterBiz); + } + } + return this.convertToBusinesses((await query.get()).docs); + } + + async setBusiness(newBusinessData: Business) : Promise { + const bc = this.firestore.collection("businesses"); + let businessRef = newBusinessData.id ? bc.doc(newBusinessData.id) : bc.doc(); + let regionRef = this.firestore.collection("regions").doc(newBusinessData.regionId); + return this.firestore.runTransaction(async transaction => { + let regionDoc = await transaction.get(regionRef); + if(!regionDoc.exists) { + throw "Bad Region"; + } + let filters = ProductionDataLayer.getCurrentFilters(regionDoc); + let businessDoc = await transaction.get(businessRef); + let updates : RegionFilters = { + years: [{year: newBusinessData.year_added, count: 1}], + industries: [{industry: newBusinessData.industry, count: 1}] + }; + + if(businessDoc.exists) { + let existingBusinessData = businessDoc.data(); + if(!!existingBusinessData) { + // @ts-ignore no idea why it thinks updates can be undefined here. + updates.years.push( + {year: existingBusinessData.year_added, count: -1}, + ); + // @ts-ignore no idea why it thinks updates can be undefined here. + updates.industries.push( + {industry: existingBusinessData.industry, count: -1}, + ); + } + } + + await transaction.update(regionRef, {filters: this.calculateNewFilters(filters, updates)}) + await transaction.set(businessRef, newBusinessData); + }).then(() => {id: businessRef.id}); + } + + async deleteBusiness(id: string) { + await this.firestore.runTransaction( + async transaction => { + let businessRef = this.firestore.collection("businesses").doc(id); + let businessDoc = await transaction.get(businessRef); + let businessData = businessDoc.data(); + if (!!businessData) { + if (!!businessData.regionId) { + let deleteBizFromFilter = { + years: [{year: businessData.year_added, count: -1}], + industries: [{industry: businessData.industry, count: -1}] + } + + await this.updateRegionFilters(businessData.regionId, deleteBizFromFilter, transaction); + } + await transaction.delete(businessRef); + } + } + ); + } + + async getFilters(region?: string) : Promise{ + if(!!region) { + let regionData = (await this.firestore.collection("regions").doc(region).get()).data(); + regionData = !!regionData ? regionData : {}; + return regionData.filters; + } else { + let industries: string[] = []; + let industryData = (await this.firestore.collection("industries").get()).docs; + industryData.forEach(i => industries.push(i.id)); + return {industries}; + } + } + + async getAllRegions() : Promise { + let regionsSnapshot = await this.firestore.collection("regions").get(); + return regionsSnapshot.docs.map((r) => ({id: r.id, name: r.data().name, manager: r.data().manager})); + } + + async getRegionsManagedBy(managerId: string): Promise { + let regionsSnapshot = await this.firestore.collection("regions").where("manager", "==", managerId).get(); + return regionsSnapshot.docs.map((r) => ({id: r.id, name: r.data().name, manager: r.data().manager})); + } + + async setRegion(region: Region): Promise { + let regionDoc = !!region.id + ? this.firestore.collection("regions").doc(region.id) + : this.firestore.collection("regions").doc(); + await regionDoc.set({name: region.name, "manager": region.manager}, {merge: true}); + return {id: regionDoc.id}; + } + + async deleteRegion(id: string): Promise { + await this.firestore.collection("regions").doc(id).delete(); + } + + async createEditRequest(editRequest: EditRequest): Promise { + let doc = this.firestore.collection("editRequests").doc(); + await doc.set({ + ...editRequest, + dateSubmitted: FieldValue.serverTimestamp(), + dateUpdated: FieldValue.serverTimestamp() + }); + return {id : doc.id}; + } + + async getEditRequestById(id: string) : Promise { + let requestData = (await this.firestore.collection("editRequests").doc(id).get()).data(); + return this.convertToEditRequest(id, requestData); + } + + async updateEditRequest(body: EditRequest): Promise { + let id = !!body.id ? body.id : ""; + let requestData = (await this.firestore.collection("editRequests").doc(id).get()).data(); + let updatedRequestData = { + ...this.convertToEditRequest(id, requestData), + ...body, + dateUpdated: FieldValue.serverTimestamp() + }; + await this.firestore.collection("editRequests").doc(id).update(updatedRequestData); + let updatedRequest = await this.getEditRequestById(id); + if(!updatedRequest) { + throw "Could not retrieve updated record!"; + } else { + return updatedRequest; + } + } + + async getAllEditRequests(pageSize: number, afterId?: string): Promise { + let query = this.firestore.collection("editRequests"); + return await this.getPaginatedEditRequests(query, pageSize, afterId); + } + + async getEditRequestsForRegion(regionId: string, pageSize: number,afterId?: string) : Promise { + let query = this.firestore.collection("editRequests").where("regionId", "==", regionId); + return this.getPaginatedEditRequests(query, pageSize, afterId); + } + + async getEditRequestsByStatus(status: string, pageSize: number, afterId?: string): Promise { + let query = this.firestore.collection("editRequests").where("status", "==", status); + return await this.getPaginatedEditRequests(query, pageSize, afterId); + } + + async getEditRequestsByUser(userAppId: string, pageSize: number, afterId?: string): Promise { + let query = this.firestore.collection("editRequests").where("submitter", "==", userAppId); + return this.getPaginatedEditRequests(query, pageSize, afterId); + } + + async addIndustries(industries: string[]): Promise { + industries.forEach( + i => this.firestore.collection("industries").doc(i).set({active: true}) + ); + } + + async deleteIndustries(industries: string[]) : Promise{ + let promises: Promise[] = [] + for(let i = 0; i < industries.length; i++) { + promises.push(this.firestore.collection("industries").doc(industries[i]).delete()); + } + return Promise.all(promises); + } + + private async getPaginatedEditRequests(query: Query, pageSize: number, afterId: string | undefined) { + let requests: EditRequest[] = []; + query = query.orderBy("dateSubmitted", 'desc').limit(pageSize); + if (!!afterId) { + let afterRecord = await this.firestore.collection("editRequests").doc(afterId).get(); + query = query.startAfter(afterRecord); + } + + let editRequestDocs = (await query.get()).docs; + editRequestDocs.forEach( + (doc) => { + let converted = this.convertToEditRequest(doc.id, doc.data()); + if (!!converted) { + requests.push(converted); + } + } + ); + return requests; + } + + convertToEditRequest(id: string, documentData: DocumentData | undefined) : EditRequest | null { + if(!!documentData) { + let request = documentData; + request.dateSubmitted = ((documentData.dateSubmitted)).toDate(); + request.dateUpdated = ((documentData.dateUpdated)).toDate(); + request.id = id; + return request; + } else { + return null; + } + } + + convertToBusinesses(docs: Array) : Business[] { + let businesses = []; + for(let doc of docs) { + let biz : Business = { + id: doc.id, + regionId: doc.data().regionId, + name: doc.data().name, + employees: doc.data().employees, + industry: doc.data().industry, + year_added: doc.data().year_added + } + if(!!doc.data().location) { + biz.location = doc.data().location + } + businesses.push(biz); + } + return businesses; + } + + private async updateRegionFilters(regionId: string, filterUpdate: RegionFilters, transaction: Transaction) { + let regionRef = this.firestore.collection("regions").doc(regionId); + let regionDoc = await transaction.get(regionRef); + if (!!regionDoc) { + let regionData = regionDoc.data(); + if (!!regionData) { + let updatedFilters = this.calculateNewFilters(regionData.filters, filterUpdate); + let regionUpdate = {filters: updatedFilters}; + + await transaction.update(regionRef, regionUpdate) + } + } + } + + private calculateNewFilters(filters: RegionFilters, updates: RegionFilters) { + let years : {year: number, count: number}[] = !!filters.years ? filters.years : []; + let industries : {industry: string, count: number}[] = !!filters.industries ? filters.industries : []; + if(!!updates.years && updates.years.length > 0) { + for(let yearUpdate of updates.years) { + let yearEntryIndex = years.findIndex(y => y.year === yearUpdate.year); + if (yearEntryIndex < 0) { + years.push({year: yearUpdate.year, count: yearUpdate.count}); + } else { + years[yearEntryIndex].count += yearUpdate.count; + } + } + for(let i = years.length-1; i >=0; i--) { + if(years[i].count <= 0) { + years.splice(i,1); + } + } + } + + if(!!updates.industries && updates.industries.length > 0) { + for(let industryUpdate of updates.industries) { + let industryEntryIndex = industries.findIndex(i => i.industry === industryUpdate.industry); + if (industryEntryIndex < 0) { + industries.push(industryUpdate); + } else { + industries[industryEntryIndex].count += industryUpdate.count; + } + for(let i = industries.length-1; i >=0; i--) { + if(industries[i].count <= 0) { + industries.splice(i,1); + } + } + } + } + return {years, industries}; + } + + private static getCurrentFilters(regionDoc: DocumentSnapshot) : RegionFilters{ + let regionData = !!regionDoc && !!regionDoc.data() ? regionDoc.data() : {filters: {}}; + if(!!regionData) { + return !!regionData.filters ? regionData.filters : {}; + } else { + return {}; + } + } + +} + diff --git a/src/endpoints/businesses.ts b/src/endpoints/businesses.ts new file mode 100644 index 0000000..9d4e238 --- /dev/null +++ b/src/endpoints/businesses.ts @@ -0,0 +1,149 @@ +import type {FastifyInstance, RequestGenericInterface} from 'fastify'; +import {DataLayer, Filters} from "../database/productionDataLayer"; +import {GeoPoint} from "@google-cloud/firestore"; +import {createBizSchema, exportBusinessesSchema, getBizSchema, updateBizSchema} from "./docs/businessesSchemas"; +import {isRegionManager} from "../utils"; +import {Auth0JwtVerifier} from "../auth0"; +import {AuthenticatedRequest, AuthenticatedRequestByRegionId} from "./endpointUtils"; + +export const CHUNK_SIZE = 100; +export const CSV_HEADER = "'id','name','location','regionId','industry','year_added','employees','location'\n"; + +export function convertToCSV(businesses: Business[]) { + let csvChunk = ""; + for(let biz of businesses) { + csvChunk += `'${biz.id}','${biz.name}','${biz.location}','${biz.regionId}','${biz.industry}',${biz.year_added},${biz.employees},${biz.location}\n`; + } + return csvChunk; +} + +interface CreateBusinessRequest extends RequestGenericInterface { + Params: { + regionId: string + }, + Body: Business, +} + +interface UpdateBusinessRequest extends RequestGenericInterface { + Params: { + businessId: string + }, + Body: Business +} + +export interface Business { + id?: string | undefined; + name: string; + employees: number; + regionId: string; + industry: string; + year_added: number; + location?: GeoPoint | null | undefined +} + +export interface BusinessUpdate { + id: string, + name?: string, + employees?: number, + industry?: string, + year_added?: number; + location?: GeoPoint | null | undefined +} + +export function createBusinessesEndpoint(app: FastifyInstance, dataLayer: DataLayer, verifyJwt: Auth0JwtVerifier) { + + app.get( + '/regions/:regionId/businesses', + {schema: getBizSchema}, + async (request , reply) => { + + let {userAppId, admin} = await verifyJwt(request) + if(!(admin || await isRegionManager(userAppId, request.params.regionId, dataLayer))) { + reply.unauthorized("User does not have access to region"); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + region: request.params.regionId, + businesses: [], + filters: {} + }; + + response.businesses = (await dataLayer.getBusinessesByRegion(request.params.regionId)).slice(0, 10); + response.filters = await dataLayer.getFilters(request.params.regionId); + return JSON.stringify(response); + } + } + ); + + app.post( + '/regions/:regionId/businesses', + {schema: createBizSchema}, + async (request) => { + let {userAppId, admin} = await verifyJwt(request); + if(!(admin || await isRegionManager(userAppId, request.params.regionId, dataLayer))) { + throw app.httpErrors.unauthorized("User does not have access to region"); + } else if (!!request.body.regionId && request.body.regionId !== request.params.regionId) { + throw app.httpErrors.badRequest("Region ID mismatch between route and body "); + } else { + let businessRef = await dataLayer.setBusiness(request.body); + let response = { + status: "ok", + date: Date.now(), + businessId: businessRef.id + }; + return JSON.stringify(response); + } + } + ); + + app.post( + '/businesses/:businessId', + {schema: updateBizSchema}, + async (request,reply) => { + let {admin} = await verifyJwt(request); + if(!admin) { + reply.unauthorized("Only admin users can directly update business information"); + return; + } else { + let updatedBiz = {...request.body, id: request.params.businessId}; + await dataLayer.setBusiness(updatedBiz); + let response = { + status: "ok", + date: Date.now(), + business: updatedBiz + }; + return JSON.stringify(response); + } + } + ); + + app.get( + '/businesses/export', + {schema: exportBusinessesSchema}, + async (request,reply) => { + let {admin} = await verifyJwt(request); + + if(!admin) { + reply.unauthorized("Only admin users can export all business rows!"); + return; + } else { + reply.raw.writeHead(200, { + 'Content-Type': 'text/csv', + 'Content-Disposition': 'attachment; filename=businesses.csx' + }); + + reply.raw.write(CSV_HEADER); + let chunk = await dataLayer.getAllBusinesses(); + while(chunk.length > 0) { + reply.raw.write(convertToCSV(chunk)); + chunk = await dataLayer.getAllBusinesses(chunk[chunk.length-1].id); + } + reply.raw.end(); + } + } + ); + + return app; +} diff --git a/src/endpoints/docs/basicSchemas.ts b/src/endpoints/docs/basicSchemas.ts new file mode 100644 index 0000000..5d2a365 --- /dev/null +++ b/src/endpoints/docs/basicSchemas.ts @@ -0,0 +1,19 @@ +export const byIdSchema = { + type: 'object', + properties: { + id: { + type: 'string', + description: 'The ID of the item to retrieve' + } + } +}; + +export const byRegionIdSchema = { + type: 'object', + properties: { + regionId: { + type: 'string', + description: "The region to use for this request" + } + } +}; diff --git a/src/endpoints/docs/businessesSchemas.ts b/src/endpoints/docs/businessesSchemas.ts new file mode 100644 index 0000000..104ae3c --- /dev/null +++ b/src/endpoints/docs/businessesSchemas.ts @@ -0,0 +1,104 @@ +import {filtersSchema} from "./filterSchemas"; +import {byRegionIdSchema} from "./basicSchemas"; + +export const businessSchema = { + type: 'object', + properties: { + id: { + type: "string", + description: "The id for the specific business. Should be omitted when creating new entries.", + nullable: true + }, + name: {type: 'string'}, + employees: { type: 'number'}, + regionId: {type: 'string'}, + industry: {type: 'string'}, + year_added: {type: 'number'}, + location: { + type: 'array', + items: { type: "number", max: 2}, + nullable: true + } + } +}; + +export const businessesSchema = { + type: 'array', + items: businessSchema +}; + +const bizByIdSchema = { + type: 'object', + properties: { + businessId: {type: "string"} + } +}; + +export const getBizSchema = { + description: "Returns all businesses located in the specified region", + params: byRegionIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + region: {type: 'string'}, + businesses: businessesSchema, + filters: filtersSchema + } + } + } +}; + +export const exportBusinessesSchema = { + description: "Exports all business records as a stream of csv text", + responses: { + 200: { + description: 'Successful response', + headers: { + schema: { + 'transfer-encoding': 'chunked' + } + }, + content: { + schema: { + 'text/csv': 'string' + } + } + } + } +}; + +export const createBizSchema = { + description: "Creates a business in the specified region", + params: byRegionIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: "string"}, + businessId: {type: "string"} + } + } + } +}; + +export const updateBizSchema = { + description: "Updates the specified business", + params: bizByIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: "string"}, + business: businessSchema + } + } + } +}; diff --git a/src/endpoints/docs/editRequestSchemas.ts b/src/endpoints/docs/editRequestSchemas.ts new file mode 100644 index 0000000..a225400 --- /dev/null +++ b/src/endpoints/docs/editRequestSchemas.ts @@ -0,0 +1,137 @@ +import {byIdSchema, byRegionIdSchema} from './basicSchemas'; +import { businessSchema} from './businessesSchemas'; + + const editRequestSchema = { + type: 'object', + properties: { + id: { + type: 'string', + description: 'Omit when creating new edit requests' + }, + regionId: {type: 'string'}, + submitter: {type: 'string'}, + dateSubmitted: {type: 'string'}, + dateUpdated: {type: 'string'}, + status: {type: 'string'}, + adds: { + type: 'array', + items: businessSchema + }, + updates: { + type: 'array', + items: businessSchema + }, + deletes: { + type: 'array', + items: businessSchema + } + } +} + +const paginatedEditRequestsResponseSchema = { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + editRequests: { + type: 'array', + items: editRequestSchema + }, + totalCount: { type: 'number' } + } +}; + +export const getEditRequestByIdSchema = { + params: byIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + editRequest: editRequestSchema + } + } + } +}; + +export const getEditRequestsByRegionSchema = { + params: byRegionIdSchema, + querystring: { + type: 'object', + properties: { + afterId: {type: 'string'} + } + }, + response: { + 200: paginatedEditRequestsResponseSchema + } +}; + +export const getAllEditRequestsByStatusSchema = { + description: 'Returns all edit requests. Query string may specify the status parameter to filter for a specific status. Only usable by system admins', + querystring: { + type: 'object', + properties: { + status: {type: 'string'}, + afterId: {type: 'string'} + } + }, + response: { + 200: paginatedEditRequestsResponseSchema + } +}; + +export const getEditPreviewSchema = { + params: byIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + added: { + type: 'array', + items: businessSchema + }, + updated: { + type: 'array', + items: businessSchema + }, + deleted: { + type: 'array', + items: businessSchema + } + } + } + } +}; + +export const updateEditRequestSchema = { + params: byIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + editRequest: editRequestSchema + } + } + } +} + +export const createEditRequestSchema = { + description: 'Creates the supplied edit request', + body: editRequestSchema, + response: { + 201: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + id: {type: 'string'} + } + } + } +}; diff --git a/src/endpoints/docs/filterSchemas.ts b/src/endpoints/docs/filterSchemas.ts new file mode 100644 index 0000000..b223fcd --- /dev/null +++ b/src/endpoints/docs/filterSchemas.ts @@ -0,0 +1,95 @@ +import {byRegionIdSchema} from "./basicSchemas"; + +export const filtersSchema = { + type: 'object', + description: "Only valid for GET responses", + properties: { + years: { + type: 'array', + items: {type: 'number'} + }, + industries: { + type: 'array', + items: {type: 'string' } + }, + } +} + +let industryListSchema = { + type: 'object', + properties: { + industries: { + type: 'array', + items: { + type: 'string' + } + } + } +}; + +export const getFilterSchema = { + description: 'Endpoint for interacting directly with filters for a particular region', + params: byRegionIdSchema, + security: [], + response: { + 200: { + type: 'object', + properties: { + status: { type: 'string' }, + date: { type: 'string' }, + filters: filtersSchema + } + } + } +}; + +export const getAllIndustriesSchema = { + description: 'Endpoint returning the global list of industries', + security: [], + response: { + 200: { + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + industries: { + type: "array", + items: { + type: "string" + } + } + } + } + } +}; + +export const addIndustriesSchema = { + description: 'Adds one or more industries to the global list of industries that can be filtered on', + body: industryListSchema, + response: { + 201: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + id: {type: 'string'} + } + } + } +}; + +export const deleteIndustriesSchema = { + description: 'Deletes one or more industries from the global list of industries', + body: industryListSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + id: {type: 'string'} + } + } + } +}; + diff --git a/src/endpoints/docs/regionSchemas.ts b/src/endpoints/docs/regionSchemas.ts new file mode 100644 index 0000000..e14d5b4 --- /dev/null +++ b/src/endpoints/docs/regionSchemas.ts @@ -0,0 +1,124 @@ +import {filtersSchema} from "./filterSchemas"; +import {byRegionIdSchema} from "./basicSchemas"; + +const managerIdSchema = { + type: 'string', + description: "The second half of the auth0 sub claim. For example, for a user with {sub: 'auth0|123456'}, their manager ID is '123456'" +}; + +const byManagerIdSchema = { + type: 'object', + properties: { + managerId: managerIdSchema + } +}; + +const getRegionSchema = { + type: 'object', + properties: { + id: {type: "string"}, + name: {type: "string"}, + manager: managerIdSchema, + filters: filtersSchema + } +}; + +const createRegionSchema = { + type: 'object', + properties: { + name: {type: "string"}, + manager: managerIdSchema + } +}; + +const updateRegionSchema = { + id: { + type: "string", + description: "The id of the region, if applicable.", + nullable: true + }, + name: {type: "string"}, + manager: managerIdSchema +} + + +export const getManagedRegionsReqSchema = { + description: "Returns all regions managed by the authenticated user", + securitySchemes: [], + params: byManagerIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + regions: { + type: 'array', + items: getRegionSchema + } + } + } + } +}; + +export const getSingleRegionReqSchema = { + description: "Returns a single region specified by ID", + securitySchemes: [], + params: byRegionIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + region: getRegionSchema + } + } + } +}; + +export const createRegionReqSchema = { + description: "Returns all regions managed by the authenticated user", + securitySchemes: [], + params: byManagerIdSchema, + body: createRegionSchema, + response: { + 201: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + region: {type: 'string'} + } + } + } +}; + +export const updateRegionReqSchema = { + description: "Updates the data for a specified region", + params: byRegionIdSchema, + body: updateRegionSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + region: getRegionSchema + } + } + } +}; + +export const deleteRegionReqSchema = { + description: "Deletes the specified region", + params: byRegionIdSchema, + response: { + 204: { + description: 'Region successfully deleted', + type: 'null' + } + } +}; diff --git a/src/endpoints/docs/userSchema.ts b/src/endpoints/docs/userSchema.ts new file mode 100644 index 0000000..0870f1d --- /dev/null +++ b/src/endpoints/docs/userSchema.ts @@ -0,0 +1,82 @@ +export const userInfoSchema = { + type: 'object', + properties: { + sub: {type: 'string'}, + } +}; + +export const byUserIdSchema = { + type: 'object', + properties: { + id: { + description: "userAppId - this is the second half of the user's auth0 'sub' claim (ie for auth0|123456, the userAppId is 123456)", + type: 'string' + } + } +} + +export const getUserInfoRequestSchema = { + description: 'Returns a single user specified by userAppId', + securitySchemes: [], + params: byUserIdSchema, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + userInfo: userInfoSchema + } + } + } +}; + +export const getAllUsersRequestSchema = { + description: 'Returns all users, with optional pagination', + securitySchemes: [], + querystring: { + type: 'object', + properties: { + per_page: { + type: 'number', + description: 'Number of users per page' + }, + page: { + type: 'number', + description: 'Page number to retrieve' + } + } + }, + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + userInfo: { + type: 'array', + items: userInfoSchema + } + } + } + } +}; + +export const updateUserRequestSchema = { + description: 'Updates the specified user', + params: byUserIdSchema, + securitySchemes: [], + response: { + 200: { + description: 'Successful response', + type: 'object', + properties: { + status: {type: 'string'}, + date: {type: 'string'}, + userInfo: userInfoSchema + } + } + } +}; diff --git a/src/endpoints/editRequest.ts b/src/endpoints/editRequest.ts new file mode 100644 index 0000000..5b553bb --- /dev/null +++ b/src/endpoints/editRequest.ts @@ -0,0 +1,275 @@ +import {FastifyInstance } from "fastify"; +import {DataLayer} from "../database/productionDataLayer"; +import {Business, BusinessUpdate} from "./businesses"; +import {Auth0JwtVerifier} from "../auth0"; +import {AuthenticatedRequest, AuthenticatedRequestById, AuthenticatedRequestByRegionId} from "./endpointUtils"; +import {isRegionManager} from "../utils"; +import { + createEditRequestSchema, getAllEditRequestsByStatusSchema, getEditPreviewSchema, + getEditRequestByIdSchema, getEditRequestsByRegionSchema, updateEditRequestSchema +} from "./docs/editRequestSchemas"; + +export const DEFAULT_PAGE_SIZE = 25; +export const previewAddId = "[preview-id]" + +export interface EditRequest { + id?: string, + regionId: string, + submitter?: string, + reviewer?: string, + dateSubmitted?: Date | string, + dateUpdated?: Date | string + status?: string, + adds?: Business[], + updates?: BusinessUpdate[], + deletes?: BusinessUpdate[] +} + +interface CreateEditRequest extends AuthenticatedRequestByRegionId { + Body: EditRequest +} + +interface UpdateEditRequest extends AuthenticatedRequestById { + Body: EditRequest +} + +interface GetPaginatedEditsByRegionId extends AuthenticatedRequestByRegionId { + Querystring: { + afterId: string, + pageSize: number + } +} + +interface GetPaginatedEditsByStatusRequest extends AuthenticatedRequest { + Querystring: { + status: string, + pageSize: number, + afterId: string + } +} + +export function createEditEndpoint(app: FastifyInstance, dataLayer: DataLayer, verifyJwt: Auth0JwtVerifier) { + const MAX_COUNT = 1000000000; + + app.get( + "/edits/all", + {schema: getAllEditRequestsByStatusSchema}, + async (request, reply) => { + let { admin } = await verifyJwt(request); + if(!admin) { + reply.unauthorized("Only admins may access this data!"); + return; + } else { + let editRequests : EditRequest[]; + let totalCount: number; + let pageSize = !!request.query.pageSize && request.query.pageSize > 0 ? request.query.pageSize : DEFAULT_PAGE_SIZE; + if(!request.query.status) { + editRequests = await dataLayer.getAllEditRequests(pageSize, request.query.afterId); + totalCount = (await dataLayer.getAllEditRequests(MAX_COUNT)).length; + } else { + editRequests = await dataLayer.getEditRequestsByStatus(request.query.status, pageSize, request.query.afterId) + totalCount = (await dataLayer.getEditRequestsByStatus(request.query.status, MAX_COUNT)).length; + } + let response = { + status: "ok", + editRequests, + totalCount + } + return JSON.stringify(response); + } + } + ); + + app.get( + `/region/:regionId/edits`, + {schema: getEditRequestsByRegionSchema}, + async (request, reply) => { + let {userAppId, admin} = await verifyJwt(request); + if(!admin && !userAppId && !(await isRegionManager(userAppId, request.params.regionId, dataLayer))) { + reply.unauthorized("Must be logged in to submit requests"); + return; + } else { + let pageSize = !!request.query.pageSize && request.query.pageSize > 0 ? request.query.pageSize : DEFAULT_PAGE_SIZE; + let response = { + status: "ok", + editRequests: [], + totalCount: 0 + } + response.editRequests = await dataLayer.getEditRequestsForRegion(request.params.regionId, pageSize, request.query.afterId) + response.totalCount = (await dataLayer.getEditRequestsForRegion(request.params.regionId, MAX_COUNT)).length + return JSON.stringify(response); + } + } + ); + + app.get( + `/edits/:id`, + {schema: getEditRequestByIdSchema}, + async (request, reply) => { + let {userAppId, admin} = await verifyJwt(request); + if(!admin && !userAppId) { + reply.unauthorized("Must be logged in to submit requests"); + return; + } else { + let response = { + status: "ok", + editRequest: null + } + response.editRequest = await dataLayer.getEditRequestById(request.params.id) + return JSON.stringify(response); + } + } + ); + + app.post( + `/edits/:id`, + {schema: updateEditRequestSchema}, + async(request, reply) => { + let {userAppId, admin} = await verifyJwt(request); + if(!admin && !!request.body.status) { + reply.unauthorized("Only system administrators may update edit request statuses!"); + return; + } else if (!admin && !(await isRegionManager(userAppId, request.body.regionId, dataLayer))) { + reply.unauthorized("Only administrators and managers may update edit requests!"); + return; + } else { + if(!!request.body.status) { + let currentRequest = await dataLayer.getEditRequestById(request.params.id); + if (currentRequest?.status !== request.body.status) { + if (!currentRequest?.reviewer) { + request.body.reviewer = userAppId; + } else { + reply.forbidden("Cannot edit the status of a request after review has been started!") + return; + } + if(request.body.status.toLowerCase() === "approved") { + let applied = await approveAndApplyEditRequest(request.params.id, userAppId); + if (applied) { + let response = { + status: "ok", + ...applied + }; + return JSON.stringify(response); + } else { + reply.badRequest("Unable to process approval"); + return; + } + } + } + } + let response = { + status: "ok", + editRequest: await dataLayer.updateEditRequest({...request.body, id: request.params.id}) + } + return JSON.stringify(response); + } + } + ); + + app.get( + `/edits/:id/preview`, + {schema: getEditPreviewSchema}, + async (request, reply) => { + let { admin } = await verifyJwt(request); + if(!admin) { + reply.unauthorized("Only admins may access this data!"); + return; + } else { + let addedBusinesses: Business[] = []; + let updatedBusinesses: Business[] = []; + let deletedBusinesses: Business[] = []; + let editRequest = await dataLayer.getEditRequestById(request.params.id); + for(const add of editRequest?.adds || []) { + addedBusinesses.push({...add, id: previewAddId}); + } + for (const update of editRequest?.updates || []) { + let biz = await dataLayer.getBusinessById(update.id) + if(!!biz) { + updatedBusinesses.push({...biz, ...update}); + } + } + for (const deleted of editRequest?.deletes || []) { + let biz = await dataLayer.getBusinessById(deleted.id); + if(!!biz) { + deletedBusinesses.push(biz); + } + } + + let response = { + status: "ok", + added: addedBusinesses, + updated: updatedBusinesses, + deleted: deletedBusinesses + } + + return JSON.stringify(response); + } + } + ); + + app.post( + '/region/:regionId/edits', + {schema: createEditRequestSchema}, + async (request, reply) => { + let {userAppId, admin} = await verifyJwt(request); + if(!admin && !userAppId && !(await isRegionManager(userAppId, request.params.regionId, dataLayer))) { + reply.unauthorized("Must be logged in to submit requests"); + return; + } else { + const incomingRequest = request.body; + incomingRequest.status = "Pending"; + let response = { + status: "ok", + id: (await dataLayer.createEditRequest(incomingRequest)).id + }; + reply.code(201); + return JSON.stringify(response); + } + } + ); + + async function approveAndApplyEditRequest(id: string, reviewer: string) { + let addedBusinesses: Business[] = []; + let updatedBusinesses: Business[] = []; + let deletedBusinesses: Business[] = []; + let editRequest = await dataLayer.getEditRequestById(id); + if(!!editRequest) { + let editPromises = []>[]; + for (const add of editRequest?.adds || []) { + editPromises.push(dataLayer.setBusiness(add)); + addedBusinesses.push({...add, id: previewAddId}); + } + for (const update of editRequest?.updates || []) { + let biz = await dataLayer.getBusinessById(update.id) + if (!!biz) { + let updatedBiz = {...biz, ...update}; + editPromises.push(dataLayer.setBusiness(updatedBiz)); + updatedBusinesses.push(updatedBiz); + } + } + for (const deleted of editRequest?.deletes || []) { + let biz = await dataLayer.getBusinessById(deleted.id); + if (!!biz) { + editPromises.push(dataLayer.deleteBusiness(deleted.id)); + deletedBusinesses.push(biz); + } + } + + let updatedEditRequest = {...editRequest, status: "Approved", reviewer}; + editPromises.push(dataLayer.updateEditRequest(updatedEditRequest)) + await Promise.all(editPromises); + return { + added: addedBusinesses, + updated: updatedBusinesses, + deleted: deletedBusinesses, + editRequest: updatedEditRequest + } + } else { + return false; + } + } + + + return app; + +} diff --git a/src/endpoints/endpointUtils.ts b/src/endpoints/endpointUtils.ts new file mode 100644 index 0000000..96ff239 --- /dev/null +++ b/src/endpoints/endpointUtils.ts @@ -0,0 +1,19 @@ +import {RequestGenericInterface} from "fastify"; + +export interface AuthenticatedRequest extends RequestGenericInterface { + Headers: { + access_token: string + } +} + +export interface AuthenticatedRequestByRegionId extends AuthenticatedRequest { + Params: { + regionId: string + } +} + +export interface AuthenticatedRequestById extends AuthenticatedRequest { + Params: { + id: string + } +} diff --git a/src/endpoints/filters.ts b/src/endpoints/filters.ts new file mode 100644 index 0000000..7193a7c --- /dev/null +++ b/src/endpoints/filters.ts @@ -0,0 +1,111 @@ +import type {FastifyInstance, RequestGenericInterface} from 'fastify'; +import {DataLayer, Filters} from "../database/productionDataLayer"; +import { + addIndustriesSchema, + deleteIndustriesSchema, + getAllIndustriesSchema, + getFilterSchema +} from "./docs/filterSchemas"; +import {Auth0JwtVerifier} from "../auth0"; +import {AuthenticatedRequest} from "./endpointUtils"; + +interface GetFiltersRequest extends RequestGenericInterface { + Params: { + regionId: string + } +} + +interface IndustriesRequest extends AuthenticatedRequest { + Body: { + industries: string[] + } +} + +export function createFiltersEndpoint(app: FastifyInstance, dataLayer: DataLayer, verifyJwt: Auth0JwtVerifier) { + app.get('/regions/:regionId/filters', {schema: getFilterSchema}, + async (request, reply) => { + let {userAppId} = await verifyJwt(request); + if (!userAppId) { + reply.unauthorized("User not found!"); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + filters: null + } + response.filters = await dataLayer.getFilters(request.params.regionId); + return JSON.stringify(response); + } + } + ); + + app.get("/filters/industries", {schema: getAllIndustriesSchema}, + async (request, reply) =>{ + let {userAppId} = await verifyJwt(request); + if(!userAppId) { + reply.unauthorized("Must be logged in to access this data"); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + industries: [] + }; + let regions = await dataLayer.getAllRegions(); + let industries: string[] = []; + regions.forEach((r) => { + if (!!r.filters && !!r.filters.industries) { + industries.push(...r.filters.industries.map(i => i.industry)); + } + }); + let globalIndustries = (await dataLayer.getFilters()).industries ?? []; + industries.push(...globalIndustries); + // Get just unique values + response.industries = Array.from(new Set(industries)); + + return JSON.stringify(response); + } + } + ) + + app.post( + "/filters/industries", + {schema: addIndustriesSchema}, + async (request, reply) => { + let {userAppId} = await verifyJwt(request); + if (!userAppId) { + reply.unauthorized("Must be logged in to access this data"); + return; + } else { + let response = { + status: "ok", + date: Date.now() + }; + + await dataLayer.addIndustries(request.body.industries); + return JSON.stringify(response); + } + }); + + app.delete( + "/filters/industries", + {schema: deleteIndustriesSchema}, + async (request, reply) => { + let {userAppId} = await verifyJwt(request); + if (!userAppId) { + reply.unauthorized("Must be logged in to perform this operation"); + return; + } else { + let response = { + status: "ok", + date: Date.now() + }; + + await dataLayer.deleteIndustries(request.body.industries); + return JSON.stringify(response); + } + }); + + return app; +} diff --git a/src/endpoints/ping.ts b/src/endpoints/ping.ts new file mode 100644 index 0000000..2630fd1 --- /dev/null +++ b/src/endpoints/ping.ts @@ -0,0 +1,7 @@ +import type { FastifyInstance } from 'fastify'; + +export default function createPingEndpoint(app: FastifyInstance) { + app.get('/ping', async () => `${JSON.stringify({ status: 'ok', date: Date.now() })}\n`); + + return app; +} diff --git a/src/endpoints/regions.ts b/src/endpoints/regions.ts new file mode 100644 index 0000000..0854f82 --- /dev/null +++ b/src/endpoints/regions.ts @@ -0,0 +1,154 @@ +import type {FastifyInstance, RequestGenericInterface} from 'fastify'; +import {DataLayer, Region} from "../database/productionDataLayer"; +import { + getManagedRegionsReqSchema, + getSingleRegionReqSchema, + createRegionReqSchema, + updateRegionReqSchema, + deleteRegionReqSchema +} from "./docs/regionSchemas"; +import {Auth0JwtVerifier} from "../auth0"; +import {isRegionManager} from "../utils"; + +interface GetManagedRegionsRequest extends RequestGenericInterface { + Params: { + managerId: string + } +} + +interface GetSingleRegionRequest extends RequestGenericInterface { + Params: { + regionId: string + } +} + +interface CreateRegionRequest extends RequestGenericInterface { + Body: Region +} + +interface DeleteRegionRequest extends RequestGenericInterface { + Params: { + regionId: string + } +} + +interface UpdateRegionRequest extends RequestGenericInterface { + Params: { + regionId: string + }, + Body: Region +} + +export default function createRegionsEndpoint(app: FastifyInstance, dataLayer : DataLayer, verifyJwt: Auth0JwtVerifier) { + app.get( + '/regions/manager/:managerId', + {schema: getManagedRegionsReqSchema}, + async (request,reply) => { + let {userAppId, admin} = await verifyJwt(request); + if(!userAppId) { + reply.send(reply.unauthorized); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + regions: [] + }; + if (admin && userAppId == request.params.managerId) { + response.regions.push(...(await dataLayer.getAllRegions())); + } else if (admin || userAppId == request.params.managerId) { + response.regions.push(...(await dataLayer.getRegionsManagedBy(request.params.managerId))); + } + return JSON.stringify(response); + } + } + ); + + app.get( + '/regions/:regionId', + {schema: getSingleRegionReqSchema}, + async(request, reply ) => { + try { + let {userAppId, admin} = await verifyJwt(request); + let response = { + status: "ok", + date: Date.now(), + region: null + } + let regions: Region[]; + if (admin) { + regions = (await dataLayer.getAllRegions()); + } else if (!userAppId) { + reply.unauthorized(); + return; + } else { + regions = (await dataLayer.getRegionsManagedBy(userAppId)); + } + let region: Region | undefined = regions.find((r => r.name == request.params.regionId)) + response.region = !!region ? region : null; + return JSON.stringify(response); + } catch (e) { + reply.badRequest(`Error during request: ${JSON.stringify(e)}`); + return; + } + } + ); + + app.post( + '/regions', + {schema: createRegionReqSchema}, + async(request, reply) => { + let {admin} = await verifyJwt(request); + if(!admin) { + reply.unauthorized("Must be admin to create a region"); + return; + } else { + let response = { + status: "ok", + regionId: "" + }; + response.regionId = (await dataLayer.setRegion(request.body)).id; + reply.code(201); + return JSON.stringify(response); + } + } + ); + + app.post( + '/regions/:regionId', + {schema: updateRegionReqSchema}, + async(request, reply) => { + let {userAppId, admin} = await verifyJwt(request); + let UpdatedRegion: Region = {...request.body}; + if(!admin && !!UpdatedRegion.manager && !(await isRegionManager(UpdatedRegion.manager, request.params.regionId, dataLayer))) { + reply.unauthorized("Only admin users can update the region manager"); + return; + } else if(!(admin || await isRegionManager(userAppId, request.params.regionId, dataLayer))) { + reply.unauthorized("Only region managers and administrators can update region data") + return; + } else { + let response = { + status: "ok", + region: UpdatedRegion + }; + await dataLayer.setRegion(UpdatedRegion); + return JSON.stringify(response); + } + } + ); + + app.delete( + '/regions/:regionId', + {schema: deleteRegionReqSchema}, + async (request, reply) => { + let {userId, admin} = <{userId:string, admin: boolean}>await request.jwtVerify(); + if(admin || isRegionManager(userId, request.params.regionId, dataLayer)) { + await dataLayer.deleteRegion(request.params.regionId); + reply.code(204); + } else { + reply.code(401); + } + } + ); + return app; +} diff --git a/src/endpoints/users.ts b/src/endpoints/users.ts new file mode 100644 index 0000000..1cd18c9 --- /dev/null +++ b/src/endpoints/users.ts @@ -0,0 +1,81 @@ +import {FastifyInstance} from "fastify"; +import {Auth0JwtVerifier, Auth0UserInfo, getAllUsers, getUserById, updateUser, UserInfoPatch} from "../auth0"; +import {AuthenticatedRequest, AuthenticatedRequestById} from "./endpointUtils"; +import {getAllUsersRequestSchema, getUserInfoRequestSchema, updateUserRequestSchema} from "./docs/userSchema"; + +interface GetPaginatedUsersRequest extends AuthenticatedRequest { + Querystring: { + per_page?: number, + page?: number + } +} + +interface UpdateUserRequest extends AuthenticatedRequestById { + Body: UserInfoPatch +} + +export default function createUsersEndpoint(app: FastifyInstance, verifyJwt: Auth0JwtVerifier) { + app.get( + '/users/:id', + {schema: getUserInfoRequestSchema}, + async (request, reply) => { + let {userAppId, admin, role} = await verifyJwt(request); + if (!userAppId) { + reply.unauthorized("Only authenticated users can access this information"); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + userInfo: {} + }; + if (admin || role == "region") { + response.userInfo = await getUserById(userAppId); + } + return JSON.stringify(response); + } + } + ); + + app.get( + '/users', + {schema: getAllUsersRequestSchema}, + async (request, reply) => { + let {userAppId} = await verifyJwt(request); + if (!userAppId) { + reply.unauthorized("Only authenticated users can access this information"); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + users: [] + }; + response.users = await getAllUsers(request.query.per_page, request.query.page); + return JSON.stringify(response); + } + } + ); + + app.post( + '/users/:id', + {schema: updateUserRequestSchema}, + async (request, reply) => { + let {admin} = await verifyJwt(request); + if (!admin) { + reply.unauthorized("Only system administrators can update user information"); + return; + } else { + let response = { + status: "ok", + date: Date.now(), + user: {} + }; + response.user = await updateUser(request.params.id, request.body); + return JSON.stringify(response); + } + } + ); + + return app; +} diff --git a/src/index.ts b/src/index.ts index d0f7a66..803dbe2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,41 @@ -import fastify from 'fastify'; +import fastify, {FastifyInstance} from 'fastify'; +import fastifySensible from "fastify-sensible"; +import createPingEndpoint from './endpoints/ping'; +import { addRoutes } from './utils'; +import { createBusinessesEndpoint} from "./endpoints/businesses"; +import createRegionsEndpoint from "./endpoints/regions"; +import {ProductionDataLayer} from "./database/productionDataLayer"; +import {createFiltersEndpoint} from "./endpoints/filters"; +import {registerCorsHandler} from "./cors"; +import {registerSwagger} from "./swagger"; +import {verifyJwt} from "./auth0"; +import {createEditEndpoint} from "./endpoints/editRequest"; +import {productionFirestore} from "./database/firestore"; +import createUsersEndpoint from "./endpoints/users"; +let productionDataLayer = new ProductionDataLayer(productionFirestore) const port = Number(process.env.PORT || 8080); -const server = fastify(); - -server.get('/ping', async () => `${JSON.stringify({ status: 'ok', date: Date.now() })}\n`); +const server = fastify({logger: true}); +server.register(fastifySensible); +registerSwagger(server); +registerCorsHandler(server); +addRoutes( + server, + createPingEndpoint, + (app: FastifyInstance) => createFiltersEndpoint(app, productionDataLayer, verifyJwt), + (app: FastifyInstance) => createRegionsEndpoint(app, productionDataLayer, verifyJwt), + (app: FastifyInstance) => createBusinessesEndpoint(app, productionDataLayer, verifyJwt), + (app: FastifyInstance) => createEditEndpoint(app, productionDataLayer, verifyJwt), + (app: FastifyInstance) => createUsersEndpoint(app, verifyJwt) +); server.listen(port, '::', (err, address) => { if (err) { console.error(err); process.exitCode = 1; } else { + console.log("listening") + server.swagger(); console.log(`Server listening at ${address}`); } }); diff --git a/src/swagger.ts b/src/swagger.ts new file mode 100644 index 0000000..3a8c0e7 --- /dev/null +++ b/src/swagger.ts @@ -0,0 +1,21 @@ +import {FastifyInstance} from "fastify"; +import fastifySwagger from "fastify-swagger"; + +const swaggerSchema = { + routePrefix: '/documentation', + openapi: { + info: { + title: 'RAnLabAPI Swagger Schema', + description: 'Swagger Schema for the RAnLab API ', + version: '0.1.0' + }, + servers: [ + {url: "http://localhost:8080", description: "Local dev"}, + {url: "https://ranlab-api-mvp-xxvyt3l5wa-nn.a.run.app/", description: "Cloud dev"} + ] + }, + exposeRoute: true +}; +export function registerSwagger(app: FastifyInstance) { + app.register(fastifySwagger, swaggerSchema); +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..a584f6e --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,21 @@ +import type { FastifyInstance } from 'fastify'; +import {DataLayer} from "./database/productionDataLayer"; + +type EndpointFunction = (s: FastifyInstance) => FastifyInstance; + +/** + * Returns a Fastify instance composed of all the given endpoints + * + * @param server - the Fastify instance to build on + * @param endpoints - a list of endpoint creators + * @returns the built Fastify instance + */ +export function addRoutes(server: FastifyInstance, ...endpoints: EndpointFunction[]) { + return endpoints.reduce((prev, curr) => curr(prev), server); +} + +export async function isRegionManager(userId: string, regionId: string, dataLayer: DataLayer) { + const regions = (await dataLayer.getRegionsManagedBy(userId)); + return regions.find((r) => r.name === regionId); +} + diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..70b2f5d --- /dev/null +++ b/test.sh @@ -0,0 +1,3 @@ +curl -X POST -H "Content-Type: application/json" \ + --data "{\"name\":\"DummyBiz\",\"year_added\":2020,\"region\":\"DummyRegion\",\"employees\":2}" \ + https://ranlab-api-mvp-xxvyt3l5wa-nn.a.run.app/businesses diff --git a/tests/auth0.test.ts b/tests/auth0.test.ts new file mode 100644 index 0000000..cfbbfd3 --- /dev/null +++ b/tests/auth0.test.ts @@ -0,0 +1,116 @@ +import fastify, {FastifyInstance} from "fastify"; +import {addRoutes} from "../src/utils"; +import createRegionsEndpoint from "../src/endpoints/regions"; +import {DummyDatalayer} from "./testUtils/testDataLayer"; +import {createBusinessesEndpoint} from "../src/endpoints/businesses"; +import fastifySensible from "fastify-sensible"; +import {authenticateToTestDomain, setupAuth0TestEnv} from "./testUtils/testify"; +import {DummyRegion} from "./testUtils/dummyData"; +import {DataLayer} from "../src/database/productionDataLayer"; +import {getUserInfo, verifyJwt} from "../src/auth0"; + +describe("Auth0 integration tests", () => { + let sut : FastifyInstance; + let userAccessToken : string; + let adminAccessToken: string; + let userAppId: string; + + beforeAll(async (done) => { + setupAuth0TestEnv(); + let authTokens = await authenticateToTestDomain(); + userAccessToken = authTokens.userAccessToken; + adminAccessToken = authTokens.adminAccessToken; + let userInfo = await getUserInfo(`Bearer ${userAccessToken}`); + userAppId = userInfo.userId.split("|")[1]; + done(); + }); + + beforeEach(() => { + sut = fastify(); + + // fastifySensible gives us the decorator function needed when rejecting unauthorized reqs + sut.register(fastifySensible); + }); + + afterEach(async (done) => { + await sut.close(); + done(); + }); + + describe("Region Tests", () => { + beforeEach(() => { + addRoutes(sut, + () => createRegionsEndpoint(sut, new DummyDatalayer(), verifyJwt), + ); + }); + + it("Disallows an unauthenticated request", async(done) => { + let response = await sut.inject({ + method: "GET", + url: "/regions/DummyRegion", + }); + expect(response.statusCode).toBe(401); + done(); + }); + + it("Allows an authenticated request for regions by manager", async(done) => { + let response = await sut.inject({ + method: "GET", + url: "/regions/manager/DummyManager", + headers: {authorization: `Bearer ${userAccessToken}` } + }); + expect(response.statusCode).toBe(200); + done(); + }); + + it("Allows an authenticated request for a region by ID", async(done) => { + let response = await sut.inject({ + method: "GET", + url: "/regions/DummyRegion", + headers: {authorization: `Bearer ${userAccessToken}` } + }); + expect(response.statusCode).toBe(200); + done(); + }); + }); + + describe("Business Tests", () => { + let testDataLayer: DataLayer; + beforeEach(() => { + testDataLayer = new DummyDatalayer(); + addRoutes(sut, + () => createBusinessesEndpoint(sut, testDataLayer, verifyJwt), + () => createRegionsEndpoint(sut, testDataLayer, verifyJwt) + ); + }); + + it("Disallows an unauthenticated request", async(done) => { + let response = await sut.inject({ + method: "GET", + url: "/regions/DummyRegion/businesses", + }); + expect(response.statusCode).toBe(401); + done(); + }); + + it("Allows an authenticated request for businesses", async (done) => { + let authRegion = {...DummyRegion}; + authRegion.manager = userAppId + let regionsResponse = await sut.inject({ + method: 'POST', + url: `/regions`, + payload: authRegion, + headers:{authorization: `Bearer ${adminAccessToken}`} + }); + expect(regionsResponse.statusCode).toBe(201); + + let response = await sut.inject({ + method: "GET", + url: `/regions/${DummyRegion.name}/businesses`, + headers: {authorization: `Bearer ${userAccessToken}`} + }); + expect(response.statusCode).toBe(200); + done(); + }); + }); +}); diff --git a/tests/businesses.test.ts b/tests/businesses.test.ts new file mode 100644 index 0000000..fb2735f --- /dev/null +++ b/tests/businesses.test.ts @@ -0,0 +1,96 @@ +import {DummyDatalayer} from "./testUtils/testDataLayer"; +import { CHUNK_SIZE, convertToCSV, createBusinessesEndpoint, CSV_HEADER} from "../src/endpoints/businesses"; +import { + createDummyBusiness, + createDummyRegion, + dummyAdminToken, + DummyBiz, DummyRegion, dummyRegionManagerToken, dummyTokenVerifier, + getDummyBusinesses +} from "./testUtils/dummyData"; +import {getTestJwtVerifier, setupAuth0TestEnv, testify} from "./testUtils/testify"; +import createRegionsEndpoint from "../src/endpoints/regions"; + +describe("Business Endpoint Tests", () => { + let testDataLayer: DummyDatalayer; + beforeAll(() => { + setupAuth0TestEnv(); + }); + + beforeEach(async (done) => { + testDataLayer = new DummyDatalayer(); + const server = testify(); + const regionsApp = createRegionsEndpoint(server, testDataLayer, getTestJwtVerifier("admin", true)); + await createDummyRegion(regionsApp); + done(); + }); + + it('Can create and retrieve a valid business', async(done) => { + const bizApp = createBusinessesEndpoint(testify(), testDataLayer, getTestJwtVerifier(DummyRegion.manager, false)) + const createResponse = await createDummyBusiness(bizApp); + expect(createResponse.statusCode).toBe(200); + + const getResponse = await getDummyBusinesses(bizApp); + expect(getResponse.statusCode).toBe(200); + + let {businesses, filters} = JSON.parse(getResponse.payload); + expect(businesses).toEqual(expect.arrayContaining([expect.objectContaining({name: DummyBiz.name})])); + expect(filters).toEqual(expect.objectContaining({years: [DummyBiz.year_added], industries: [DummyBiz.industry]})) + await bizApp.close(); + done(); + }); + + it('Can update and retrieve a business', async(done) => { + const bizApp = createBusinessesEndpoint(testify(), testDataLayer, getTestJwtVerifier("admin", true)); + + const createResponse = await createDummyBusiness(bizApp); + const bizId = JSON.parse(createResponse.payload).businessId; + const updatedBiz = {...DummyBiz, id: bizId, year_added: 2020, employees: 2}; + const updateResponse = await bizApp.inject({ + method: 'POST', + url: `/businesses/${bizId}`, + payload: updatedBiz, + headers:{authorization: `Bearer ${dummyAdminToken}`} + }); + expect(updateResponse.statusCode).toBe(200); + expect(JSON.parse(updateResponse.payload).business).toEqual(updatedBiz); + + await bizApp.close(); + done(); + }); + + it("Can export all businesses if and only if admin", async(done) => { + const bizApp = createBusinessesEndpoint(testify(), testDataLayer, dummyTokenVerifier); + async function exportBusinesses(token: string) { + return await bizApp.inject({ + method: 'GET', + url: `/businesses/export`, + headers:{authorization: `Bearer ${token}`}, + simulate: { + end: true, + split: true, + error: true, + close: true + } + }); + } + let businesses = []; + for (let i=0; i <= CHUNK_SIZE; i++) { + let iBiz = {...DummyBiz, name: `biz_${i}`} + let {id} = await testDataLayer.setBusiness(iBiz); + businesses.push({...iBiz, id}); + } + let expectedCSV = CSV_HEADER + convertToCSV(businesses); + + let managerResponse = await exportBusinesses(dummyRegionManagerToken); + expect(managerResponse.statusCode).toBe(401); + + let adminResponse = await exportBusinesses(dummyAdminToken); + expect(adminResponse.statusCode).toBe(200); + expect(adminResponse.headers['transfer-encoding']).toBe("chunked"); + expect(adminResponse.payload).toBe(expectedCSV); + + await bizApp.close(); + done(); + }); + +}); diff --git a/tests/cors.test.ts b/tests/cors.test.ts new file mode 100644 index 0000000..4ba56be --- /dev/null +++ b/tests/cors.test.ts @@ -0,0 +1,33 @@ +import {registerCorsHandler} from "../src/cors"; +import { testify} from "./testUtils/testify"; +import {DummyRegion} from "./testUtils/dummyData"; + + +describe('CORS Handler Tests', function () { + async function testCorsRequest(origin: string, originResponse: string | boolean) { + let testApp = testify(); + registerCorsHandler(testApp); + let response = await testApp.inject({ + method: 'OPTIONS', + url: `/regions/${DummyRegion.name}/businesses`, + headers: { + 'authorization': 'Bearer abc123', + 'access-control-request-headers': 'x-requested-with', + 'access-control-request-method': 'GET', + origin: origin + } + }); + + expect(response.headers["access-control-allow-origin"]).toBe(originResponse); + await testApp.close(); + } + + it("Accepts localhost requests", async (done) => { + await testCorsRequest("http://localhost:3000", "http://localhost:3000"); + done(); + }); + it("Does not accept requests from unapproved origin", async (done) => { + await testCorsRequest('http://unapproved.com', false); + done(); + }); +}); diff --git a/tests/dataLayer.test.ts b/tests/dataLayer.test.ts new file mode 100644 index 0000000..08442c5 --- /dev/null +++ b/tests/dataLayer.test.ts @@ -0,0 +1,358 @@ +import {ProductionDataLayer, Region} from "../src/database/productionDataLayer"; +import {testFirestore} from "./testUtils/testFirestore"; +import {Business, CHUNK_SIZE} from "../src/endpoints/businesses"; +import objectContaining = jasmine.objectContaining; +import {EditRequest, DEFAULT_PAGE_SIZE} from "../src/endpoints/editRequest"; +import arrayContaining = jasmine.arrayContaining; +import {DummyBiz, DummyBizUpdate} from "./testUtils/dummyData"; +import any = jasmine.any; + +let productionDataLayer = new ProductionDataLayer(testFirestore); +function toBeLaterThan(expectedEarlierDate: Date | string | undefined, expectedLaterDate: Date) { + if(!expectedEarlierDate) { + return { + pass: false, + message: `Expected ${expectedEarlierDate} to be a date` + } + } else { + return new Date(expectedEarlierDate) < expectedLaterDate + ? { + pass: true, + message: () => `Expected ${expectedLaterDate} to be after ${expectedEarlierDate}` + } + : { + pass: false, + message: () => `Expected ${expectedLaterDate} to be after ${expectedEarlierDate}` + }; + } +} + +describe("Production Data Layer Integration Tests", () => { + const DUMMY_REGION_1 = "DummyRegion"; + const DUMMY_REGION_2 = "DummyRegion2"; + let regionId : string; + + beforeEach(async(done) => { + jest.setTimeout(30000); + (await testFirestore.collection("businesses").get()).docs.forEach((biz) => biz.ref.delete()); + (await testFirestore.collection("years").get()).docs.forEach(yr => yr.ref.delete()); + (await testFirestore.collection("editRequests").get()).docs.forEach(req => req.ref.delete()); + (await testFirestore.collection("regions").get()).docs.forEach(req => req.ref.delete()); + + done(); + }); + + it("Creates, retrieves, updates, and deletes businesses while maintaining correct region filter data", async (done) => { + jest.setTimeout(10000); + let biz : Business = { + employees: 1, + name: "DummyBiz", + regionId: "", + industry: "DummyIndustry", + year_added: 2019 + }; + biz.regionId = (await productionDataLayer.setRegion({name: DUMMY_REGION_1, manager: "Dummy"})).id; + + let id = (await productionDataLayer.setBusiness(biz)).id; + expect(id).toBeTruthy(); + + let byIdData = await productionDataLayer.getBusinessById(id); + expect(byIdData).toEqual(expect.objectContaining({...biz})); + + let byRegionData = await productionDataLayer.getBusinessesByRegion(biz.regionId); + expect(byRegionData).toEqual(expect.arrayContaining([expect.objectContaining(biz)])); + + let filters = await productionDataLayer.getFilters(biz.regionId); + expect(filters).toEqual(expect.objectContaining({years: [{year: biz.year_added, count: 1}], industries: [{industry: biz.industry, count: 1}]})) + + biz.id = id; + biz.employees = 2; + biz.year_added = 2020; + let updatedId = (await productionDataLayer.setBusiness(biz)).id; + expect(updatedId).toEqual(id); + + let updatedBizData = await productionDataLayer.getBusinessesByRegion(biz.regionId); + expect(updatedBizData).toEqual(expect.arrayContaining([expect.objectContaining(biz)])); + + let updatedFilters = await productionDataLayer.getFilters(biz.regionId); + expect(updatedFilters).toEqual(expect.objectContaining({years: [{year: biz.year_added, count: 1}], industries: [{industry: biz.industry, count: 1}]})); + + await productionDataLayer.deleteBusiness(biz.id); + let emptyBizData = await productionDataLayer.getBusinessesByRegion(biz.regionId); + expect(emptyBizData).toEqual([]); + + let emptyFilters = await productionDataLayer.getFilters(biz.regionId); + expect(emptyFilters.years).toEqual([]); + + done(); + }); + + it("Creates, retrieves, updates, and deletes regions", async (done) => { + jest.setTimeout(10000); + let region: Region = { + name: DUMMY_REGION_1, + manager: "Dummy Manager" + }; + + let {id} = await productionDataLayer.setRegion(region); + expect(id).toBeTruthy(); + expect(id).not.toBe(region.name); + + region.id = id; + let out = await productionDataLayer.getRegionsManagedBy("Dummy Manager"); + expect(out).toEqual(expect.arrayContaining([objectContaining({...region})])); + + region.manager = "New Manager"; + let{id: updatedId} = (await productionDataLayer.setRegion(region)); + expect(updatedId).toEqual(region.id); + + let oldManagerRegions = await productionDataLayer.getRegionsManagedBy("Dummy Manager"); + expect(oldManagerRegions).toEqual(expect.arrayContaining([])); + + let newManagerRegions = await productionDataLayer.getRegionsManagedBy(region.manager); + expect(newManagerRegions).toEqual(expect.arrayContaining([region])); + + let region2: Region = { + name: DUMMY_REGION_2, + manager: "Manager2" + }; + region2.id = (await productionDataLayer.setRegion(region2)).id; + expect(region2.id).not.toBe(region.id); + + let adminRegions = await productionDataLayer.getAllRegions(); + expect(adminRegions).toEqual(expect.arrayContaining([region, region2])); + + await productionDataLayer.deleteRegion(region.id); + let empty = await productionDataLayer.getRegionsManagedBy(region.manager); + expect(empty).toEqual([]); + + done(); + }); + + it("Creates, retrieves, and updates edit requests", async(done) => { + jest.setTimeout(10000); + let region: Region = { + name: DUMMY_REGION_1, + manager: "Dummy Manager" + }; + regionId = (await productionDataLayer.setRegion(region)).id; + + let testRequest : EditRequest = { + regionId: regionId, + submitter: "", + status: "Pending", + adds:[DummyBiz], + updates: [DummyBizUpdate], + deletes: [], + }; + + let spoilerRequest : EditRequest = { + regionId: `Not${regionId}`, + submitter: "", + status: "Pending", + adds:[DummyBiz], + updates: [DummyBizUpdate], + deletes: [], + }; + + let {id} = await productionDataLayer.createEditRequest(testRequest); + expect(id).toBeTruthy(); + testRequest.id = id; + + let {id:spoilerId} = await productionDataLayer.createEditRequest(spoilerRequest); + expect(spoilerId).toBeTruthy(); + spoilerRequest.id = spoilerId; + + let matchForTestRequest : any = {...testRequest, dateSubmitted: any(Date), dateUpdated: any(Date)}; + let editRequests = await productionDataLayer.getEditRequestsForRegion(regionId, DEFAULT_PAGE_SIZE); + expect(editRequests).toBeTruthy(); + expect(editRequests.length).toBe(1); + expect(editRequests).toStrictEqual(arrayContaining([matchForTestRequest])); + matchForTestRequest = editRequests[0]; + + let singleRequest = await productionDataLayer.getEditRequestById(id); + expect(singleRequest).toBeTruthy(); + if(!!singleRequest) { + expect(singleRequest).toStrictEqual(matchForTestRequest); + expect(singleRequest.regionId).toBe(regionId); + } + + let matchForSpoilerRequest = {...spoilerRequest, dateSubmitted: any(Date), dateUpdated: any(Date)} + editRequests = await productionDataLayer.getAllEditRequests(DEFAULT_PAGE_SIZE); + expect(editRequests).toBeTruthy(); + expect(editRequests.length).toBe(2); + expect(editRequests).toStrictEqual(arrayContaining([matchForTestRequest, matchForSpoilerRequest])) + + editRequests = await productionDataLayer.getEditRequestsByStatus("Pending", DEFAULT_PAGE_SIZE); + expect(editRequests).toBeTruthy(); + expect(editRequests.length).toBe(2); + expect(editRequests).toStrictEqual(arrayContaining([matchForTestRequest, matchForSpoilerRequest])) + + let updateRequest = { + id: testRequest.id, + status: "Reviewed", + regionId: testRequest.regionId, + submitter: testRequest.submitter + }; + + let requestAfterUpdate = await productionDataLayer.updateEditRequest(updateRequest); + + expect(requestAfterUpdate).toStrictEqual( + objectContaining({ + ...matchForTestRequest, + status: "Reviewed", + dateUpdated: any(Date) + }) + ); + expect(toBeLaterThan(requestAfterUpdate.dateUpdated, matchForTestRequest.dateUpdated)); + + let readRequestAfterUpdate = await productionDataLayer.getEditRequestById(testRequest.id); + expect(readRequestAfterUpdate).toBeTruthy(); + expect(readRequestAfterUpdate).toStrictEqual( + objectContaining({ + ...testRequest, + status: "Reviewed", + dateUpdated: any(Date) + }) + ); + expect(toBeLaterThan(readRequestAfterUpdate?.dateUpdated, matchForTestRequest.dateUpdated)); + + editRequests = await productionDataLayer.getEditRequestsByStatus("Reviewed", DEFAULT_PAGE_SIZE); + expect(editRequests).toBeTruthy(); + expect(editRequests.length).toBe(1); + expect(editRequests).toStrictEqual(arrayContaining([objectContaining({...testRequest, ...updateRequest, status: "Reviewed"})])) + + done(); + }); + + it("Paginates edit requests", async(done) => { + jest.setTimeout(30000); + let region: Region = { + name: DUMMY_REGION_1, + manager: "Dummy Manager" + }; + regionId = (await productionDataLayer.setRegion(region)).id; + + let testRequest: EditRequest = { + regionId: regionId, + submitter: "first", + status: "Pending", + adds: [DummyBiz], + updates: [DummyBizUpdate], + deletes: [], + }; + + let {id: id2} = await productionDataLayer.createEditRequest({...testRequest, submitter: "second"}); + expect(id2).toBeTruthy(); + let secondPageEdit = {...testRequest, id: id2, submitter: "second"}; + + let firstPageEdits = []; + for(let i = 0; i < DEFAULT_PAGE_SIZE; i++) { + let {id} = await productionDataLayer.createEditRequest(testRequest); + expect(id).toBeTruthy(); + testRequest.id = id; + firstPageEdits.push({...testRequest, dateSubmitted: any(Date), dateUpdated: any(Date)}); + } + + let expectedFirstPageEdits = firstPageEdits.reverse(); + + let firstPageRecords = await productionDataLayer.getAllEditRequests(DEFAULT_PAGE_SIZE); + expect(firstPageRecords).toStrictEqual(expectedFirstPageEdits); + + let firstPagePending = await productionDataLayer.getEditRequestsByStatus("Pending", DEFAULT_PAGE_SIZE); + expect(firstPagePending).toStrictEqual(expectedFirstPageEdits); + + let firstPageByRegion = await productionDataLayer.getEditRequestsForRegion(testRequest.regionId, DEFAULT_PAGE_SIZE); + expect(firstPageByRegion).toStrictEqual(expectedFirstPageEdits); + + let lastIdOnFirstPage = firstPageRecords[firstPageRecords.length-1].id; + let matchForSecondPage = {...secondPageEdit, dateSubmitted: any(Date), dateUpdated: any(Date)}; + let secondPageRecords = await productionDataLayer.getAllEditRequests(DEFAULT_PAGE_SIZE, lastIdOnFirstPage); + expect(secondPageRecords).toStrictEqual([matchForSecondPage]); + + let secondPagePending = await productionDataLayer.getEditRequestsByStatus("Pending", DEFAULT_PAGE_SIZE, lastIdOnFirstPage); + expect(secondPagePending).toStrictEqual([matchForSecondPage]); + + let secondPageForRegion = await productionDataLayer.getEditRequestsForRegion(testRequest.regionId, DEFAULT_PAGE_SIZE, lastIdOnFirstPage); + expect(secondPageForRegion).toStrictEqual([matchForSecondPage]); + + await productionDataLayer.updateEditRequest({id: id2, regionId: testRequest.regionId, submitter: "first"}); + let firstPageForUser = await productionDataLayer.getEditRequestsByUser("first", DEFAULT_PAGE_SIZE); + expect(firstPageForUser).toStrictEqual(expectedFirstPageEdits); + + let secondPageForUser = await productionDataLayer.getEditRequestsByUser("first", DEFAULT_PAGE_SIZE, lastIdOnFirstPage); + expect(secondPageForUser).toStrictEqual([{...matchForSecondPage, submitter: "first"}]); + + done(); + }); + + describe("Filter data layer tests", () => { + beforeEach(async (done) => { + let industryRecords = (await productionDataLayer.firestore.collection("industries").get()).docs; + for(let i = 0; i < industryRecords.length; i++) { + await industryRecords[i].ref.delete(); + } + done(); + }); + + it("Creates and deletes industries", async(done) => { + const TEST_INDUSTRY = 'testIndustry'; + await productionDataLayer.addIndustries([TEST_INDUSTRY]); + let {industries: industriesAfterCreate} = await productionDataLayer.getFilters(); + expect(industriesAfterCreate).toStrictEqual( + expect.arrayContaining([TEST_INDUSTRY]) + ); + + await productionDataLayer.deleteIndustries([TEST_INDUSTRY]); + let {industries: industriesAfterDelete} = await productionDataLayer.getFilters(); + expect(industriesAfterDelete).toStrictEqual([]); + + done(); + }) + + }) + + describe("Long running test", ()=> { + afterEach(async(done) => { + (await testFirestore.collection("businesses").get()).docs.forEach((biz) => biz.ref.delete()); + done(); + }); + it("Retrieves businesses in chunks", async(done) => { + async function addBiz(i: number) { + let iBiz = {...DummyBiz, regionId, name: `biz_${i}`}; + let doc = testFirestore.collection("businesses").doc(); + await doc.set(iBiz); + expect(doc.id).toBeTruthy(); + return {...iBiz, id: doc.id}; + } + jest.setTimeout(120000); + + let region: Region = { + name: DUMMY_REGION_1, + manager: "Dummy Manager" + }; + regionId = (await productionDataLayer.setRegion(region)).id; + + let firstChunkPromises: Promise[] = []; + let firstChunkBusinesses = []; + + for(let i = 0; i < CHUNK_SIZE; i++) { + firstChunkPromises.push(addBiz(i)); + firstChunkBusinesses.push(...(await Promise.all(firstChunkPromises))); + firstChunkPromises = []; + } + + let secondChunkBiz = {...DummyBiz, regionId, name: `biz_999`}; + let{id: id2} = await productionDataLayer.setBusiness(secondChunkBiz); + expect(id2).toBeTruthy(); + + let firstChunk = await productionDataLayer.getAllBusinesses(); + expect(firstChunk).toStrictEqual(arrayContaining(firstChunkBusinesses)); + + let secondChunk = await productionDataLayer.getAllBusinesses(firstChunk[CHUNK_SIZE -1].id); + expect(secondChunk).toStrictEqual([{...secondChunkBiz, id: id2}]) + + done(); + }); + }); +}); diff --git a/tests/editRequests.test.ts b/tests/editRequests.test.ts new file mode 100644 index 0000000..aff3646 --- /dev/null +++ b/tests/editRequests.test.ts @@ -0,0 +1,395 @@ +import {DummyDatalayer} from "./testUtils/testDataLayer"; +import { testify} from "./testUtils/testify"; +import {createEditEndpoint, EditRequest, DEFAULT_PAGE_SIZE, previewAddId} from "../src/endpoints/editRequest"; +import { + dummyAdminId, + dummyAdminToken, + DummyBiz, + DummyRegion, + dummyRegionManagerToken, + dummyTokenVerifier +} from "./testUtils/dummyData"; +import arrayContaining = jasmine.arrayContaining; +import {FastifyInstance} from "fastify"; +import objectContaining = jasmine.objectContaining; +import any = jasmine.any; +import {Business, createBusinessesEndpoint} from "../src/endpoints/businesses"; +import {Region} from "../src/database/productionDataLayer"; +import createRegionsEndpoint from "../src/endpoints/regions"; + +const DummyAdd : EditRequest = { + regionId: DummyRegion.name, + submitter: DummyRegion.manager, + dateSubmitted: new Date(), + dateUpdated: new Date(), + status: "", + adds: [DummyBiz] +} + +describe("Edit Request unit tests", () => { + + let testDataLayer: DummyDatalayer + let testApp : FastifyInstance; + let editEndpoint : FastifyInstance; + + beforeEach(() => { + testDataLayer = new DummyDatalayer(); + testApp = testify(); + editEndpoint = createEditEndpoint(testApp, testDataLayer, dummyTokenVerifier); + }); + + it("Cannot submit or view edit requests as an unauthenticated user", async (done) => { + const postResponse = await submitEditRequest(DummyAdd, DummyRegion.name, ""); + expect(postResponse.statusCode).toBe(401); + + const getResponse = await getEditRequestsByRegion(DummyRegion.name, ""); + expect(getResponse.statusCode).toBe(401); + + await testApp.close(); + done(); + }); + + it("Can view a single edit request by request id as region or system admin", async(done) => { + const postResponse = await submitEditRequest(DummyAdd, DummyRegion.name, dummyRegionManagerToken); + expect(postResponse.statusCode).toBe(201); + const responseData = JSON.parse(postResponse.payload); + expect(responseData).toStrictEqual(expect.objectContaining({ id: responseData.id})); + + const regionManagerResponse = await getRequestById(responseData.id, dummyRegionManagerToken); + expect(regionManagerResponse.statusCode).toBe(200); + const postedEdit = asResponse(asInitializedEditRequest(DummyAdd, responseData.id)); + expect(JSON.parse(regionManagerResponse.payload).editRequest).toStrictEqual(postedEdit); + + const adminResponse = await getRequestById(responseData.id, dummyAdminToken); + expect(adminResponse.statusCode).toBe(200); + expect(JSON.parse(adminResponse.payload).editRequest).toStrictEqual(postedEdit); + + await testApp.close(); + done(); + }); + + describe("Edit Requests by Region", () => { + it("Can submit and view edit requests by region as a region admin", async (done) => { + const postResponse = await submitEditRequest(DummyAdd, DummyRegion.name, dummyRegionManagerToken); + expect(postResponse.statusCode).toBe(201); + const responseData = JSON.parse(postResponse.payload); + expect(responseData).toStrictEqual(expect.objectContaining({ id: responseData.id})); + + const postedEdit = asResponse(asInitializedEditRequest(DummyAdd, responseData.id)); + const getResponse = await getEditRequestsByRegion(DummyRegion.name, dummyRegionManagerToken) + expect(getResponse.statusCode).toBe(200); + expect(JSON.parse(getResponse.payload).editRequests).toStrictEqual(arrayContaining([postedEdit])); + + await testApp.close(); + done(); + }); + + it("Can submit and view edit requests as a system admin", async (done) => { + const postResponse = await submitEditRequest(DummyAdd, DummyRegion.name, dummyAdminToken); + expect(postResponse.statusCode).toBe(201); + const responseData = JSON.parse(postResponse.payload); + expect(responseData).toStrictEqual(expect.objectContaining({ id: responseData.id})); + const postedEdit = asResponse(asInitializedEditRequest(DummyAdd,responseData.id)); + + const getResponse = await getEditRequestsByRegion(DummyRegion.name, dummyAdminToken); + expect(getResponse.statusCode).toBe(200); + expect(JSON.parse(getResponse.payload).editRequests).toStrictEqual(arrayContaining([postedEdit])); + + await testApp.close(); + done(); + }); + }); + + it("Can view all edit requests as a system admin but not as region manager", async(done) => { + + const postResponse1 = await submitEditRequest(DummyAdd, DummyRegion.name, dummyRegionManagerToken); + expect(postResponse1.statusCode).toBe(201); + let {id: id1} = JSON.parse(postResponse1.payload); + + const differentRegionAdd = {...DummyAdd, regionId: `Not${DummyRegion.name}`}; + const postResponse2 = await submitEditRequest(differentRegionAdd, differentRegionAdd.regionId, dummyRegionManagerToken); + expect(postResponse2.statusCode).toBe(201); + + const regionManagerResponse = await getAllEditRequests(dummyRegionManagerToken); + expect(regionManagerResponse.statusCode).toBe(401); + + const allResponse = await getAllEditRequests(dummyAdminToken); + expect(allResponse.statusCode).toBe(200); + + let postedRequest1 = asResponse(asInitializedEditRequest(DummyAdd, JSON.parse(postResponse1.payload).id)); + let postedRequest2 = asResponse(asInitializedEditRequest(differentRegionAdd, JSON.parse(postResponse2.payload).id)); + expect(JSON.parse(allResponse.payload).editRequests).toStrictEqual(arrayContaining([ + postedRequest1, + postedRequest2 + ])); + + const pendingResponse = await getAllEditRequests(dummyAdminToken, {status: "Pending"}); + expect(pendingResponse.statusCode).toBe(200); + expect(JSON.parse(pendingResponse.payload).editRequests).toStrictEqual(arrayContaining([ + postedRequest1, + postedRequest2 + ])); + + await updateEditRequestStatus(id1, "Reviewed", dummyAdminToken); + + const adminResponse = await getAllEditRequests(dummyAdminToken, {status: "Reviewed"} ); + expect(adminResponse.statusCode).toBe(200); + expect(JSON.parse(adminResponse.payload).editRequests).toStrictEqual( + arrayContaining([ + objectContaining({ + ...DummyAdd, + id: id1, + status: "Reviewed", + reviewer: dummyAdminId, + dateSubmitted: any(String), + dateUpdated: any(String)}) + ]) + ); + + await testApp.close(); + done(); + }); + + it("Can retrieve all edit requests with pagination", async(done) => { + + const page2Response = await submitEditRequest(DummyAdd, DummyRegion.name, dummyRegionManagerToken); + expect(page2Response.statusCode).toBe(201); + let {id: id2} = JSON.parse(page2Response.payload); + let secondPageRequest = asResponse(asInitializedEditRequest(DummyAdd, id2)); + + let firstPageObjects = []; + for(let i = 0; i < DEFAULT_PAGE_SIZE; i++) { + const page1Response = await submitEditRequest(DummyAdd, DummyRegion.name, dummyRegionManagerToken); + expect(page1Response.statusCode).toBe(201); + let {id} = JSON.parse(page1Response.payload); + firstPageObjects.push(asResponse(asInitializedEditRequest(DummyAdd, id))); + } + + const firstPageResponse = await getAllEditRequests(dummyAdminToken); + expect(firstPageResponse.statusCode).toBe(200); + let firstResponsePayload = JSON.parse(firstPageResponse.payload); + expect(firstResponsePayload.totalCount).toBe(DEFAULT_PAGE_SIZE + 1); + expect(firstResponsePayload.editRequests).toStrictEqual(arrayContaining(firstPageObjects)); + + let afterId = firstResponsePayload.editRequests[firstResponsePayload.editRequests.length-1].id; + afterId = !!afterId? afterId : ""; + const secondPageResponse = await getAllEditRequests(dummyAdminToken, {afterId}); + expect(secondPageResponse.statusCode).toBe(200); + let secondPagePayload = JSON.parse(secondPageResponse.payload); + expect(secondPagePayload.totalCount).toBe(DEFAULT_PAGE_SIZE + 1); + expect(secondPagePayload.editRequests).toStrictEqual([secondPageRequest]); + + await testApp.close(); + done(); + }) + + + it("Can only update edit request status as sysadmin", async(done) => { + const submitResponse = await submitEditRequest(DummyAdd, DummyRegion.name, dummyRegionManagerToken); + expect(submitResponse.statusCode).toBe(201); + let createdRequest = {...DummyAdd, id: JSON.parse(submitResponse.payload).id}; + + const unauthorizedResponse = await updateEditRequestStatus(createdRequest.id, "Reviewed", ""); + expect(unauthorizedResponse.statusCode).toBe(401); + + const regionManagerResponse = await updateEditRequestStatus(createdRequest.id, "Reviewed", dummyRegionManagerToken); + expect(regionManagerResponse.statusCode).toBe(401); + + const updateResponse = await updateEditRequestStatus(createdRequest.id, "Reviewed", dummyAdminToken); + expect(updateResponse.statusCode).toBe(200); + + const updatedRequest = asResponse({ + ...createdRequest, + status: "Reviewed", + reviewer: dummyAdminId + }); + expect(JSON.parse(updateResponse.payload).editRequest).toStrictEqual(objectContaining(updatedRequest)); + + const postUpdateRead = await getRequestById(createdRequest.id, dummyRegionManagerToken); + expect(postUpdateRead.statusCode).toBe(200); + expect(JSON.parse(postUpdateRead.payload).editRequest).toStrictEqual(objectContaining(updatedRequest)); + + await testApp.close(); + done(); + }); + + it("Implements Approval preview and workflow", async(done) => { + async function createRegion(regionApp: FastifyInstance, region: Region) { + let temp = await regionApp.inject({ + method: "POST", + url: `/regions/`, + payload: region, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + return temp; + } + + async function createBusiness(bizApp: FastifyInstance, biz: Business) { + let temp = await bizApp.inject({ + method: "POST", + url: `/regions/${biz.regionId}/businesses`, + payload: biz, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + return temp; + } + + const regionApp = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + const bizApp = createBusinessesEndpoint(testApp, testDataLayer, dummyTokenVerifier); + await createRegion(regionApp, DummyRegion); + let updatedBiz = {...DummyBiz, name: `UpdatingName`, employees: DummyBiz.employees + 10, industry: `OriginalIndustry}`}; + let deletedBiz = {...DummyBiz, name: "Deleting"}; + let {businessId: updatedBizId} = JSON.parse((await createBusiness(bizApp, updatedBiz)).payload); + let {businessId: deletedBizId} = JSON.parse((await createBusiness(bizApp, deletedBiz)).payload); + updatedBiz.id = updatedBizId; + deletedBiz.id = deletedBizId; + const request : EditRequest = { + ...DummyAdd, + updates: [{ + id: updatedBizId, + name: "UpdatedName", + industry: "UpdatedIndustry" + }], + deletes: [{ + id: deletedBizId, + ...deletedBiz + }] + }; + + let submitResponse = await submitEditRequest(request, DummyRegion.name, dummyRegionManagerToken); + expect(submitResponse.statusCode).toBe(201); + let {id: editRequestId} = JSON.parse(submitResponse.payload); + + let previewResponse = await getEditPreview(editRequestId, dummyAdminToken); + expect(previewResponse.statusCode).toBe(200); + expect(JSON.parse(previewResponse.payload)).toStrictEqual( + objectContaining({ + added: arrayContaining([objectContaining({...DummyBiz, id: previewAddId})]), + updated: arrayContaining([objectContaining({...updatedBiz, name: "UpdatedName", industry: "UpdatedIndustry"})]), + deleted: arrayContaining([objectContaining({...deletedBiz})]) + }) + ); + + let postPreviewResponse = await getBusinessesByRegion(bizApp, DummyBiz.regionId); + expect(JSON.parse(postPreviewResponse.payload).businesses).toStrictEqual(arrayContaining([ + updatedBiz, + deletedBiz + ])); + + let approvalResponse = await updateEditRequestStatus(editRequestId, "Approved", dummyAdminToken); + let approvalPayload = JSON.parse(approvalResponse.payload); + expect(approvalPayload).toStrictEqual( + objectContaining({ + added: any(Object), + updated: any(Object), + deleted: any(Object), + editRequest: objectContaining({ + status: "Approved", + submitter: DummyRegion.manager, + reviewer: dummyAdminId, + dateSubmitted: any(String), + dateUpdated: any(String) + }) + }) + ); + expect(approvalPayload.added).toStrictEqual(arrayContaining([objectContaining({...DummyBiz, id: any(String)})])); + expect(approvalPayload.updated).toStrictEqual(arrayContaining([objectContaining({...updatedBiz, name: "UpdatedName", industry: "UpdatedIndustry"})])); + expect(approvalPayload.deleted).toStrictEqual(arrayContaining([objectContaining({...deletedBiz})])); + + let postApprovalResponse = await getBusinessesByRegion(regionApp, DummyBiz.regionId); + let businesses = JSON.parse(postApprovalResponse.payload).businesses; + expect(businesses).toStrictEqual(arrayContaining([ + {...updatedBiz, name: "UpdatedName", industry: "UpdatedIndustry"}, + {...DummyBiz, id: any(String)} + ])); + expect(businesses.find((b:Business) => b.name === DummyBiz.name).id).not.toBe(previewAddId); + + await testApp.close(); + done(); + }); + + async function getBusinessesByRegion(bizEndpoint: FastifyInstance, regionId: string) { + return await bizEndpoint.inject({ + method: "GET", + url: `/regions/${regionId}/businesses`, + headers: { Authorization: `Bearer ${dummyRegionManagerToken}` } + }); + } + + async function getRequestById(requestId: string, token: string) : Promise { + return await editEndpoint.inject({ + method: "GET", + url: `/edits/${requestId}`, + headers: {authorization: `Bearer ${token}`} + }); + } + + async function submitEditRequest(request: EditRequest, regionId: string, token: string) { + let postOptions = { + method: <"POST">"POST", + url: `/region/${regionId}/edits`, + payload: request, + headers: {} + }; + if(!!token) { + postOptions.headers = {authorization: `Bearer ${token}`}; + } + return await editEndpoint.inject(postOptions); + } + + async function updateEditRequestStatus(id: string, newStatus: string, token: string) : Promise { + let temp = await testApp.inject({ + method: "POST", + url: `/edits/${id}`, + payload: {status: newStatus}, + headers: { + authorization: `Bearer ${token}` + } + }); + return temp; + } + + async function getEditRequestsByRegion(regionId: string, token: string) { + return await editEndpoint.inject({ + method: "GET", + url: `/region/${regionId}/edits`, + headers: {authorization: `Bearer ${token}`} + }); + } + + async function getEditPreview(id: string, token: string) { + return await editEndpoint.inject({ + method: "GET", + url: `/edits/${id}/preview`, + headers: {authorization: `Bearer ${token}`} + }); + } + + function getAllEditRequests(token: string, params?: {status?: string, afterId?: string}) { + let statusParam, pageParam; + if (!!params) { + statusParam = !!params.status ? `status=${params.status}` : ""; + pageParam = !!params.afterId ? `afterId=${params.afterId}` : ""; + } + let querystring = !!statusParam || !!pageParam ? `?${statusParam}&${pageParam}` : ""; + let url = `/edits/all${querystring}`; + return editEndpoint.inject({ + method: "GET", + url, + headers: {authorization: `Bearer ${token}`} + }); + } + + /** + * Simulates the standard data conversion when data is submitted in a request and then received in a response + * @param rawEdit - the edit data in its original pre-submission + * @return - the same data, but having been stringified and then parsed again. This has implicates for, for example, Date strings + */ + function asResponse(rawEdit: EditRequest) : EditRequest { + return JSON.parse(JSON.stringify(rawEdit)); + } + + function asInitializedEditRequest(rawEdit: EditRequest, id: string) : EditRequest { + return {...rawEdit, id, status: "Pending"}; + } +}); diff --git a/tests/filters.test.ts b/tests/filters.test.ts new file mode 100644 index 0000000..860b9b5 --- /dev/null +++ b/tests/filters.test.ts @@ -0,0 +1,146 @@ +import {createFiltersEndpoint} from "../src/endpoints/filters"; +import {Business, createBusinessesEndpoint} from "../src/endpoints/businesses"; +import {DummyDatalayer} from "./testUtils/testDataLayer"; +import { + createDummyBusiness, + createDummyRegion, dummyAdminToken, + DummyBiz, + DummyRegion, + dummyRegionManagerToken, + dummyTokenVerifier +} from "./testUtils/dummyData"; +import createRegionsEndpoint from "../src/endpoints/regions"; +import {setupAuth0TestEnv, testify} from "./testUtils/testify"; +import {FastifyInstance} from "fastify"; + +describe("Filter Endpoint Tests", () => { + let testDataLayer: DummyDatalayer; + const CREATED_INDUSTRY = 'Created Industry'; + let DEFAULT_INDUSTRIES = [DummyBiz.industry, `Not ${DummyBiz.industry}`, CREATED_INDUSTRY]; + + beforeAll(async (done) => { + setupAuth0TestEnv(); + testDataLayer = new DummyDatalayer(); + const server = testify(); + const regionsApp = createRegionsEndpoint(server, testDataLayer, dummyTokenVerifier); + const bizApp = createBusinessesEndpoint(server, testDataLayer, dummyTokenVerifier); + const filterApp = createFiltersEndpoint(server, testDataLayer, dummyTokenVerifier); + + await createDummyRegion(regionsApp); + let notDummyRegion = {...DummyRegion}; + notDummyRegion.name = `Not ${DummyRegion.name}`; + await regionsApp.inject({ + method: "POST", + url: "/regions", + payload: notDummyRegion, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + + await createDummyBusiness(bizApp); + await createDummyBusiness(bizApp, { + name: `Not ${DummyBiz.name}`, + employees: 1, + regionId: notDummyRegion.name, + industry: `Not ${DummyBiz.industry}`, + year_added: DummyBiz.year_added + 1 + }); + await createGlobalIndustry(filterApp, CREATED_INDUSTRY); + done(); + }); + + it('Region-specific filter data is correct', async (done) => { + const server = testify(); + const filterApp = createFiltersEndpoint(server, testDataLayer, dummyTokenVerifier); + const filterResponse = await filterApp.inject({ + method: 'GET', + url: `/regions/${DummyRegion.name}/filters`, + headers: { authorization: `Bearer ${dummyRegionManagerToken}`} + }); + + expect(filterResponse.statusCode).toBe(200); + expect(JSON.parse(filterResponse.payload).filters).toStrictEqual( + expect.objectContaining({ + years: [DummyBiz.year_added], + industries: [DummyBiz.industry] + }) + ); + + await filterApp.close(); + done(); + }); + + it("Global list contains all industries for admin and non-admin users", async (done) => { + const server = testify(); + const filterApp = createFiltersEndpoint(server, testDataLayer, dummyTokenVerifier); + const adminResponse = await filterApp.inject({ + method: 'GET', + url: `/filters/industries`, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + expect(adminResponse.statusCode).toBe(200); + expect(JSON.parse(adminResponse.payload).industries).toStrictEqual(DEFAULT_INDUSTRIES); + + const nonAdminResponse = await filterApp.inject({ + method: 'GET', + url: `/filters/industries`, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`} + }); + expect(nonAdminResponse.statusCode).toBe(200); + expect(JSON.parse(nonAdminResponse.payload).industries).toStrictEqual( + expect.arrayContaining([ + DummyBiz.industry, + `Not ${DummyBiz.industry}` + ]) + ); + await filterApp.close(); + done(); + }); + + it("Can add and remove industries from the global list", async (done) => { + const server = testify(); + const filterApp = createFiltersEndpoint(server, testDataLayer, dummyTokenVerifier); + const SECOND_INDUSTRY = "SECOND"; + let createResponse = await createGlobalIndustry(filterApp, SECOND_INDUSTRY); + + expect(createResponse.statusCode).toBe(200); + const getAfterCreate = await filterApp.inject({ + method: 'GET', + url: `/filters/industries`, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`} + }); + expect(getAfterCreate.statusCode).toBe(200); + expect(JSON.parse(getAfterCreate.payload).industries).toStrictEqual(expect.arrayContaining([CREATED_INDUSTRY, SECOND_INDUSTRY ])); + + const deleteResponse = await filterApp.inject({ + method: 'DELETE', + url: `/filters/industries`, + payload: {industries: [CREATED_INDUSTRY]}, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + + expect(deleteResponse.statusCode).toBe(200); + const getAfterDelete = await filterApp.inject({ + method: 'GET', + url: `/filters/industries`, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`} + }); + expect(getAfterDelete.statusCode).toBe(200); + expect(JSON.parse(getAfterDelete.payload).industries).toStrictEqual( + expect.not.arrayContaining([ + CREATED_INDUSTRY + ]) + ); + + await filterApp.close(); + done(); + }); + + async function createGlobalIndustry(filterApp: FastifyInstance, globalIndustry: string) { + return await filterApp.inject({ + method: 'POST', + url: `/filters/industries`, + payload: {industries: [globalIndustry]}, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + } +}); diff --git a/tests/ping.test.ts b/tests/ping.test.ts new file mode 100644 index 0000000..9bec365 --- /dev/null +++ b/tests/ping.test.ts @@ -0,0 +1,12 @@ +import fastify from 'fastify'; +import createPingEndpoint from '../src/endpoints/ping'; + +test('returns a status code of 200', async (done) => { + const app = createPingEndpoint(fastify()); + const response = await app.inject({ method: 'GET', url: '/ping' }); + + expect(response.statusCode).toBe(200); + + await app.close(); + done(); +}); diff --git a/tests/regions.test.ts b/tests/regions.test.ts new file mode 100644 index 0000000..d36baa8 --- /dev/null +++ b/tests/regions.test.ts @@ -0,0 +1,153 @@ +import createRegionsEndpoint from "../src/endpoints/regions"; +import { + testify, + setupAuth0TestEnv +} from "./testUtils/testify"; +import {DummyDatalayer} from "./testUtils/testDataLayer"; +import { + dummyAdminToken, + DummyRegion, + dummyRegionManagerToken, + getRegionsByDummyManager, + dummyTokenVerifier, dummyAdminId +} from "./testUtils/dummyData"; +import {Region} from "../src/database/productionDataLayer"; +import {FastifyInstance} from "fastify"; + +describe("Region Endpoint Tests", () => { + let testDataLayer: DummyDatalayer; + let testApp: FastifyInstance; + + beforeAll(() => { + setupAuth0TestEnv(); + }); + + beforeEach(async (done) => { + testDataLayer= new DummyDatalayer() + testApp = testify(); + await testDataLayer.setRegion(DummyRegion); + done(); + }); + + it('Can create and retrieve all regions as SysAdmin', async (done) => { + const app = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + let testRegions: Region[] = [{name: "region1", manager: "manager1"}, {name: "region2", manager: "manager2"}]; + testRegions.forEach((r) => testDataLayer.setRegion(r)); + for(let region of testRegions) { + const response = await app.inject({ + method: 'GET', + headers: {authorization: `Bearer ${dummyAdminToken}`}, + url: `/regions/manager/${region.manager}` + }); + expect(response.statusCode).toBe(200); + expect(JSON.parse(response.payload).regions).toEqual(expect.arrayContaining([...testRegions.filter(r => r.manager == region.manager)])); + } + const response = await app.inject({ + method: 'GET', + headers: {authorization: `Bearer ${dummyAdminToken}`}, + url: `/regions/manager/${dummyAdminId}` + }); + expect(response.statusCode).toBe(200); + expect(JSON.parse(response.payload).regions).toEqual(expect.arrayContaining([...testRegions])); + await app.close(); + done(); + }); + + it('Can create and retrieve a region as Region Manager', async (done) => { + const app = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + const response = await getRegionsByDummyManager(app); + + expect(response.statusCode).toBe(200); + expect(JSON.parse(response.payload).regions).toEqual(expect.arrayContaining([DummyRegion])); + await app.close(); + done(); + }); + + it('Can update and retrieve a region', async (done) => { + const app = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + const updatedRegion = { + name: "TestRegion", + manager: DummyRegion.manager + }; + const response = await app.inject( { + method: 'POST', + url: `/regions/${DummyRegion.name}`, + payload: updatedRegion, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`}, + }); + + expect(response.statusCode).toBe(200); + expect(JSON.parse(response.payload).region).toStrictEqual(updatedRegion); + await app.close(); + done(); + }); + + it('Can update region manager only as admin', async (done) => { + const app = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + const updatedRegion = { + name: "TestRegion", + manager: `Not ${DummyRegion.manager}` + }; + const nonAdminResponse = await app.inject( { + method: 'POST', + url: `/regions/${DummyRegion.name}`, + payload: updatedRegion, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`}, + }); + expect(nonAdminResponse.statusCode).toBe(401); + + const adminResponse = await app.inject( { + method: 'POST', + url: `/regions/${DummyRegion.name}`, + payload: updatedRegion, + headers: {authorization: `Bearer ${dummyAdminToken}`}, + }); + expect(adminResponse.statusCode).toBe(200); + expect(JSON.parse(adminResponse.payload).region).toStrictEqual(updatedRegion); + + await app.close(); + done(); + }); + + + it('Can delete a region', async (done) => { + const app = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + const deleteResponse = await app.inject( { + method: 'DELETE', + url: `/regions/${DummyRegion.name}`, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`} + }); + + expect(deleteResponse.statusCode).toBe(204); + + const getResponse = await getRegionsByDummyManager(app); + expect(JSON.parse(getResponse.payload).regions).toEqual([]); + + await app.close(); + done(); + }); + + it("Can retrieve a single region as either region admin or system admin", async(done) => { + const app = createRegionsEndpoint(testApp, testDataLayer, dummyTokenVerifier); + const getRegionAdminResponse = await app.inject({ + method: 'GET', + url: `/regions/${DummyRegion.name}`, + headers: {authorization: `Bearer ${dummyRegionManagerToken}`} + }); + + expect(getRegionAdminResponse.statusCode).toBe(200); + expect(JSON.parse(getRegionAdminResponse.payload).region).toStrictEqual(DummyRegion); + + const getSysAdminResponse = await app.inject({ + method: 'GET', + url: `/regions/${DummyRegion.name}`, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + + expect(getSysAdminResponse.statusCode).toBe(200); + expect(JSON.parse(getSysAdminResponse.payload).region).toStrictEqual(DummyRegion); + + await app.close(); + done(); + }); +}) diff --git a/tests/testUtils/dummyData.ts b/tests/testUtils/dummyData.ts new file mode 100644 index 0000000..2d09353 --- /dev/null +++ b/tests/testUtils/dummyData.ts @@ -0,0 +1,86 @@ +import {FastifyInstance, FastifyRequest} from "fastify"; +import {Business, BusinessUpdate} from "../../src/endpoints/businesses"; +import {Region} from "../../src/database/productionDataLayer"; +import {getMockToken} from "./testify"; + +const dummyManager = "DummyManagerId"; +export const dummyAdminId = "admin"; +export const dummyRegionManagerToken = getMockToken({userAppId: dummyManager, admin: false}) +export const dummyAdminToken = getMockToken({userAppId: dummyAdminId, admin: true}); + +export const DummyRegion: Region = { + name: "DummyRegion", + manager: dummyManager +}; + +export const DummyBiz: Business = { + name: "DummyBiz", + regionId: DummyRegion.name, + year_added: 2009, + employees: 1, + industry: "DummyIndustry" +}; + +export const DummyBizUpdate: BusinessUpdate = { + id: "DummyID", + name: "DummyBiz", + year_added: 2009, + employees: 1, + industry: "DummyIndustry" +}; + + +export async function dummyTokenVerifier (req: FastifyRequest) { + if(!req.headers.authorization || !req.headers.authorization.split("Bearer")[1].trim()) { + return {userAppId: "", admin: false, role: ""}; + } + if(req.headers.authorization.indexOf(dummyAdminToken) > 0) { + return {userAppId: dummyAdminId, admin: true, role: "admin"}; + } else if (req.headers.authorization?.indexOf(dummyRegionManagerToken) > 0) { + return {userAppId: DummyRegion.manager, admin: false, role: "region"}; + } else { + throw new Error("Unrecognized token"); + } +} + +export async function createDummyRegion(regionsApp: FastifyInstance, manager: string = dummyManager) { + let region = {...DummyRegion}; + region.manager = manager + try { + let response = await regionsApp.inject({ + method: "POST", + url: "/regions", + payload: region, + headers: {authorization: `Bearer ${dummyAdminToken}`} + }); + return response; + } catch (e) { + console.log(e); + return null; + } +} + +export async function createDummyBusiness(bizApp: FastifyInstance, biz : Business = DummyBiz, token: string = dummyRegionManagerToken) { + return await bizApp.inject({ + method: 'POST', + url: `/regions/${biz.regionId}/businesses`, + payload: biz, + headers: {authorization: `Bearer ${token}`} + }); +} + +export async function getDummyBusinesses(bizApp: FastifyInstance, token: string = dummyRegionManagerToken) { + return await bizApp.inject({ + method: 'GET', + url: `/regions/${DummyBiz.regionId}/businesses`, + headers: {authorization: `Bearer ${token}`} + }); +} + +export async function getRegionsByDummyManager(app: FastifyInstance, token: string = dummyRegionManagerToken) { + return await app.inject({ + method: 'GET', + headers: {authorization: `Bearer ${token}`}, + url: `/regions/manager/${DummyRegion.manager}` + }); +} diff --git a/tests/testUtils/testDataLayer.ts b/tests/testUtils/testDataLayer.ts new file mode 100644 index 0000000..942055b --- /dev/null +++ b/tests/testUtils/testDataLayer.ts @@ -0,0 +1,136 @@ +import {DataLayer, Filters, IdObject, Region} from "../../src/database/productionDataLayer"; +import {Business, CHUNK_SIZE} from "../../src/endpoints/businesses"; +import {EditRequest} from "../../src/endpoints/editRequest"; + +export class DummyDatalayer implements DataLayer { + businesses: Business[] = []; + regions: Region[] = []; + editRequests: EditRequest[] = []; + industries: string[] = []; + + async getBusinessById(id: string): Promise { + return this.businesses.find((b) => b.id === id) || null; + } + + async getBusinessesByRegion(_:string): Promise { + return Promise.resolve(this.businesses); + } + + async getAllBusinesses(afterId?: string): Promise { + let startIndex = this.businesses.findIndex(biz => biz.id === afterId); + startIndex = startIndex > 0 ? startIndex + 1 : 0; + return this.businesses.slice(startIndex, startIndex + CHUNK_SIZE); + } + + async deleteBusiness(id: string): Promise { + let startIndex = this.businesses.findIndex(biz => biz.id === id); + startIndex = startIndex > 0 ? startIndex : 0; + this.businesses.splice(startIndex, 1); + } + + async setBusiness(business:Business): Promise { + if(!business.id) { + business.id = `${Math.random()}`; + this.businesses.push({...business}); + } else { + this.businesses[this.businesses.findIndex(b => b.id == business.id)] = business; + } + let regionIndex = this.regions.findIndex((r) => r.name == business.regionId); + let bizRegion = this.regions[regionIndex] + if(!bizRegion.filters) { + bizRegion.filters = {}; + } + if(!bizRegion.filters.industries) { + bizRegion.filters.industries = []; + } + let industryIndex = bizRegion.filters.industries.findIndex((i) => i.industry === business.industry); + if(industryIndex < 0) { + bizRegion.filters.industries.push({industry: business.industry, count: 1}); + } else { + bizRegion.filters.industries[industryIndex].count++; + } + return {id: business.id}; + } + + async getFilters(regionId: string): Promise { + if(!regionId) { + return {industries: this.industries}; + } else { + return { + years: this.businesses.filter(b => b.regionId === regionId).map((b) => b.year_added), + industries: this.businesses.filter(b => b.regionId === regionId).map((b) => b.industry) + }; + } + } + + async setRegion(region: Region): Promise { + this.regions.push(region); + return {id: region.name}; + } + + async getAllRegions() : Promise { + return this.regions; + } + + async getRegionsManagedBy(managerId: string ): Promise { + return this.regions.filter((r) => managerId === r.manager); + } + + async deleteRegion(regionId: string): Promise { + this.regions = this.regions.filter((r) => r.name !== regionId); + } + + async createEditRequest(editRequest: EditRequest): Promise { + const newRequest = {...editRequest, id: new Date().toISOString() + Math.random()} + this.editRequests.push(newRequest); + return {id: newRequest.id} ; + } + + async getEditRequestById(id: string): Promise { + return this.editRequests.find((req) => req.id === id) || null; + } + + async getEditRequestsForRegion(regionId: string, pageSize: number, afterId?: string): Promise { + return this.getPaginatedEditRequests(pageSize, afterId, req => req.regionId === regionId); + } + + async getAllEditRequests(pageSize: number, afterId?: string): Promise { + return this.getPaginatedEditRequests(pageSize, afterId, () => true); + } + + async getEditRequestsByStatus(status: string, pageSize: number, afterId?: string): Promise { + return this.getPaginatedEditRequests(pageSize, afterId, (req) => req.status === status); + } + + async getEditRequestsByUser(userAppId: string, pageSize: number, afterId?: string): Promise { + return this.getPaginatedEditRequests(pageSize, afterId, (r) => r.submitter === userAppId); + } + + async updateEditRequest(body: EditRequest): Promise { + let index = this.editRequests.findIndex((req) => req.id === body.id); + this.editRequests[index] = {...this.editRequests[index], ...body}; + return this.editRequests[index]; + } + + async addIndustries(industries: string[]): Promise { + this.industries.push(...industries); + } + + async deleteIndustries(industries: string[]): Promise { + this.industries = this.industries.filter(i => !industries.find(i2 => i2 === i)); + return Promise.resolve([]); + } + + getPaginatedEditRequests(pageSize: number, afterId: string | undefined, filter: (r: EditRequest) => boolean) { + this.editRequests.reverse(); + let startIndex = !!afterId? this.editRequests.findIndex((r) => r.id === afterId) + 1 : 0; + let ret = this.editRequests.filter(filter).slice(startIndex, startIndex + pageSize); + this.editRequests.reverse(); + return ret; + } + + clearRegions() { + this.regions = []; + } + +} diff --git a/tests/testUtils/testFirestore.ts b/tests/testUtils/testFirestore.ts new file mode 100644 index 0000000..1c4ee94 --- /dev/null +++ b/tests/testUtils/testFirestore.ts @@ -0,0 +1,24 @@ +import {Firestore} from "@google-cloud/firestore"; + +// Because of some fun with quotas, I now have two possible test firestore targets, whee. +/* +var firebaseConfig = { + apiKey: "AIzaSyBhRqqU7RuMT72eFPP-VuswjMCA0uObj-s", + authDomain: "ranlab-test.firebaseapp.com", + projectId: "ranlab-test", + storageBucket: "ranlab-test.appspot.com", + messagingSenderId: "861002956013", + appId: "1:861002956013:web:3d9236b7e1e8a276bd20e4", + measurementId: "G-JPX391FT8E" +};*/ +var firebaseConfig = { + apiKey: "AIzaSyAyaNrOrfio5hL3DfNuexMQOpdefys7PSA", + authDomain: "temp-test-b8f04.firebaseapp.com", + projectId: "temp-test-b8f04", + storageBucket: "temp-test-b8f04.appspot.com", + messagingSenderId: "829656614445", + appId: "1:829656614445:web:bcb7083f650653310f1aa4", + measurementId: "G-180MVEJXN2" +}; + +export const testFirestore = new Firestore(firebaseConfig); diff --git a/tests/testUtils/testify.ts b/tests/testUtils/testify.ts new file mode 100644 index 0000000..dee7f34 --- /dev/null +++ b/tests/testUtils/testify.ts @@ -0,0 +1,78 @@ +import fastify, {FastifyRequest} from "fastify"; +import fastifyJWT, {FastifyJWTOptions} from "fastify-jwt"; +import jwt from "jsonwebtoken"; +import fastifySensible from "fastify-sensible"; +import fetch from "node-fetch"; + +export const AUTH0_CLAIMS_NAMESPACE = "https://mun.ca"; +export const mockSecret = 'dummy'; + +export function setupAuth0TestEnv() { + process.env.AUTH0_CLAIMS_NAMESPACE = AUTH0_CLAIMS_NAMESPACE; + process.env.AUTH0_DOMAIN = "dev-5ju75h98.us.auth0.com"; + process.env.AUTH0_CLIENT_ID = "iqwBRZwwuKGz0BkHiInTWTyqvOFLepd6"; + process.env.AUTH0_MGMT_CLIENT_ID = "HU171Qtg3j3395vGXuNsgmRq8XQPbKzT"; + process.env.TEST_AUTH0_USERNAME = "liquiddark@gmail.com"; + process.env.TEST_AUTH0_ADMIN_USERNAME = "burton.technical.709@gmail.com"; + +// process.env.TEST_AUTH0_PASSWORD must be set in local runtime environment +// process.env.TEST_AUTH0_ADMIN_PASSWORD must be set in local runtime environment +// process.env.TEST_AUTH0_CLIENT_SECRET must be set in local runtime environment +} + +export async function authenticateToTestDomain() { + let userResponse = await fetch(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, { + method: "POST", + headers: {'content-type': 'application/json'}, + body: JSON.stringify({ + client_id: process.env.AUTH0_CLIENT_ID, + client_secret: process.env.AUTH0_CLIENT_SECRET, + username: process.env.TEST_AUTH0_USERNAME, + password: process.env.TEST_AUTH0_PASSWORD, + scope: 'openid', + grant_type: "password" + }) + }); + let userJson = await userResponse.json() + let userAccessToken = userJson.access_token; + let adminResponse = await fetch(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, { + method: "POST", + headers: {'content-type': 'application/json'}, + body: JSON.stringify({ + client_id: process.env.AUTH0_CLIENT_ID, + client_secret: process.env.AUTH0_CLIENT_SECRET, + username: process.env.TEST_AUTH0_ADMIN_USERNAME, + password: process.env.TEST_AUTH0_ADMIN_PASSWORD, + scope: 'openid', + grant_type: "password" + }) + }); + let adminJson = await adminResponse.json(); + let adminAccessToken = adminJson.access_token; + return {userAccessToken, adminAccessToken}; +} + + +export function getMockToken(payload: {userAppId: string, admin: boolean }) { + let token : any = {}; + token['sub'] = `auth0|${payload.userAppId}`; + token[`${AUTH0_CLAIMS_NAMESPACE}/admin`] = payload.admin; + return jwt.sign(token, mockSecret) +} + +export function getTestJwtVerifier(userAppId: string, admin: boolean) { + return async(_: FastifyRequest) => ({userAppId, admin, role: admin ? "admin" : "region" }); +} + +export const testify = () => { + const f = fastify({logger: {level: "debug"}}); + f.register(fastifyJWT, { + secret: (_request, _reply, _provider) => { _provider(null, mockSecret);}, + audience: 'https://localhost', + issuer: 'https://localhost/', + algorithms: ['none'], + decode: { complete: true }, + }); + f.register(fastifySensible); + return f; +}; diff --git a/tests/users.test.ts b/tests/users.test.ts new file mode 100644 index 0000000..cdc503b --- /dev/null +++ b/tests/users.test.ts @@ -0,0 +1,105 @@ +import {authenticateToTestDomain, setupAuth0TestEnv, testify} from "./testUtils/testify"; +import createUsersEndpoint from "../src/endpoints/users"; +import { verifyJwt } from "../src/auth0"; + +describe("Auth0 user endpoint tests", () => { + let userAccessToken: string; + let adminAccessToken: string; + it("Can get all users as regular or admin user", async (done) => { + jest.setTimeout(15000); + async function getAllUsers(token:string) { + return await userApp.inject({ + method: "GET", + url: "/users", + headers: {authorization: `Bearer ${token}`} + }); + } + let server = testify(); + let userApp = createUsersEndpoint(server, verifyJwt); + setupAuth0TestEnv(); + let authTokens = await authenticateToTestDomain(); + userAccessToken = authTokens.userAccessToken; + adminAccessToken = authTokens.adminAccessToken; + + let userResponse = await getAllUsers(userAccessToken); + expect(userResponse.statusCode).toBe(200); + let userPayload = JSON.parse(userResponse.payload); + expect(userPayload.users).toBeTruthy(); + expect(userPayload.users.length).toBeGreaterThanOrEqual(1); + + let adminResponse = await getAllUsers(adminAccessToken); + expect(adminResponse.statusCode).toBe(200); + let adminPayload = JSON.parse(adminResponse.payload); + expect(adminPayload.users).toBeTruthy(); + expect(adminPayload.users.length).toBeGreaterThanOrEqual(1); + + done(); + }); + + it("Can get single user by ID as regular or admin user", async (done) => { + jest.setTimeout(15000); + async function getUserById(id: string, token:string) { + return await userApp.inject({ + method: "GET", + url: `/users/${id}`, + headers: {authorization: `Bearer ${token}`} + }); + } + let server = testify(); + let userApp = createUsersEndpoint(server, verifyJwt); + setupAuth0TestEnv(); + let authTokens = await authenticateToTestDomain(); + userAccessToken = authTokens.userAccessToken; + adminAccessToken = authTokens.adminAccessToken; + + const TEST_USER_ID = "601e3f8c531b71006cb088a2"; + + let userResponse = await getUserById(TEST_USER_ID, userAccessToken); + expect(userResponse.statusCode).toBe(200); + expect(userResponse.payload).toBeTruthy(); + + let adminResponse = await getUserById(TEST_USER_ID, adminAccessToken); + expect(adminResponse.statusCode).toBe(200); + expect(adminResponse.payload).toBeTruthy(); + + done(); + }); + + it("Can update a user as an admin but not a regular user", async (done) => { + jest.setTimeout(15000); + async function updateUser(id: string, updated: string, token:string) { + return await userApp.inject({ + method: "POST", + url: `/users/${id}`, + payload: {user_metadata: {updated: updated}}, + headers: {authorization: `Bearer ${token}`} + }); + } + let server = testify(); + let userApp = createUsersEndpoint(server, verifyJwt); + setupAuth0TestEnv(); + let authTokens = await authenticateToTestDomain(); + userAccessToken = authTokens.userAccessToken; + adminAccessToken = authTokens.adminAccessToken; + + const TEST_USER_ID = "601e3f8c531b71006cb088a2"; + const updatedDate = new Date().toISOString(); + + let userResponse = await updateUser(TEST_USER_ID, updatedDate, userAccessToken); + expect(userResponse.statusCode).toBe(401); + + let adminResponse = await updateUser(TEST_USER_ID, updatedDate, adminAccessToken); + expect(adminResponse.statusCode).toBe(200); + let adminPayload = JSON.parse(adminResponse.payload); + expect(adminPayload.user).toBeTruthy(); + expect(adminPayload.user.user_metadata.updated).toBe(updatedDate); + + done(); + }); + + /* + let userInfo = await getUserInfo(`Bearer ${userAccessToken}`); + let userAppId = userInfo.userId.split("|")[1]; + + */ +}); diff --git a/tsconfig.json b/tsconfig.json index 1fd317d..7c0e8e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,9 @@ /* Basic Options */ "incremental": true, "target": "ES2017", - "module": "CommonJS", + "module": "commonjs", "declaration": true, + "outDir": "build", /* Strict Type-Checking Options */ "strict": true, diff --git a/tsoa.json b/tsoa.json new file mode 100644 index 0000000..de252fe --- /dev/null +++ b/tsoa.json @@ -0,0 +1,12 @@ +{ + "entryFile": "src/index.ts", + "noImplicitAdditionalProperties": "throw-on-extras", + "controllerPathGlobs": ["src/**/*Controller.ts"], + "spec": { + "outputDirectory": "build", + "specVersion": 3 + }, + "routes": { + "routesDir": "build" + } +} diff --git a/types/baretest.d.ts b/types/baretest.d.ts new file mode 100644 index 0000000..ac80e40 --- /dev/null +++ b/types/baretest.d.ts @@ -0,0 +1,14 @@ +declare module 'baretest' { + export interface Test { + (name: string, fn: () => Promise): void; + run(): Promise; + skip(fn: () => Promise): void; + // This isn't real, but skip ignores its args + skip(name: string, fn: () => Promise): void; + // This isn't real, but skip ignores its args + skip(name: string): void; + only(name: string, fn: () => Promise): void; + } + + export default function (headline: string): Test; +} diff --git a/yarn.lock b/yarn.lock index 91445c5..1adbe6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,19 +2,838 @@ # yarn lockfile v1 +"@auth0/auth0-spa-js@^1.13.6": + version "1.13.6" + resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.13.6.tgz#3ad50e1a60467cd35ff3887edef67399bff98dfc" + integrity sha512-o+Mb0YXrasP364j+WB5hoALoL9sn8SZC0rDFLcyks6HUnQH/vzupdoWWFAIl1+EocAFB3CGMgSc425wpHNB4pw== + dependencies: + abortcontroller-polyfill "^1.5.0" + browser-tabs-lock "1.2.9" + core-js "^3.8.0" + es-cookie "^1.3.2" + fast-text-encoding "^1.0.3" + promise-polyfill "^8.2.0" + unfetch "^4.2.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.1.0", "@babel/core@^7.7.5": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz" + integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.1" + "@babel/parser" "^7.12.3" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.12.1", "@babel/generator@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz" + integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== + dependencies: + "@babel/types" "^7.12.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz" + integrity sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-module-imports@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-replace-supers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz" + integrity sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helpers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.12.3", "@babel/parser@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz" + integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz" + integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz" + integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/template@^7.10.4", "@babel/template@^7.3.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz" + integrity sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.5" + "@babel/types" "^7.12.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.12.1", "@babel/types@^7.12.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.12.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz" + integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@google-cloud/firestore@^4.9.9": + version "4.9.9" + resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-4.9.9.tgz#1bcf66a4f5a6c54c938489eb08f4ac97908835e8" + integrity sha512-M/Ts8oyJP1Ig8uMrUr/pNGGwBDPkB9ALqmH182T5aY5HW00yzLcHiDcKlfk9PoBINakAMk15GJZ1ov+I17HcnQ== + dependencies: + fast-deep-equal "^3.1.1" + functional-red-black-tree "^1.0.1" + google-gax "^2.9.2" + protobufjs "^6.8.6" + +"@grpc/grpc-js@~1.2.0": + version "1.2.12" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.12.tgz#0153f27512acf69184bb52c0a1035ca91d6c14b0" + integrity sha512-+gPCklP1eqIgrNPyzddYQdt9+GvZqPlLpIjIo+TveE+gbtp74VV1A2ju8ExeO8ma8f7MbpaGZx/KJPYVWL9eDw== + dependencies: + "@types/node" ">=12.12.47" + google-auth-library "^6.1.1" + semver "^6.2.0" + +"@grpc/proto-loader@^0.5.1": + version "0.5.6" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.6.tgz#1dea4b8a6412b05e2d58514d507137b63a52a98d" + integrity sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ== + dependencies: + lodash.camelcase "^4.3.0" + protobufjs "^6.8.6" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + dependencies: + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== + dependencies: + "@jest/types" "^26.6.2" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" + +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + optionalDependencies: + node-notifier "^8.0.0" + +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== + dependencies: + "@jest/test-result" "^26.6.2" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.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" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz" + 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" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@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" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + +"@sinonjs/commons@^1.7.0": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + version "7.1.12" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz" + integrity sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz" + integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz" + integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz" + integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.34" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz" + integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== + dependencies: + "@types/node" "*" + +"@types/express-jwt@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@types/express-jwt/-/express-jwt-0.0.42.tgz" + integrity sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag== + dependencies: + "@types/express" "*" + "@types/express-unless" "*" + +"@types/express-serve-static-core@*": + version "4.17.15" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.15.tgz" + integrity sha512-pb71P0BrBAx7cQE+/7QnA1HTQUkdBKMlkPY7lHUMn0YvPJkL2UA+KW3BdWQ309IT+i9En/qm45ZxpjIcpgEhNQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express-unless@*": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@types/express-unless/-/express-unless-0.5.1.tgz" + integrity sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw== + dependencies: + "@types/express" "*" + +"@types/express@*": + version "4.17.9" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.9.tgz" + integrity sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/graceful-fs@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz" + integrity sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@26.x", "@types/jest@^26.0.15": + version "26.0.15" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.15.tgz" + integrity sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + +"@types/jsonwebtoken@^8.3.2", "@types/jsonwebtoken@^8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== + dependencies: + "@types/node" "*" + +"@types/long@^4.0.0", "@types/long@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + +"@types/mime@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz" + integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== + +"@types/node-fetch@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" + integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "14.14.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz" + integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== + +"@types/node@>=12.12.47": + version "14.14.41" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" + integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== + "@types/node@^12.12.67": version "12.12.67" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.67.tgz#4f86badb292e822e3b13730a1f9713ed2377f789" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.67.tgz" integrity sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg== +"@types/node@^13.7.0": + version "13.13.30" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.30.tgz" + integrity sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/prettier@^2.0.0": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz" + integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ== + +"@types/qs@*": + version "6.9.5" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz" + integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/serve-static@*": + version "1.13.8" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.8.tgz" + integrity sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== + +"@types/yargs-parser@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz" + integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + +"@types/yargs@^15.0.0": + version "15.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.9.tgz" + integrity sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g== + dependencies: + "@types/yargs-parser" "*" + +abab@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abortcontroller-polyfill@^1.5.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.1.tgz#27084bac87d78a7224c8ee78135d05df430c2d2f" + integrity sha512-yml9NiDEH4M4p0G4AcPkg8AAa4mF3nfYF28VQxaokpO67j9H7gWgmsVWJ/f1Rn+PzsnDYvzJzWIQzCqDKRvWlA== + abstract-logging@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.0.tgz#08a85814946c98ef06f4256ad470aba1886d4490" + resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.0.tgz" integrity sha512-/oA9z7JszpIioo6J6dB79LVUgJ3eD3cxkAmdCkvWWS+Y9tPtALs1rLqOekLUXUbYqM2fB9TTK0ibAyZJJOP/CA== +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + ajv@^6.11.0, ajv@^6.12.2: version "6.12.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz" integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== dependencies: fast-deep-equal "^3.1.1" @@ -22,153 +841,2395 @@ ajv@^6.11.0, ajv@^6.12.2: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arrify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + +avvio@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/avvio/-/avvio-7.2.0.tgz" + integrity sha512-KtC63UyZARidAoIV8wXutAZnDIbZcXBqLjTAhZOX+mdMZBQCh5il/15MvCvma1178nhTwvN2D0TOAdiKG1MpUA== + dependencies: + archy "^1.0.0" + debug "^4.0.0" + fastq "^1.6.1" + queue-microtask "^1.1.2" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +axios@^0.21.1: + version "0.21.1" + resolved "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz" + integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + dependencies: + follow-redirects "^1.10.0" + +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz" + integrity sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +barecolor@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/barecolor/-/barecolor-1.0.1.tgz" + integrity sha512-ncJ680U+r1CGBt73L3O6V9GIAPy3hbDmWODEQajwEnDmmzeStvc4UYhapUSxUpS76+MHxyRihzZfwhyl122Zdw== + +baretest@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/baretest/-/baretest-2.0.0.tgz" + integrity sha512-hRmYnBojeijT3jH0GtqLoHus+adPoeYh2NmcNT3wBPH903AUphcFqs1gJ64fBovDXql51Df24g9D9jcXRZd4vA== + dependencies: + barecolor "1.0.1" + +base64-js@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bignumber.js@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +body-parser@^1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browser-tabs-lock@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/browser-tabs-lock/-/browser-tabs-lock-1.2.9.tgz#fa9568471c49de1dbe311a6da95f86794bbe1fdf" + integrity sha512-cczryjv6i6kAfTWKhhNW3LWhFDwzPazEsNG9IG2n6AeYzPVb1tUCY3aqTKeFJL0rKUnfSXto7esjrqY3fz+ugA== + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-from@1.x, buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz" + 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" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +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" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js@^3.8.0: + version "3.8.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.3.tgz#c21906e1f14f3689f93abcc6e26883550dd92dd0" + integrity sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decimal.js@^10.2.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +duplexify@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" + integrity sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-cookie@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/es-cookie/-/es-cookie-1.3.2.tgz#80e831597f72a25721701bdcb21d990319acd831" + integrity sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^1.14.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.2, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-decode-uri-component@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz" + integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-json-stringify@^2.2.1: + version "2.2.8" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.2.8.tgz" + integrity sha512-/ASLcFlk998wuzAqbbspt7038fHXM1mTYk5N2Il+tOSnfVuHBwkH5tRnTMaB45Adv+SeS4McFXvAFI1+7uEkXw== + dependencies: + ajv "^6.11.0" + deepmerge "^4.2.2" + string-similarity "^4.0.1" + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-redact@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.0.tgz" + integrity sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w== + +fast-safe-stringify@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz" + integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== + +fastfall@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/fastfall/-/fastfall-1.5.1.tgz" + integrity sha1-P+4DMxpJ0dObPN96XpzWb0dee5Q= + dependencies: + reusify "^1.0.0" + +fastify-auth0-verify@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/fastify-auth0-verify/-/fastify-auth0-verify-0.4.2.tgz#96281adeb13f0f8298f3ffe80647d141411f28d6" + integrity sha512-l7LBdS5mAYJgYD+khzB3DH5ZLuO2+/MxOa2NsKIowDAcrVntteUavMQljvweLWQLFe1KfmmmZEbf5JktlSYtoQ== + dependencies: + fastify-jwt "^2.1.2" + fastify-plugin "^3.0.0" + http-errors "^1.7.3" + node-cache "^5.0.1" + node-fetch "^2.6.1" + +fastify-authz-jwks@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/fastify-authz-jwks/-/fastify-authz-jwks-1.1.11.tgz#1b0b8d18962e4f1a4e6033a687eaf5325a6b8351" + integrity sha512-8puiTBrphRrQy0fpJQLpwYKIyyeAWDkdp9/EDGVWbGOypvA/zc8RXaTCtDQxQ9JOxx0X0/g8IEYQv/37yJtCPQ== + dependencies: + jwks-rsa "^1.6.0" + +fastify-cors@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/fastify-cors/-/fastify-cors-5.1.0.tgz" + integrity sha512-PTq4wSBFljXmbFKmp5eKW9gg0m488nXBl+OSTb/vnWXXLvVn7HFk7WKgt7uQVfd9UuksveCGTXipVlus3qhmrw== + dependencies: + fastify-plugin "^3.0.0" + vary "^1.1.2" + +fastify-error@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/fastify-error/-/fastify-error-0.2.0.tgz" + integrity sha512-zabxsBatj59ROG0fhP36zNdc5Q1/eYeH9oSF9uvfrurZf8/JKfrJbMcIGrLpLWcf89rS6L91RHWm20A/X85hcA== + +fastify-jwt@^2.1.2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/fastify-jwt/-/fastify-jwt-2.3.0.tgz#0f20e3267728d6b570e4240915b087a1f8853e13" + integrity sha512-dRjkrfVpZnic1DxIjN8lyhL/KM/YQsLy6SeyPhKxRkTEJIjY3sViDqfhCUc812jSgbqM7Sx22BA7moqpmCtA3Q== + dependencies: + "@types/jsonwebtoken" "^8.5.0" + fastify-plugin "^3.0.0" + http-errors "^1.8.0" + jsonwebtoken "^8.5.1" + steed "^1.1.3" + +fastify-jwt@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fastify-jwt/-/fastify-jwt-2.1.3.tgz" + integrity sha512-8732zt+7UA9JzeRebJFCH+56laMCAxq/Wyou6pzXZzEWcPGPLcRqCk+R0CcgwjjVToo6wLIeNNKHFegyemyHug== + dependencies: + "@types/jsonwebtoken" "^8.3.2" + fastify-plugin "^2.0.0" + http-errors "^1.7.1" + jsonwebtoken "^8.3.0" + steed "^1.1.3" + +fastify-plugin@^2.0.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-2.3.4.tgz" + integrity sha512-I+Oaj6p9oiRozbam30sh39BiuiqBda7yK2nmSPVwDCfIBlKnT8YB3MY+pRQc2Fcd07bf6KPGklHJaQ2Qu81TYQ== + dependencies: + semver "^7.3.2" + +fastify-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.0.tgz" + integrity sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w== + +fastify-sensible@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fastify-sensible/-/fastify-sensible-3.1.0.tgz#259f13373e0df2d37b720e08474db96c6f62985a" + integrity sha512-T5ZzpZ0I+seu4WvtU3O1YD3OhYU2RLewTObaMHCxkxEw8uLFRk9zgnbYk7YeiS8azV65jTPYTElCfgrHhfqOXA== + dependencies: + fast-deep-equal "^3.1.1" + fastify-plugin "^3.0.0" + forwarded "^0.1.2" + http-errors "^1.7.3" + type-is "^1.6.18" + vary "^1.1.2" + +fastify-static@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/fastify-static/-/fastify-static-3.4.0.tgz#812f7af416ef2e998c7d6c19ae3dd7afc8d7ca58" + integrity sha512-5y9xTNiPTj6/jDwzH6CqBIcI3/yZtocUiHoLud2NYPfHSOLlS6eW6DTheiU8b9WWlfmHfqOjwFFBdhiH1+nBhg== + dependencies: + fastify-plugin "^3.0.0" + glob "^7.1.4" + readable-stream "^3.4.0" + send "^0.17.1" + +fastify-swagger@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fastify-swagger/-/fastify-swagger-4.0.1.tgz#b852c56942b244127493330a700e5ae661dd8b8c" + integrity sha512-K3CRNnpEUtBO+CZYGzXPgR+2dwQPqHppPRUI1Q0u2GcR89tT6wZhVOMbtxw8ks9Y6lt1IRRdV0C8ZbT+TC/yjg== + dependencies: + fastify-plugin "^3.0.0" + fastify-static "^3.3.0" + js-yaml "^4.0.0" + json-schema-resolver "^1.2.0" + openapi-types "^7.2.3" + +fastify-warning@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/fastify-warning/-/fastify-warning-0.2.0.tgz" + integrity sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw== + +fastify@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/fastify/-/fastify-3.5.1.tgz" + integrity sha512-SO/ZZSbbAUrRxxz9zIsf1Ek5qqRux5EN03UF1lJGt/xwFzVpmW4h+p8kRCD08VCL1sItFy2dhFlR8FPaQfRGBw== + dependencies: + abstract-logging "^2.0.0" + ajv "^6.12.2" + avvio "^7.1.2" + fast-json-stringify "^2.2.1" + fastify-error "^0.2.0" + fastify-warning "^0.2.0" + find-my-way "^3.0.0" + flatstr "^1.0.12" + light-my-request "^4.0.2" + pino "^6.2.1" + proxy-addr "^2.0.5" + readable-stream "^3.4.0" + rfdc "^1.1.4" + secure-json-parse "^2.0.0" + semver "^7.3.2" + tiny-lru "^7.0.0" + +fastparallel@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/fastparallel/-/fastparallel-2.4.0.tgz" + integrity sha512-sacwQ7wwKlQXsa7TN24UvMBLZNLmVcPhmxccC9riFqb3N+fSczJL8eWdnZodZ/KijGVgNBBfvF/NeXER08uXnQ== + dependencies: + reusify "^1.0.4" + xtend "^4.0.2" + +fastq@^1.3.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz" + integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== + dependencies: + reusify "^1.0.4" + +fastq@^1.6.1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz" + integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== + dependencies: + reusify "^1.0.4" + +fastseries@^1.7.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/fastseries/-/fastseries-1.7.2.tgz" + integrity sha1-0izhO5Qz3/M4jZHb1ri9qbIaD0s= + dependencies: + reusify "^1.0.0" + xtend "^4.0.0" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flatstr@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz" + integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== + +follow-redirects@^1.10.0: + version "1.13.1" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz" + integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + 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" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@^0.1.2, forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^2.1.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.1.tgz#b209ab14c61012636c8863507edf7fb68cc54e9f" + integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gaxios@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.0.1.tgz" + integrity sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ== + dependencies: + abort-controller "^3.0.0" + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.3.0" + +gcp-metadata@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.2.1.tgz" + integrity sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw== + dependencies: + gaxios "^4.0.0" + json-bigint "^1.0.0" + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +google-auth-library@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.3.tgz" + integrity sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^4.0.0" + gcp-metadata "^4.2.0" + gtoken "^5.0.4" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-auth-library@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.0.4.tgz#610cb010de71435dca47dfbe8dc7fbff23055d2c" + integrity sha512-o8irYyeijEiecTXeoEe8UKNEzV1X+uhR4b2oNdapDMZixypp0J+eHimGOyx5Joa3UAeokGngdtDLXtq9vDqG2Q== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^4.0.0" + gcp-metadata "^4.2.0" + gtoken "^5.0.4" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-gax@^2.9.2: + version "2.11.2" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.11.2.tgz#9ef7773b94aaa61c4588fb2408d62e8444995026" + integrity sha512-PNqXv7Oi5XBMgoMWVxLZHUidfMv7cPHrDSDXqLyEd6kY6pqFnVKC8jt2T1df4JPSc2+VLPdeo6L7X9mbdQG8Xw== + dependencies: + "@grpc/grpc-js" "~1.2.0" + "@grpc/proto-loader" "^0.5.1" + "@types/long" "^4.0.0" + abort-controller "^3.0.0" + duplexify "^4.0.0" + fast-text-encoding "^1.0.3" + google-auth-library "^7.0.2" + is-stream-ended "^0.1.4" + node-fetch "^2.6.1" + protobufjs "^6.10.2" + retry-request "^4.0.0" + +google-p12-pem@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.3.tgz" + integrity sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA== + dependencies: + node-forge "^0.10.0" + +graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +gtoken@^5.0.4: + version "5.0.5" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.0.5.tgz" + integrity sha512-wvjkecutFh8kVfbcdBdUWqDRrXb+WrgD79DBDEYf1Om8S1FluhylhtFjrL7Tx69vNhh259qA3Q1P4sPtb+kUYw== + dependencies: + gaxios "^4.0.0" + google-p12-pem "^3.0.3" + jws "^4.0.0" + mime "^2.2.0" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@^1.7.1, http-errors@^1.7.3, http-errors@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + 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" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz" + integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz" + integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +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" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz" + integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + +is-stream-ended@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" + integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== + dependencies: + "@jest/types" "^26.6.2" + execa "^4.0.0" + throat "^5.0.0" + +jest-cli@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== + dependencies: + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + is-ci "^2.0.0" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" + prompts "^2.0.1" + yargs "^15.4.1" + +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + micromatch "^4.0.2" + pretty-format "^26.6.2" + +jest-diff@^26.0.0, jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" + +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" + +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^26.6.2" + is-generator-fn "^2.0.0" + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" + throat "^5.0.0" + +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== + dependencies: + "@jest/types" "^26.6.2" + jest-regex-util "^26.0.0" + jest-snapshot "^26.6.2" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-docblock "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" + source-map-support "^0.5.6" + throat "^5.0.0" + +jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + cjs-module-lexer "^0.6.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.4.1" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.1.0, jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== + dependencies: + "@jest/types" "^26.6.2" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + leven "^3.1.0" + pretty-format "^26.6.2" + +jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== + dependencies: + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.6.2" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz" + integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== + dependencies: + "@jest/core" "^26.6.3" + import-local "^3.0.2" + jest-cli "^26.6.3" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" + integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== + dependencies: + argparse "^2.0.1" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^16.4.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz" + integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== + dependencies: + abab "^2.0.3" + acorn "^7.1.1" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.2.0" + data-urls "^2.0.0" + decimal.js "^10.2.0" + domexception "^2.0.1" + escodegen "^1.14.1" + html-encoding-sniffer "^2.0.1" + is-potential-custom-element-name "^1.0.0" + nwsapi "^2.2.0" + parse5 "5.1.1" + request "^2.88.2" + request-promise-native "^1.0.8" + saxes "^5.0.0" + symbol-tree "^3.2.4" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + ws "^7.2.3" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -atomic-sleep@^1.0.0: +json-bigint@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" - integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== - -avvio@^7.1.2: - version "7.2.0" - resolved "https://registry.yarnpkg.com/avvio/-/avvio-7.2.0.tgz#b4bf4eaf4a0207a4e6a58a7859207250793cc81b" - integrity sha512-KtC63UyZARidAoIV8wXutAZnDIbZcXBqLjTAhZOX+mdMZBQCh5il/15MvCvma1178nhTwvN2D0TOAdiKG1MpUA== + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== dependencies: - archy "^1.0.0" - debug "^4.0.0" - fastq "^1.6.1" - queue-microtask "^1.1.2" + bignumber.js "^9.0.0" -cookie@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -debug@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== +json-schema-resolver@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/json-schema-resolver/-/json-schema-resolver-1.2.2.tgz#52aeb8dbe4ece0df82c0ac2f1642384612542270" + integrity sha512-sW4b4BDJzYiKpJind7l1JtH3P1yn43vCv3w51YR2Ixse5rXr006TL10gM0Ek54pET6vxwiWq5RQuIMgmH9YrrQ== dependencies: - ms "2.1.2" + debug "^4.1.1" + rfdc "^1.1.4" + uri-js "^4.2.2" -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +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" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -fast-decode-uri-component@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" - integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -fast-deep-equal@^3.1.1: - 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== +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +json5@2.x, json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" -fast-json-stringify@^2.2.1: - version "2.2.8" - resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.2.8.tgz#6c6ff59bda09412c1ee462323eda2debe4446884" - integrity sha512-/ASLcFlk998wuzAqbbspt7038fHXM1mTYk5N2Il+tOSnfVuHBwkH5tRnTMaB45Adv+SeS4McFXvAFI1+7uEkXw== +jsonwebtoken@^8.3.0, jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== dependencies: - ajv "^6.11.0" - deepmerge "^4.2.2" - string-similarity "^4.0.1" + 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" -fast-redact@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.0.tgz#ac2f9e36c9f4976f5db9fb18c6ffbaf308cf316d" - integrity sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w== +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" -fast-safe-stringify@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" - integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" -fastify-error@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/fastify-error/-/fastify-error-0.2.0.tgz#9a1c28d4f42b6259e7a549671c8e5e2d85660634" - integrity sha512-zabxsBatj59ROG0fhP36zNdc5Q1/eYeH9oSF9uvfrurZf8/JKfrJbMcIGrLpLWcf89rS6L91RHWm20A/X85hcA== +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" -fastify-warning@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/fastify-warning/-/fastify-warning-0.2.0.tgz#e717776026a4493dc9a2befa44db6d17f618008f" - integrity sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw== +jwks-rsa@^1.12.0, jwks-rsa@^1.6.0: + version "1.12.2" + resolved "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.12.2.tgz" + integrity sha512-6gPo/mQUxXJt75oPtjhM3Jm3FSXnmwg73QDA8dpgP7YmIKlIY+2StngFxt4w4Y1podtSbtV3jttNOdctuxAX1Q== + dependencies: + "@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" -fastify@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/fastify/-/fastify-3.5.1.tgz#753a6909e3154d61fcde7402500ed92177132c1a" - integrity sha512-SO/ZZSbbAUrRxxz9zIsf1Ek5qqRux5EN03UF1lJGt/xwFzVpmW4h+p8kRCD08VCL1sItFy2dhFlR8FPaQfRGBw== +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: - abstract-logging "^2.0.0" - ajv "^6.12.2" - avvio "^7.1.2" - fast-json-stringify "^2.2.1" - fastify-error "^0.2.0" - fastify-warning "^0.2.0" - find-my-way "^3.0.0" - flatstr "^1.0.12" - light-my-request "^4.0.2" - pino "^6.2.1" - proxy-addr "^2.0.5" - readable-stream "^3.4.0" - rfdc "^1.1.4" - secure-json-parse "^2.0.0" - semver "^7.3.2" - tiny-lru "^7.0.0" + jwa "^1.4.1" + safe-buffer "^5.0.1" -fastq@^1.6.1: - version "1.8.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" - integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== dependencies: - reusify "^1.0.4" + jwa "^2.0.0" + safe-buffer "^5.0.1" + +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= find-my-way@^3.0.0: version "3.0.5" resolved "https://registry.yarnpkg.com/find-my-way/-/find-my-way-3.0.5.tgz#f71c5ef1b4865401e1b97ba428121a8f55439eec" integrity sha512-FweGg0cv1sBX8z7WhvBX5B5AECW4Zdh/NiB38Oa0qwSNIyPgRBCl/YjxuZn/rz38E/MMBHeVKJ22i7W3c626Gg== dependencies: - fast-decode-uri-component "^1.0.1" - safe-regex2 "^2.0.0" - semver-store "^0.3.0" + is-buffer "^1.1.5" -flatstr@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" - integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" light-my-request@^4.0.2: version "4.1.1" - resolved "https://registry.yarnpkg.com/light-my-request/-/light-my-request-4.1.1.tgz#71a4f5742affd21d54e70895b6c6cec08feb9bad" + resolved "https://registry.yarnpkg.com/light-my-request/-/light-my-request-4.1.1.tgz" integrity sha512-4H0T0PQcFB/fGTIkNV5ShuftWnuUKdtLWq5t2zt+lwMWRZkVviTfmJqGOXeAAqkdREnGJQXa8zJ4wXJ0LrzrTA== dependencies: ajv "^6.12.2" @@ -177,145 +3238,1697 @@ light-my-request@^4.0.2: readable-stream "^3.6.0" set-cookie-parser "^2.4.1" +limiter@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz" + integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz" + 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" + integrity sha1-HRdnnAac2l0ECZGgnbwsDbN35V4= + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +lru-memoizer@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.1.3.tgz" + integrity sha512-DcAptVUrKHbyKfSpvthwHwD42bFBLSAhTXJf5PQunu4F0/Hzy41WTamvavUWqsOPps26D0l5534aFvcwEcYzDw== + dependencies: + lodash.clonedeep "^4.5.0" + lru-cache "~4.0.0" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.2.0: + version "2.4.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz" + integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@1.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1, ms@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-cache@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== + dependencies: + clone "2.x" + +node-fetch@^2.3.0, node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz" + integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +openapi-types@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-7.2.3.tgz#83829911a3410a022f0e0cf2b0b2e67232ccf96e" + integrity sha512-olbaNxz12R27+mTyJ/ZAFEfUruauHH27AkeQHDHRq5AF0LdNkK1SSV7EourXQDK+4aX7dv2HtyirAGK06WMAsA== + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +p-each-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz" + integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz" + integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz" + 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" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.5: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + pino-std-serializers@^2.4.2: version "2.5.0" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.5.0.tgz#40ead781c65a0ce7ecd9c1c33f409d31fe712315" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.5.0.tgz" integrity sha512-wXqbqSrIhE58TdrxxlfLwU9eDhrzppQDvGhBEr1gYbzzM4KKo3Y63gSjiDXRKLVS2UOXdPNR2v+KnQgNrs+xUg== pino@^6.2.1: version "6.7.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-6.7.0.tgz#d5d96b7004fed78816b5694fda3eab02b5ca6d23" + resolved "https://registry.yarnpkg.com/pino/-/pino-6.7.0.tgz" integrity sha512-vPXJ4P9rWCwzlTJt+f0Ni4THc3DWyt8iDDCO4edQ8narTu6hnpzdXu8FqeSJCGndl1W6lfbYQUQihUO54y66Lw== dependencies: - fast-redact "^3.0.0" - fast-safe-stringify "^2.0.7" - flatstr "^1.0.12" - pino-std-serializers "^2.4.2" - quick-format-unescaped "^4.0.1" - sonic-boom "^1.0.2" + fast-redact "^3.0.0" + fast-safe-stringify "^2.0.7" + flatstr "^1.0.12" + pino-std-serializers "^2.4.2" + quick-format-unescaped "^4.0.1" + sonic-boom "^1.0.2" + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +promise-polyfill@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0" + integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g== + +prompts@^2.0.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz" + integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +protobufjs@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b" + integrity sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" "^13.7.0" + long "^4.0.0" + +protobufjs@^6.8.6: + version "6.10.1" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.1.tgz" + integrity sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" "^13.7.0" + long "^4.0.0" proxy-addr@^2.0.5: version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz" integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== dependencies: forwarded "~0.1.2" ipaddr.js "1.9.1" -punycode@^2.1.0: +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + queue-microtask@^1.1.2: version "1.1.4" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.1.4.tgz#40841ace4356b48b35b5ea61a2e1fe0a23c59ce1" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.1.4.tgz" integrity sha512-eY/4Obve9cE5FK8YvC1cJsm5cr7XvAurul8UtBDJ2PR1p5NmAwHtvAt5ftcLtwYRCUKNhxCneZZlxmUDFoSeKA== quick-format-unescaped@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz#437a5ea1a0b61deb7605f8ab6a8fd3858dbeb701" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz" integrity sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A== -readable-stream@^3.4.0, readable-stream@^3.6.0: +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-is@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz" + integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^3.1.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" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0, resolve@^1.18.1, resolve@^1.3.2: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + ret@~0.2.0: version "0.2.2" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz" integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== -reusify@^1.0.4: +retry-request@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.1.3.tgz#d5f74daf261372cff58d08b0a1979b4d7cab0fde" + integrity sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ== + dependencies: + debug "^4.1.1" + +reusify@^1.0.0, reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz" integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== -safe-buffer@~5.2.0: +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-regex2@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/safe-regex2/-/safe-regex2-2.0.0.tgz#b287524c397c7a2994470367e0185e1916b1f5b9" + resolved "https://registry.yarnpkg.com/safe-regex2/-/safe-regex2-2.0.0.tgz" integrity sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ== dependencies: ret "~0.2.0" +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +saxes@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + secure-json-parse@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.1.0.tgz#ae76f5624256b5c497af887090a5d9e156c9fb20" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.1.0.tgz" integrity sha512-GckO+MS/wT4UogDyoI/H/S1L0MCcKS1XX/vp48wfmU7Nw4woBmb8mIpu4zPBQjKlRT88/bt9xdoV4111jPpNJA== semver-store@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/semver-store/-/semver-store-0.3.0.tgz#ce602ff07df37080ec9f4fb40b29576547befbe9" + resolved "https://registry.yarnpkg.com/semver-store/-/semver-store-0.3.0.tgz" integrity sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg== -semver@^7.3.2: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.x, semver@^7.3.2: version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@^0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + set-cookie-parser@^2.4.1: version "2.4.6" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz#43bdea028b9e6f176474ee5298e758b4a44799c3" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz" integrity sha512-mNCnTUF0OYPwYzSHbdRdCfNNHqrne+HS5tS5xNb6yJbdP9wInV0q5xPLE0EyfV/Q3tImo3y/OXpD8Jn0Jtnjrg== +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz" + 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" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + sonic-boom@^1.0.2: version "1.3.0" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.3.0.tgz#5c77c846ce6c395dddf2eb8e8e65f9cc576f2e76" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.3.0.tgz" integrity sha512-4nX6OYvOYr6R76xfQKi6cZpTO3YSWe/vd+QdIfoH0lBy0MnPkeAbb2rRWgmgADkXUeCKPwO1FZAKlAVWAadELw== dependencies: atomic-sleep "^1.0.0" flatstr "^1.0.12" +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz" + integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== + dependencies: + escape-string-regexp "^2.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +steed@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/steed/-/steed-1.1.3.tgz" + integrity sha1-8VJd1a2xLrIb90dJU3Zo1iW5q8U= + dependencies: + fastfall "^1.5.0" + fastparallel "^2.2.0" + fastq "^1.3.0" + fastseries "^1.7.0" + reusify "^1.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +string-length@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz" + integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + string-similarity@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.2.tgz#1dc29518d0e92e50509499ce2974368f77bbb1b1" + resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.2.tgz" integrity sha512-eCsPPyoQBgY4TMpVD6DVfO7pLrimUONriaO4Xjp3WPUW0YnNLqdHgRj23xotLlqrL90eJhBeq3zdAJf2mQgfBQ== +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz" + integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + tiny-lru@^7.0.0: version "7.0.6" - resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-7.0.6.tgz#b0c3cdede1e5882aa2d1ae21cb2ceccf2a331f24" + resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-7.0.6.tgz" integrity sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow== +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +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" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +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" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@^2.3.3, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz" + integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + dependencies: + punycode "^2.1.1" + +ts-jest@^26.4.4: + version "26.4.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.4.4.tgz" + integrity sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg== + dependencies: + "@types/jest" "26.x" + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + jest-util "^26.1.0" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + mkdirp "1.x" + semver "7.x" + yargs-parser "20.x" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@^1.6.18, type-is@~1.6.17: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typescript@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz" integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + uri-js@^4.2.2: version "4.4.0" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz" integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== dependencies: punycode "^2.1.0" +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + util-deprecate@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== + +v8-to-istanbul@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz" + integrity sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz" + integrity sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^2.0.2" + webidl-conversions "^6.1.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + 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" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.2.3: + version "7.4.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz" + integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xtend@^4.0.0, xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@20.x: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.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 "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2"