commit 42921527f8874ea20f0d1fad9c4c8f2f41dec5f1 Author: null31 Date: Mon Oct 13 00:23:31 2025 -0300 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..0166584 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Todos os direitos reservados a https://atendechat.com diff --git a/backend/.editorconfig b/backend/.editorconfig new file mode 100644 index 0000000..11695db --- /dev/null +++ b/backend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..035daca --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,36 @@ +NODE_ENV= +BACKEND_URL=http://localhost +FRONTEND_URL=http://localhost:3000 +PROXY_PORT=8080 +PORT=8080 + +DB_DIALECT=postgres +DB_HOST=localhost +DB_PORT=5432 +DB_USER=user +DB_PASS=senha +DB_NAME=db_name + +JWT_SECRET=kZaOTd+YZpjRUyyuQUpigJaEMk4vcW4YOymKPZX0Ts8= +JWT_REFRESH_SECRET=dBSXqFg9TaNUEDXVp6fhMTRLBysP+j2DSqf7+raxD3A= + +REDIS_URI=redis://:123456@127.0.0.1:6379 +REDIS_OPT_LIMITER_MAX=1 +REDIS_OPT_LIMITER_DURATION=3000 + +USER_LIMIT=10000 +CONNECTIONS_LIMIT=100000 +CLOSED_SEND_BY_ME=true + +GERENCIANET_SANDBOX=false +GERENCIANET_CLIENT_ID=Client_Id_Gerencianet +GERENCIANET_CLIENT_SECRET=Client_Secret_Gerencianet +GERENCIANET_PIX_CERT=certificado-Gerencianet +GERENCIANET_PIX_KEY=chave pix gerencianet + +# EMAIL + MAIL_HOST="smtp.gmail.com" + MAIL_USER="seu@gmail.com" + MAIL_PASS="SuaSenha" + MAIL_FROM="seu@gmail.com" + MAIL_PORT="465" diff --git a/backend/.eslintignore b/backend/.eslintignore new file mode 100644 index 0000000..77b9a34 --- /dev/null +++ b/backend/.eslintignore @@ -0,0 +1,3 @@ +/*.js +node_modules +dist diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json new file mode 100644 index 0000000..aa015fa --- /dev/null +++ b/backend/.eslintrc.json @@ -0,0 +1,49 @@ +{ + "env": { + "es2021": true, + "node": true, + "jest": true + }, + "extends": [ + "airbnb-base", + "plugin:@typescript-eslint/recommended", + "prettier/@typescript-eslint", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint", "prettier"], + "rules": { + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { "argsIgnorePattern": "_" } + ], + "import/prefer-default-export": "off", + "no-console": "off", + "no-param-reassign": "off", + "prettier/prettier": "error", + "import/extensions": [ + "error", + "ignorePackages", + { + "ts": "never" + } + ], + "quotes": [ + 1, + "double", + { + "avoidEscape": true + } + ] + }, + "settings": { + "import/resolver": { + "typescript": {} + } + } +} diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..1c092bc --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,16 @@ +node_modules +public/* +dist +!public/.gitkeep +.env +.env.test + +package-lock.json +yarn.lock +yarn-error.log + +/src/config/sentry.js + +# Ignore test-related files +/coverage.data +/coverage/ diff --git a/backend/.sequelizerc b/backend/.sequelizerc new file mode 100644 index 0000000..264f851 --- /dev/null +++ b/backend/.sequelizerc @@ -0,0 +1,8 @@ +const { resolve } = require("path"); + +module.exports = { + "config": resolve(__dirname, "dist", "config", "database.js"), + "modules-path": resolve(__dirname, "dist", "models"), + "migrations-path": resolve(__dirname, "dist", "database", "migrations"), + "seeders-path": resolve(__dirname, "dist", "database", "seeds") +}; diff --git a/backend/certs/coloque_seus_certificado_aqui b/backend/certs/coloque_seus_certificado_aqui new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/backend/certs/coloque_seus_certificado_aqui @@ -0,0 +1 @@ + diff --git a/backend/jest.config.js b/backend/jest.config.js new file mode 100644 index 0000000..76c1b57 --- /dev/null +++ b/backend/jest.config.js @@ -0,0 +1,186 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/en/configuration.html + */ + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + bail: 1, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + collectCoverageFrom: ["/src/services/**/*.ts"], + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "v8", + + // A list of reporter names that Jest uses when writing coverage reports + coverageReporters: ["text", "lcov"], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + preset: "ts-jest", + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + testMatch: ["**/__tests__/**/*.spec.ts"] + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..cd4d620 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,99 @@ +{ + "name": "backend", + "version": "6.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "start": "nodemon dist/server.js", + "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts", + "db:migrate": "npx sequelize db:migrate", + "db:seed": "sequelize db:seed:all", + "pretest": "NODE_ENV=test sequelize db:migrate && NODE_ENV=test sequelize db:seed:all", + "test": "NODE_ENV=test jest", + "posttest": "NODE_ENV=test sequelize db:migrate:undo:all", + "lint": "eslint src/**/*.ts" + }, + "author": "", + "license": "MIT", + "dependencies": { + "@adiwajshing/keyed-db": "^0.2.4", + "@ffmpeg-installer/ffmpeg": "^1.1.0", + "@hapi/boom": "^9.1.4", + "@sentry/node": "^6.18.1", + "@types/fs-extra": "^11.0.4", + "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys", + "bcryptjs": "^2.4.3", + "bull": "^4.8.2", + "cookie-parser": "^1.4.6", + "cors": "^2.8.5", + "cron": "^2.1.0", + "body-parser":"^1.20.2", + "date-fns": "^2.28.0", + "dotenv": "^16.0.0", + "express": "^4.17.3", + "express-async-errors": "^3.1.1", + "fluent-ffmpeg": "^2.1.2", + "gn-api-sdk-typescript": "^1.0.7", + "http-graceful-shutdown": "^3.1.6", + "jsonwebtoken": "^8.5.1", + "microsoft-cognitiveservices-speech-sdk": "1.31.0", + "multer": "^1.4.4", + "mustache": "^4.2.0", + "mysql2": "^2.2.5", + "node-cache": "^5.1.2", + "node-cron": "^3.0.2", + "nodemailer": "^6.8.0", + "openai": "3.3.0", + "pg": "^8.7.3", + "pino": "^7.8.0", + "pino-pretty": "^10.0.0", + "puppeteer": "^19.4.0", + "qrcode-terminal": "^0.12.0", + "reflect-metadata": "^0.1.13", + "request": "2.88.2", + "sequelize": "^5.22.3", + "sequelize-cli": "^5.5.1", + "sequelize-typescript": "^1.1.0", + "socket.io": "^4.7.4", + "uuid": "^8.3.2", + "xlsx": "^0.18.3", + "yup": "^0.32.11" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.2", + "@types/bluebird": "^3.5.36", + "@types/chance": "^1.1.3", + "@types/cookie-parser": "^1.4.2", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.13", + "@types/factory-girl": "^5.0.8", + "@types/jest": "^27.4.1", + "@types/jsonwebtoken": "^8.5.8", + "@types/multer": "^1.4.7", + "@types/mustache": "^4.1.2", + "@types/node": "^17.0.21", + "@types/supertest": "^2.0.11", + "@types/uuid": "^8.3.4", + "@types/validator": "^13.7.1", + "@types/yup": "^0.29.13", + "@typescript-eslint/eslint-plugin": "^5.13.0", + "@typescript-eslint/parser": "^5.13.0", + "chance": "^1.1.8", + "eslint": "^8.10.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-prettier": "^4.0.0", + "factory-girl": "^5.0.4", + "jest": "^27.5.1", + "nodemon": "^2.0.15", + "prettier": "^2.5.1", + "supertest": "^6.2.2", + "ts-jest": "^27.1.3", + "ts-node-dev": "^1.1.8", + "typescript": "^4.6.3" + } +} diff --git a/backend/prettier.config.js b/backend/prettier.config.js new file mode 100644 index 0000000..2821955 --- /dev/null +++ b/backend/prettier.config.js @@ -0,0 +1,5 @@ +module.exports = { + singleQuote: false, + trailingComma: "none", + arrowParens: "avoid" +}; diff --git a/backend/src/@types/express.d.ts b/backend/src/@types/express.d.ts new file mode 100644 index 0000000..113a3e7 --- /dev/null +++ b/backend/src/@types/express.d.ts @@ -0,0 +1,5 @@ +declare namespace Express { + export interface Request { + user: { id: string; profile: string; companyId: number }; + } +} diff --git a/backend/src/@types/qrcode-terminal.d.ts b/backend/src/@types/qrcode-terminal.d.ts new file mode 100644 index 0000000..3b59fed --- /dev/null +++ b/backend/src/@types/qrcode-terminal.d.ts @@ -0,0 +1 @@ +declare module "qrcode-terminal"; diff --git a/backend/src/app.ts b/backend/src/app.ts new file mode 100644 index 0000000..1eed665 --- /dev/null +++ b/backend/src/app.ts @@ -0,0 +1,53 @@ +import "./bootstrap"; +import "reflect-metadata"; +import "express-async-errors"; +import express, { Request, Response, NextFunction } from "express"; +import cors from "cors"; +import cookieParser from "cookie-parser"; +import * as Sentry from "@sentry/node"; + +import "./database"; +import uploadConfig from "./config/upload"; +import AppError from "./errors/AppError"; +import routes from "./routes"; +import { logger } from "./utils/logger"; +import { messageQueue, sendScheduledMessages } from "./queues"; +import bodyParser from 'body-parser'; + +Sentry.init({ dsn: process.env.SENTRY_DSN }); + +const app = express(); + +app.set("queues", { + messageQueue, + sendScheduledMessages +}); + +const bodyparser = require('body-parser'); +app.use(bodyParser.json({ limit: '10mb' })); + +app.use( + cors({ + credentials: true, + origin: process.env.FRONTEND_URL + }) +); +app.use(cookieParser()); +app.use(express.json()); +app.use(Sentry.Handlers.requestHandler()); +app.use("/public", express.static(uploadConfig.directory)); +app.use(routes); + +app.use(Sentry.Handlers.errorHandler()); + +app.use(async (err: Error, req: Request, res: Response, _: NextFunction) => { + if (err instanceof AppError) { + logger.warn(err); + return res.status(err.statusCode).json({ error: err.message }); + } + + logger.error(err); + return res.status(500).json({ error: "Internal server error" }); +}); + +export default app; diff --git a/backend/src/bootstrap.ts b/backend/src/bootstrap.ts new file mode 100644 index 0000000..03fffb5 --- /dev/null +++ b/backend/src/bootstrap.ts @@ -0,0 +1,5 @@ +import dotenv from "dotenv"; + +dotenv.config({ + path: process.env.NODE_ENV === "test" ? ".env.test" : ".env" +}); diff --git a/backend/src/config/Gn.ts b/backend/src/config/Gn.ts new file mode 100644 index 0000000..d4cbb91 --- /dev/null +++ b/backend/src/config/Gn.ts @@ -0,0 +1,13 @@ +import path from "path"; + +const cert = path.join( + __dirname, + `../../certs/${process.env.GERENCIANET_PIX_CERT}.p12` +); + +export = { + sandbox: false, + client_id: process.env.GERENCIANET_CLIENT_ID as string, + client_secret: process.env.GERENCIANET_CLIENT_SECRET as string, + pix_cert: cert +}; diff --git a/backend/src/config/auth.ts b/backend/src/config/auth.ts new file mode 100644 index 0000000..6f8c5fd --- /dev/null +++ b/backend/src/config/auth.ts @@ -0,0 +1,6 @@ +export default { + secret: process.env.JWT_SECRET || "mysecret", + expiresIn: "15m", + refreshSecret: process.env.JWT_REFRESH_SECRET || "myanothersecret", + refreshExpiresIn: "7d" +}; diff --git a/backend/src/config/database.ts b/backend/src/config/database.ts new file mode 100644 index 0000000..d5e452b --- /dev/null +++ b/backend/src/config/database.ts @@ -0,0 +1,16 @@ +import "../bootstrap"; + +module.exports = { + define: { + charset: "utf8mb4", + collate: "utf8mb4_bin" + }, + dialect: process.env.DB_DIALECT || "mysql", + timezone: "-03:00", + host: process.env.DB_HOST, + port: process.env.DB_PORT || 3306, + database: process.env.DB_NAME, + username: process.env.DB_USER, + password: process.env.DB_PASS, + logging: process.env.DB_DEBUG === "true" +}; diff --git a/backend/src/config/redis.ts b/backend/src/config/redis.ts new file mode 100644 index 0000000..43118e2 --- /dev/null +++ b/backend/src/config/redis.ts @@ -0,0 +1,3 @@ +export const REDIS_URI_CONNECTION = process.env.REDIS_URI || ""; +export const REDIS_OPT_LIMITER_MAX = process.env.REDIS_OPT_LIMITER_MAX || 1; +export const REDIS_OPT_LIMITER_DURATION = process.env.REDIS_OPT_LIMITER_DURATION || 3000; diff --git a/backend/src/config/upload.ts b/backend/src/config/upload.ts new file mode 100644 index 0000000..5a06e12 --- /dev/null +++ b/backend/src/config/upload.ts @@ -0,0 +1,39 @@ +import path from "path"; +import multer from "multer"; +import fs from "fs"; + +const publicFolder = path.resolve(__dirname, "..", "..", "public"); + +export default { + directory: publicFolder, + storage: multer.diskStorage({ + destination: async function (req, file, cb) { + + const { typeArch, fileId } = req.body; + + let folder; + + if (typeArch && typeArch !== "announcements") { + folder = path.resolve(publicFolder , typeArch, fileId ? fileId : "") + } else if (typeArch && typeArch === "announcements") { + folder = path.resolve(publicFolder , typeArch) + } + else + { + folder = path.resolve(publicFolder) + } + + if (!fs.existsSync(folder)) { + fs.mkdirSync(folder, { recursive: true }) + fs.chmodSync(folder, 0o777) + } + return cb(null, folder); + }, + filename(req, file, cb) { + const { typeArch } = req.body; + + const fileName = typeArch && typeArch !== "announcements" ? file.originalname.replace('/','-').replace(/ /g, "_") : new Date().getTime() + '_' + file.originalname.replace('/','-').replace(/ /g, "_"); + return cb(null, fileName); + } + }) +}; diff --git a/backend/src/controllers/AnnouncementController.ts b/backend/src/controllers/AnnouncementController.ts new file mode 100644 index 0000000..3d3a3a4 --- /dev/null +++ b/backend/src/controllers/AnnouncementController.ts @@ -0,0 +1,205 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import { head } from "lodash"; +import fs from "fs"; +import path from "path"; + +import ListService from "../services/AnnouncementService/ListService"; +import CreateService from "../services/AnnouncementService/CreateService"; +import ShowService from "../services/AnnouncementService/ShowService"; +import UpdateService from "../services/AnnouncementService/UpdateService"; +import DeleteService from "../services/AnnouncementService/DeleteService"; +import FindService from "../services/AnnouncementService/FindService"; + +import Announcement from "../models/Announcement"; + +import AppError from "../errors/AppError"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + companyId: string | number; +}; + +type StoreData = { + priority: string; + title: string; + text: string; + status: string; + companyId: number; + mediaPath?: string; + mediaName?: string; +}; + +type FindParams = { + companyId: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { records, count, hasMore } = await ListService({ + searchParam, + pageNumber + }); + + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + title: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await CreateService({ + ...data, + companyId + }); + + const io = getIO(); + io.emit(`company-announcement`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + title: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + id + }); + + const io = getIO(); + io.emit(`company-announcement`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-announcement`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Announcement deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const params = req.query as FindParams; + const records: Announcement[] = await FindService(params); + + return res.status(200).json(records); +}; + +export const mediaUpload = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const files = req.files as Express.Multer.File[]; + const file = head(files); + + try { + const announcement = await Announcement.findByPk(id); + + await announcement.update({ + mediaPath: file.filename, + mediaName: file.originalname + }); + await announcement.reload(); + + const io = getIO(); + io.emit(`company-announcement`, { + action: "update", + record: announcement + }); + + return res.send({ mensagem: "Mensagem enviada" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; + +export const deleteMedia = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + try { + const announcement = await Announcement.findByPk(id); + const filePath = path.resolve("public", announcement.mediaPath); + const fileExists = fs.existsSync(filePath); + if (fileExists) { + fs.unlinkSync(filePath); + } + + await announcement.update({ + mediaPath: null, + mediaName: null + }); + await announcement.reload(); + + const io = getIO(); + io.emit(`company-announcement`, { + action: "update", + record: announcement + }); + + return res.send({ mensagem: "Arquivo excluído" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; diff --git a/backend/src/controllers/CampaignController.ts b/backend/src/controllers/CampaignController.ts new file mode 100644 index 0000000..30e83d9 --- /dev/null +++ b/backend/src/controllers/CampaignController.ts @@ -0,0 +1,287 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import { head } from "lodash"; +import fs from "fs"; +import path from "path"; + +import ListService from "../services/CampaignService/ListService"; +import CreateService from "../services/CampaignService/CreateService"; +import ShowService from "../services/CampaignService/ShowService"; +import UpdateService from "../services/CampaignService/UpdateService"; +import DeleteService from "../services/CampaignService/DeleteService"; +import FindService from "../services/CampaignService/FindService"; + +import Campaign from "../models/Campaign"; + +import AppError from "../errors/AppError"; +import { CancelService } from "../services/CampaignService/CancelService"; +import { RestartService } from "../services/CampaignService/RestartService"; +import TicketTag from "../models/TicketTag"; +import Ticket from "../models/Ticket"; +import Contact from "../models/Contact"; +import ContactList from "../models/ContactList"; +import ContactListItem from "../models/ContactListItem"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + companyId: string | number; +}; + +type StoreData = { + name: string; + status: string; + confirmation: boolean; + scheduledAt: string; + companyId: number; + contactListId: number; + tagListId: number | string; + fileListId: number; +}; + +type FindParams = { + companyId: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + const { companyId } = req.user; + + const { records, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId + }); + + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + if (typeof data.tagListId === 'number') { + + const tagId = data.tagListId; + const campanhaNome = data.name; + + async function createContactListFromTag(tagId) { + + const currentDate = new Date(); + const formattedDate = currentDate.toISOString(); + + try { + const ticketTags = await TicketTag.findAll({ where: { tagId } }); + const ticketIds = ticketTags.map((ticketTag) => ticketTag.ticketId); + + const tickets = await Ticket.findAll({ where: { id: ticketIds } }); + const contactIds = tickets.map((ticket) => ticket.contactId); + + const contacts = await Contact.findAll({ where: { id: contactIds } }); + + const randomName = `${campanhaNome} | TAG: ${tagId} - ${formattedDate}` // Implement your own function to generate a random name + const contactList = await ContactList.create({ name: randomName, companyId: companyId }); + + const { id: contactListId } = contactList; + + const contactListItems = contacts.map((contact) => ({ + name: contact.name, + number: contact.number, + email: contact.email, + contactListId, + companyId, + isWhatsappValid: true, + + })); + + await ContactListItem.bulkCreate(contactListItems); + + // Return the ContactList ID + return contactListId; + } catch (error) { + console.error('Error creating contact list:', error); + throw error; + } + } + + + createContactListFromTag(tagId) + .then(async (contactListId) => { + const record = await CreateService({ + ...data, + companyId, + contactListId: contactListId, + }); + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, { + action: "create", + record + }); + return res.status(200).json(record); + }) + .catch((error) => { + console.error('Error:', error); + return res.status(500).json({ error: 'Error creating contact list' }); + }); + + } else { // SAI DO CHECK DE TAG + + + const record = await CreateService({ + ...data, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, { + action: "create", + record + }); + + return res.status(200).json(record); + } +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + id + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const cancel = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + await CancelService(+id); + + return res.status(204).json({ message: "Cancelamento realizado" }); +}; + +export const restart = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + await RestartService(+id); + + return res.status(204).json({ message: "Reinício dos disparos" }); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Campaign deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const params = req.query as FindParams; + const records: Campaign[] = await FindService(params); + + return res.status(200).json(records); +}; + +export const mediaUpload = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const files = req.files as Express.Multer.File[]; + const file = head(files); + + try { + const campaign = await Campaign.findByPk(id); + campaign.mediaPath = file.filename; + campaign.mediaName = file.originalname; + await campaign.save(); + return res.send({ mensagem: "Mensagem enviada" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; + +export const deleteMedia = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + try { + const campaign = await Campaign.findByPk(id); + const filePath = path.resolve("public", campaign.mediaPath); + const fileExists = fs.existsSync(filePath); + if (fileExists) { + fs.unlinkSync(filePath); + } + + campaign.mediaPath = null; + campaign.mediaName = null; + await campaign.save(); + return res.send({ mensagem: "Arquivo excluído" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; diff --git a/backend/src/controllers/CampaignSettingController.ts b/backend/src/controllers/CampaignSettingController.ts new file mode 100644 index 0000000..860dfaf --- /dev/null +++ b/backend/src/controllers/CampaignSettingController.ts @@ -0,0 +1,34 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListService from "../services/CampaignSettingServices/ListService"; +import CreateService from "../services/CampaignSettingServices/CreateService"; + +interface StoreData { + settings: any; +} + +export const index = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + + const records = await ListService({ + companyId + }); + + return res.json(records); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const record = await CreateService(data, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaignSettings`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; diff --git a/backend/src/controllers/ChatController.ts b/backend/src/controllers/ChatController.ts new file mode 100644 index 0000000..6165181 --- /dev/null +++ b/backend/src/controllers/ChatController.ts @@ -0,0 +1,205 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import CreateService from "../services/ChatService/CreateService"; +import ListService from "../services/ChatService/ListService"; +import ShowFromUuidService from "../services/ChatService/ShowFromUuidService"; +import DeleteService from "../services/ChatService/DeleteService"; +import FindMessages from "../services/ChatService/FindMessages"; +import UpdateService from "../services/ChatService/UpdateService"; + +import Chat from "../models/Chat"; +import CreateMessageService from "../services/ChatService/CreateMessageService"; +import User from "../models/User"; +import ChatUser from "../models/ChatUser"; + +type IndexQuery = { + pageNumber: string; + companyId: string | number; + ownerId?: number; +}; + +type StoreData = { + users: any[]; + title: string; +}; + +type FindParams = { + companyId: number; + ownerId?: number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { pageNumber } = req.query as unknown as IndexQuery; + const ownerId = +req.user.id; + + const { records, count, hasMore } = await ListService({ + ownerId, + pageNumber + }); + + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const ownerId = +req.user.id; + const data = req.body as StoreData; + + const record = await CreateService({ + ...data, + ownerId, + companyId + }); + + const io = getIO(); + + record.users.forEach(user => { + io.to(`user-${user.userId}`).emit(`company-${companyId}-chat-user-${user.userId}`, { + action: "create", + record + }); + }); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { companyId } = req.user; + const data = req.body; + const { id } = req.params; + + const record = await UpdateService({ + ...data, + id: +id + }); + + const io = getIO(); + + record.users.forEach(user => { + io.to(`user-${user.userId}`).emit(`company-${companyId}-chat-user-${user.userId}`, { + action: "update", + record + }); + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowFromUuidService(id); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Chat deleted" }); +}; + +export const saveMessage = async ( + req: Request, + res: Response +): Promise => { + const { companyId } = req.user; + const { message } = req.body; + const { id } = req.params; + const senderId = +req.user.id; + const chatId = +id; + + const newMessage = await CreateMessageService({ + chatId, + senderId, + message + }); + + const chat = await Chat.findByPk(chatId, { + include: [ + { model: User, as: "owner" }, + { model: ChatUser, as: "users" } + ] + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat-${chatId}`, { + action: "new-message", + newMessage, + chat + }); + + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat`, { + action: "new-message", + newMessage, + chat + }); + + return res.json(newMessage); +}; + +export const checkAsRead = async ( + req: Request, + res: Response +): Promise => { + const { companyId } = req.user; + const { userId } = req.body; + const { id } = req.params; + + const chatUser = await ChatUser.findOne({ where: { chatId: id, userId } }); + await chatUser.update({ unreads: 0 }); + + const chat = await Chat.findByPk(id, { + include: [ + { model: User, as: "owner" }, + { model: ChatUser, as: "users" } + ] + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat-${id}`, { + action: "update", + chat + }); + + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat`, { + action: "update", + chat + }); + + return res.json(chat); +}; + +export const messages = async ( + req: Request, + res: Response +): Promise => { + const { pageNumber } = req.query as unknown as IndexQuery; + const { id: chatId } = req.params; + const ownerId = +req.user.id; + + const { records, count, hasMore } = await FindMessages({ + chatId, + ownerId, + pageNumber + }); + + return res.json({ records, count, hasMore }); +}; diff --git a/backend/src/controllers/CompanyController.ts b/backend/src/controllers/CompanyController.ts new file mode 100644 index 0000000..7bdcc68 --- /dev/null +++ b/backend/src/controllers/CompanyController.ts @@ -0,0 +1,180 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +// import { getIO } from "../libs/socket"; +import AppError from "../errors/AppError"; +import Company from "../models/Company"; +import authConfig from "../config/auth"; + +import ListCompaniesService from "../services/CompanyService/ListCompaniesService"; +import CreateCompanyService from "../services/CompanyService/CreateCompanyService"; +import UpdateCompanyService from "../services/CompanyService/UpdateCompanyService"; +import ShowCompanyService from "../services/CompanyService/ShowCompanyService"; +import UpdateSchedulesService from "../services/CompanyService/UpdateSchedulesService"; +import DeleteCompanyService from "../services/CompanyService/DeleteCompanyService"; +import FindAllCompaniesService from "../services/CompanyService/FindAllCompaniesService"; +import { verify } from "jsonwebtoken"; +import User from "../models/User"; +import ShowPlanCompanyService from "../services/CompanyService/ShowPlanCompanyService"; +import ListCompaniesPlanService from "../services/CompanyService/ListCompaniesPlanService"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +interface TokenPayload { + id: string; + username: string; + profile: string; + companyId: number; + iat: number; + exp: number; +} + +type CompanyData = { + name: string; + id?: number; + phone?: string; + email?: string; + status?: boolean; + planId?: number; + campaignsEnabled?: boolean; + dueDate?: string; + recurrence?: string; +}; + +type SchedulesData = { + schedules: []; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { companies, count, hasMore } = await ListCompaniesService({ + searchParam, + pageNumber + }); + + return res.json({ companies, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const newCompany: CompanyData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(newCompany); + } catch (err: any) { + throw new AppError(err.message); + } + + const company = await CreateCompanyService(newCompany); + + return res.status(200).json(company); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const company = await ShowCompanyService(id); + + return res.status(200).json(company); +}; + +export const list = async (req: Request, res: Response): Promise => { + const companies: Company[] = await FindAllCompaniesService(); + + return res.status(200).json(companies); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const companyData: CompanyData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string() + }); + + try { + await schema.validate(companyData); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const company = await UpdateCompanyService({ id, ...companyData }); + + return res.status(200).json(company); +}; + +export const updateSchedules = async ( + req: Request, + res: Response +): Promise => { + const { schedules }: SchedulesData = req.body; + const { id } = req.params; + + const company = await UpdateSchedulesService({ + id, + schedules + }); + + return res.status(200).json(company); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + const company = await DeleteCompanyService(id); + + return res.status(200).json(company); +}; + +export const listPlan = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { id: requestUserId, profile, companyId } = decoded as TokenPayload; + const requestUser = await User.findByPk(requestUserId); + + if (requestUser.super === true) { + const company = await ShowPlanCompanyService(id); + return res.status(200).json(company); + } else if (companyId.toString() !== id) { + return res.status(400).json({ error: "Você não possui permissão para acessar este recurso!" }); + } else { + const company = await ShowPlanCompanyService(id); + return res.status(200).json(company); + } + +}; + +export const indexPlan = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { id, profile, companyId } = decoded as TokenPayload; + // const company = await Company.findByPk(companyId); + const requestUser = await User.findByPk(id); + + if (requestUser.super === true) { + const companies = await ListCompaniesPlanService(); + return res.json({ companies }); + } else { + return res.status(400).json({ error: "Você não possui permissão para acessar este recurso!" }); + } + +}; \ No newline at end of file diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts new file mode 100644 index 0000000..08ae136 --- /dev/null +++ b/backend/src/controllers/ContactController.ts @@ -0,0 +1,193 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListContactsService from "../services/ContactServices/ListContactsService"; +import CreateContactService from "../services/ContactServices/CreateContactService"; +import ShowContactService from "../services/ContactServices/ShowContactService"; +import UpdateContactService from "../services/ContactServices/UpdateContactService"; +import DeleteContactService from "../services/ContactServices/DeleteContactService"; +import GetContactService from "../services/ContactServices/GetContactService"; + +import CheckContactNumber from "../services/WbotServices/CheckNumber"; +import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact"; +import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl"; +import AppError from "../errors/AppError"; +import SimpleListService, { + SearchContactParams +} from "../services/ContactServices/SimpleListService"; +import ContactCustomField from "../models/ContactCustomField"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +type IndexGetContactQuery = { + name: string; + number: string; +}; + +interface ExtraInfo extends ContactCustomField { + name: string; + value: string; +} +interface ContactData { + name: string; + number: string; + email?: string; + extraInfo?: ExtraInfo[]; +} + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + const { companyId } = req.user; + + const { contacts, count, hasMore } = await ListContactsService({ + searchParam, + pageNumber, + companyId + }); + + return res.json({ contacts, count, hasMore }); +}; + +export const getContact = async ( + req: Request, + res: Response +): Promise => { + const { name, number } = req.body as IndexGetContactQuery; + const { companyId } = req.user; + + const contact = await GetContactService({ + name, + number, + companyId + }); + + return res.status(200).json(contact); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const newContact: ContactData = req.body; + newContact.number = newContact.number.replace("-", "").replace(" ", ""); + + const schema = Yup.object().shape({ + name: Yup.string().required(), + number: Yup.string() + .required() + .matches(/^\d+$/, "Invalid number format. Only numbers is allowed.") + }); + + try { + await schema.validate(newContact); + } catch (err: any) { + throw new AppError(err.message); + } + + await CheckIsValidContact(newContact.number, companyId); + const validNumber = await CheckContactNumber(newContact.number, companyId); + const number = validNumber.jid.replace(/\D/g, ""); + newContact.number = number; + + /** + * Código desabilitado por demora no retorno + */ + // const profilePicUrl = await GetProfilePicUrl(validNumber.jid, companyId); + + const contact = await CreateContactService({ + ...newContact, + // profilePicUrl, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, { + action: "create", + contact + }); + + return res.status(200).json(contact); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { contactId } = req.params; + const { companyId } = req.user; + + const contact = await ShowContactService(contactId, companyId); + + return res.status(200).json(contact); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const contactData: ContactData = req.body; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + name: Yup.string(), + number: Yup.string().matches( + /^\d+$/, + "Invalid number format. Only numbers is allowed." + ) + }); + + try { + await schema.validate(contactData); + } catch (err: any) { + throw new AppError(err.message); + } + + await CheckIsValidContact(contactData.number, companyId); + const validNumber = await CheckContactNumber(contactData.number, companyId); + const number = validNumber.jid.replace(/\D/g, ""); + contactData.number = number; + + const { contactId } = req.params; + + const contact = await UpdateContactService({ + contactData, + contactId, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, { + action: "update", + contact + }); + + return res.status(200).json(contact); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { contactId } = req.params; + const { companyId } = req.user; + + await ShowContactService(contactId, companyId); + + await DeleteContactService(contactId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, { + action: "delete", + contactId + }); + + return res.status(200).json({ message: "Contact deleted" }); +}; + +export const list = async (req: Request, res: Response): Promise => { + const { name } = req.query as unknown as SearchContactParams; + const { companyId } = req.user; + + const contacts = await SimpleListService({ name, companyId }); + + return res.json(contacts); +}; diff --git a/backend/src/controllers/ContactListController.ts b/backend/src/controllers/ContactListController.ts new file mode 100644 index 0000000..ba1b422 --- /dev/null +++ b/backend/src/controllers/ContactListController.ts @@ -0,0 +1,159 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListService from "../services/ContactListService/ListService"; +import CreateService from "../services/ContactListService/CreateService"; +import ShowService from "../services/ContactListService/ShowService"; +import UpdateService from "../services/ContactListService/UpdateService"; +import DeleteService from "../services/ContactListService/DeleteService"; +import FindService from "../services/ContactListService/FindService"; +import { head } from "lodash"; + +import ContactList from "../models/ContactList"; + +import AppError from "../errors/AppError"; +import { ImportContacts } from "../services/ContactListService/ImportContacts"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + companyId: string | number; +}; + +type StoreData = { + name: string; + companyId: string; +}; + +type FindParams = { + companyId: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + const { companyId } = req.user; + + const { records, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId + }); + + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await CreateService({ + ...data, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactList`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + id + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactList`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactList`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Contact list deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const params = req.query as FindParams; + const records: ContactList[] = await FindService(params); + + return res.status(200).json(records); +}; + +export const upload = async (req: Request, res: Response) => { + const files = req.files as Express.Multer.File[]; + const file: Express.Multer.File = head(files) as Express.Multer.File; + const { id } = req.params; + const { companyId } = req.user; + + const response = await ImportContacts(+id, companyId, file); + + const io = getIO(); + + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem-${+id}`, { + action: "reload", + records: response + }); + + return res.status(200).json(response); +}; diff --git a/backend/src/controllers/ContactListItemController.ts b/backend/src/controllers/ContactListItemController.ts new file mode 100644 index 0000000..56295be --- /dev/null +++ b/backend/src/controllers/ContactListItemController.ts @@ -0,0 +1,145 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListService from "../services/ContactListItemService/ListService"; +import CreateService from "../services/ContactListItemService/CreateService"; +import ShowService from "../services/ContactListItemService/ShowService"; +import UpdateService from "../services/ContactListItemService/UpdateService"; +import DeleteService from "../services/ContactListItemService/DeleteService"; +import FindService from "../services/ContactListItemService/FindService"; + +import ContactListItem from "../models/ContactListItem"; + +import AppError from "../errors/AppError"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + companyId: string | number; + contactListId: string | number; +}; + +type StoreData = { + name: string; + number: string; + contactListId: number; + companyId?: string; + email?: string; +}; + +type FindParams = { + companyId: number; + contactListId: number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber, contactListId } = req.query as IndexQuery; + const { companyId } = req.user; + + const { contacts, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId, + contactListId + }); + + return res.json({ contacts, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await CreateService({ + ...data, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + id + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Contact deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const params = req.query as unknown as FindParams; + const records: ContactListItem[] = await FindService(params); + + return res.status(200).json(records); +}; diff --git a/backend/src/controllers/DashbardController.ts b/backend/src/controllers/DashbardController.ts new file mode 100644 index 0000000..e0527f5 --- /dev/null +++ b/backend/src/controllers/DashbardController.ts @@ -0,0 +1,42 @@ +import { Request, Response } from "express"; +import DashboardDataService, { DashboardData, Params } from "../services/ReportService/DashbardDataService"; +import { TicketsAttendance } from "../services/ReportService/TicketsAttendance"; +import { TicketsDayService } from "../services/ReportService/TicketsDayService"; + +type IndexQuery = { + initialDate: string; + finalDate: string; + companyId: number | any; +}; + +export const index = async (req: Request, res: Response): Promise => { + const params: Params = req.query; + const { companyId } = req.user; + let daysInterval = 3; + + const dashboardData: DashboardData = await DashboardDataService( + companyId, + params + ); + return res.status(200).json(dashboardData); +}; + +export const reportsUsers = async (req: Request, res: Response): Promise => { + + const { initialDate, finalDate, companyId } = req.query as IndexQuery + + const { data } = await TicketsAttendance({ initialDate, finalDate, companyId }); + + return res.json({ data }); + +} + +export const reportsDay = async (req: Request, res: Response): Promise => { + + const { initialDate, finalDate, companyId } = req.query as IndexQuery + + const { count, data } = await TicketsDayService({ initialDate, finalDate, companyId }); + + return res.json({ count, data }); + +} diff --git a/backend/src/controllers/FilesController.ts b/backend/src/controllers/FilesController.ts new file mode 100644 index 0000000..ee54e26 --- /dev/null +++ b/backend/src/controllers/FilesController.ts @@ -0,0 +1,155 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import AppError from "../errors/AppError"; +import { head } from "lodash"; + +import CreateService from "../services/FileServices/CreateService"; +import ListService from "../services/FileServices/ListService"; +import UpdateService from "../services/FileServices/UpdateService"; +import ShowService from "../services/FileServices/ShowService"; +import DeleteService from "../services/FileServices/DeleteService"; +import SimpleListService from "../services/FileServices/SimpleListService"; +import DeleteAllService from "../services/FileServices/DeleteAllService"; +import ShowTicketService from "../services/TicketServices/ShowTicketService"; +import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; +import FilesOptions from "../models/FilesOptions"; + +type IndexQuery = { + searchParam?: string; + pageNumber?: string | number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { pageNumber, searchParam } = req.query as IndexQuery; + const { companyId } = req.user; + + const { files, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId + }); + + return res.json({ files, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { name, message, options } = req.body; + const { companyId } = req.user; + + const fileList = await CreateService({ + name, + message, + options, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company${companyId}-file`, { + action: "create", + fileList + }); + + return res.status(200).json(fileList); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { fileId } = req.params; + const { companyId } = req.user; + + const file = await ShowService(fileId, companyId); + + return res.status(200).json(file); +}; + +export const uploadMedias = async (req: Request, res: Response): Promise => { + const { fileId, id, mediaType } = req.body; + const files = req.files as Express.Multer.File[]; + const file = head(files); + + try { + + let fileOpt + if (files.length > 0) { + + for (const [index, file] of files.entries()) { + fileOpt = await FilesOptions.findOne({ + where: { + fileId, + id: Array.isArray(id)? id[index] : id + } + }); + + fileOpt.update({ + path: file.filename.replace('/','-'), + mediaType: Array.isArray(mediaType)? mediaType[index] : mediaType + }) ; + } + } + + return res.send({ mensagem: "Arquivos atualizados" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + const { fileId } = req.params; + const fileData = req.body; + const { companyId } = req.user; + + const fileList = await UpdateService({ fileData, id: fileId, companyId }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company${companyId}-file`, { + action: "update", + fileList + }); + + return res.status(200).json(fileList); +}; + + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { fileId } = req.params; + const { companyId } = req.user; + + await DeleteService(fileId, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company${companyId}-file`, { + action: "delete", + fileId + }); + + return res.status(200).json({ message: "File List deleted" }); +}; + +export const removeAll = async ( + req: Request, + res: Response +): Promise => { + const { companyId } = req.user; + await DeleteAllService(companyId); + + return res.send(); +}; + +export const list = async (req: Request, res: Response): Promise => { + const { searchParam } = req.query as IndexQuery; + const { companyId } = req.user; + + const ratings = await SimpleListService({ searchParam, companyId }); + + return res.json(ratings); +}; diff --git a/backend/src/controllers/ForgotController.ts b/backend/src/controllers/ForgotController.ts new file mode 100644 index 0000000..121efb2 --- /dev/null +++ b/backend/src/controllers/ForgotController.ts @@ -0,0 +1,25 @@ +import { v4 as uuid } from "uuid"; +import { Request, Response } from "express"; +import SendMail from "../services/ForgotPassWordServices/SendMail"; +import ResetPassword from "../services/ResetPasswordService/ResetPassword"; +type IndexQuery = { email?: string; token?: string; password?: string }; +export const store = async (req: Request, res: Response): Promise => { + const { email } = req.params as IndexQuery; + const TokenSenha = uuid(); + const forgotPassword = await SendMail(email, TokenSenha); + if (!forgotPassword) { + return res.status(200).json({ message: "E-mail enviado com sucesso" }); + } + return res.status(404).json({ error: "E-mail enviado com sucesso" }); +}; +export const resetPasswords = async ( + req: Request, + res: Response +): Promise => { + const { email, token, password } = req.params as IndexQuery; + const resetPassword = await ResetPassword(email, token, password); + if (!resetPassword) { + return res.status(200).json({ message: "Senha redefinida com sucesso" }); + } + return res.status(404).json({ error: "Verifique o Token informado" }); +}; diff --git a/backend/src/controllers/HelpController.ts b/backend/src/controllers/HelpController.ts new file mode 100644 index 0000000..e6bb521 --- /dev/null +++ b/backend/src/controllers/HelpController.ts @@ -0,0 +1,131 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListService from "../services/HelpServices/ListService"; +import CreateService from "../services/HelpServices/CreateService"; +import ShowService from "../services/HelpServices/ShowService"; +import UpdateService from "../services/HelpServices/UpdateService"; +import DeleteService from "../services/HelpServices/DeleteService"; +import FindService from "../services/HelpServices/FindService"; + +import Help from "../models/Help"; + +import AppError from "../errors/AppError"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +type StoreData = { + title: string; + description: string; + video?: string; + link?: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { records, count, hasMore } = await ListService({ + searchParam, + pageNumber + }); + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + title: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err) { + throw new AppError(err.message); + } + + const record = await CreateService({ + ...data + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-help`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + title: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + id + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-help`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-help`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Help deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const records: Help[] = await FindService(); + + return res.status(200).json(records); +}; diff --git a/backend/src/controllers/ImportPhoneContactsController.ts b/backend/src/controllers/ImportPhoneContactsController.ts new file mode 100644 index 0000000..a1084f6 --- /dev/null +++ b/backend/src/controllers/ImportPhoneContactsController.ts @@ -0,0 +1,10 @@ +import { Request, Response } from "express"; +import ImportContactsService from "../services/WbotServices/ImportContactsService"; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + + await ImportContactsService(companyId); + + return res.status(200).json({ message: "contacts imported" }); +}; diff --git a/backend/src/controllers/InvoicesController.ts b/backend/src/controllers/InvoicesController.ts new file mode 100644 index 0000000..44180c8 --- /dev/null +++ b/backend/src/controllers/InvoicesController.ts @@ -0,0 +1,172 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +// import { getIO } from "../libs/socket"; +import AppError from "../errors/AppError"; +import Invoices from "../models/Invoices"; + +import CreatePlanService from "../services/PlanService/CreatePlanService"; +import UpdatePlanService from "../services/PlanService/UpdatePlanService"; +import ShowPlanService from "../services/PlanService/ShowPlanService"; +import DeletePlanService from "../services/PlanService/DeletePlanService"; + +import FindAllInvoiceService from "../services/InvoicesService/FindAllInvoiceService"; +import ListInvoicesServices from "../services/InvoicesService/ListInvoicesServices"; +import ShowInvoceService from "../services/InvoicesService/ShowInvoiceService"; +import UpdateInvoiceService from "../services/InvoicesService/UpdateInvoiceService"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +type StorePlanData = { + name: string; + id?: number | string; + users: number | 0; + connections: number | 0; + queues: number | 0; + value: number; +}; + +type UpdateInvoiceData = { + status: string; + id?: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { invoices, count, hasMore } = await ListInvoicesServices({ + searchParam, + pageNumber + }); + + return res.json({ invoices, count, hasMore }); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { Invoiceid } = req.params; + + const invoice = await ShowInvoceService(Invoiceid); + + return res.status(200).json(invoice); +}; + + +export const list = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const invoice: Invoices[] = await FindAllInvoiceService(companyId); + + return res.status(200).json(invoice); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const InvoiceData: UpdateInvoiceData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string() + }); + + try { + await schema.validate(InvoiceData); + } catch (err) { + throw new AppError(err.message); + } + + const { id, status } = InvoiceData; + + const plan = await UpdateInvoiceService({ + id, + status, + + }); + + // const io = getIO(); + // io.emit("plan", { + // action: "update", + // plan + // }); + + return res.status(200).json(plan); +}; +/* export const store = async (req: Request, res: Response): Promise => { + const newPlan: StorePlanData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(newPlan); + } catch (err) { + throw new AppError(err.message); + } + + const plan = await CreatePlanService(newPlan); + + // const io = getIO(); + // io.emit("plan", { + // action: "create", + // plan + // }); + + return res.status(200).json(plan); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const plan = await ShowPlanService(id); + + return res.status(200).json(plan); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const planData: UpdateInvoiceData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string() + }); + + try { + await schema.validate(planData); + } catch (err) { + throw new AppError(err.message); + } + + const { id, name, users, connections, queues, value } = planData; + + const plan = await UpdatePlanService({ + id, + name, + users, + connections, + queues, + value + }); + + // const io = getIO(); + // io.emit("plan", { + // action: "update", + // plan + // }); + + return res.status(200).json(plan); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + const plan = await DeletePlanService(id); + + return res.status(200).json(plan); +}; */ diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts new file mode 100644 index 0000000..f6f132a --- /dev/null +++ b/backend/src/controllers/MessageController.ts @@ -0,0 +1,192 @@ +import { Request, Response } from "express"; +import AppError from "../errors/AppError"; + +import SetTicketMessagesAsRead from "../helpers/SetTicketMessagesAsRead"; +import { getIO } from "../libs/socket"; +import Message from "../models/Message"; +import Queue from "../models/Queue"; +import User from "../models/User"; +import Whatsapp from "../models/Whatsapp"; +import formatBody from "../helpers/Mustache"; + +import ListMessagesService from "../services/MessageServices/ListMessagesService"; +import ShowTicketService from "../services/TicketServices/ShowTicketService"; +import FindOrCreateTicketService from "../services/TicketServices/FindOrCreateTicketService"; +import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; +import DeleteWhatsAppMessage from "../services/WbotServices/DeleteWhatsAppMessage"; +import SendWhatsAppMedia from "../services/WbotServices/SendWhatsAppMedia"; +import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage"; +import CheckContactNumber from "../services/WbotServices/CheckNumber"; +import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact"; +import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl"; +import CreateOrUpdateContactService from "../services/ContactServices/CreateOrUpdateContactService"; +type IndexQuery = { + pageNumber: string; +}; + +type MessageData = { + body: string; + fromMe: boolean; + read: boolean; + quotedMsg?: Message; + number?: string; + closeTicket?: true; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { ticketId } = req.params; + const { pageNumber } = req.query as IndexQuery; + const { companyId, profile } = req.user; + const queues: number[] = []; + + if (profile !== "admin") { + const user = await User.findByPk(req.user.id, { + include: [{ model: Queue, as: "queues" }] + }); + user.queues.forEach(queue => { + queues.push(queue.id); + }); + } + + const { count, messages, ticket, hasMore } = await ListMessagesService({ + pageNumber, + ticketId, + companyId, + queues + }); + + SetTicketMessagesAsRead(ticket); + + return res.json({ count, messages, ticket, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { ticketId } = req.params; + const { body, quotedMsg }: MessageData = req.body; + const medias = req.files as Express.Multer.File[]; + const { companyId } = req.user; + + const ticket = await ShowTicketService(ticketId, companyId); + + SetTicketMessagesAsRead(ticket); + + if (medias) { + await Promise.all( + medias.map(async (media: Express.Multer.File, index) => { + await SendWhatsAppMedia({ media, ticket, body: Array.isArray(body) ? body[index] : body }); + }) + ); + } else { + const send = await SendWhatsAppMessage({ body, ticket, quotedMsg }); + } + + return res.send(); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { messageId } = req.params; + const { companyId } = req.user; + + const message = await DeleteWhatsAppMessage(messageId); + + const io = getIO(); + io.to(message.ticketId.toString()).emit(`company-${companyId}-appMessage`, { + action: "update", + message + }); + + return res.send(); +}; + +export const send = async (req: Request, res: Response): Promise => { + const { whatsappId } = req.params as unknown as { whatsappId: number }; + const messageData: MessageData = req.body; + const medias = req.files as Express.Multer.File[]; + + try { + const whatsapp = await Whatsapp.findByPk(whatsappId); + + if (!whatsapp) { + throw new Error("Não foi possível realizar a operação"); + } + + if (messageData.number === undefined) { + throw new Error("O número é obrigatório"); + } + + const numberToTest = messageData.number; + const body = messageData.body; + + const companyId = whatsapp.companyId; + + const CheckValidNumber = await CheckContactNumber(numberToTest, companyId); + const number = CheckValidNumber.jid.replace(/\D/g, ""); + const profilePicUrl = await GetProfilePicUrl( + number, + companyId + ); + const contactData = { + name: `${number}`, + number, + profilePicUrl, + isGroup: false, + companyId + }; + + const contact = await CreateOrUpdateContactService(contactData); + + const ticket = await FindOrCreateTicketService(contact, whatsapp.id!, 0, companyId); + + if (medias) { + await Promise.all( + medias.map(async (media: Express.Multer.File) => { + await req.app.get("queues").messageQueue.add( + "SendMessage", + { + whatsappId, + data: { + number, + body: body ? formatBody(body, contact) : media.originalname, + mediaPath: media.path, + fileName: media.originalname + } + }, + { removeOnComplete: true, attempts: 3 } + ); + }) + ); + } else { + await SendWhatsAppMessage({ body: formatBody(body, contact), ticket }); + + await ticket.update({ + lastMessage: body, + }); + + } + + if (messageData.closeTicket) { + setTimeout(async () => { + await UpdateTicketService({ + ticketId: ticket.id, + ticketData: { status: "closed" }, + companyId + }); + }, 1000); + } + + SetTicketMessagesAsRead(ticket); + + return res.send({ mensagem: "Mensagem enviada" }); + } catch (err: any) { + if (Object.keys(err).length === 0) { + throw new AppError( + "Não foi possível enviar a mensagem, tente novamente em alguns instantes" + ); + } else { + throw new AppError(err.message); + } + } +}; diff --git a/backend/src/controllers/PlanController.ts b/backend/src/controllers/PlanController.ts new file mode 100644 index 0000000..85e071f --- /dev/null +++ b/backend/src/controllers/PlanController.ts @@ -0,0 +1,136 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +// import { getIO } from "../libs/socket"; +import AppError from "../errors/AppError"; +import Plan from "../models/Plan"; + +import ListPlansService from "../services/PlanService/ListPlansService"; +import CreatePlanService from "../services/PlanService/CreatePlanService"; +import UpdatePlanService from "../services/PlanService/UpdatePlanService"; +import ShowPlanService from "../services/PlanService/ShowPlanService"; +import FindAllPlanService from "../services/PlanService/FindAllPlanService"; +import DeletePlanService from "../services/PlanService/DeletePlanService"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +type StorePlanData = { + name: string; + id?: number | string; + users: number | 0; + connections: number | 0; + queues: number | 0; + value: number; + useCampaigns?: boolean; + useSchedules?: boolean; + useInternalChat?: boolean; + useExternalApi?: boolean; + useKanban?: boolean; + useOpenAi?: boolean; + useIntegrations?: boolean; +}; + +type UpdatePlanData = { + name: string; + id?: number | string; + users?: number; + connections?: number; + queues?: number; + value?: number; + useCampaigns?: boolean; + useSchedules?: boolean; + useInternalChat?: boolean; + useExternalApi?: boolean; + useKanban?: boolean; + useOpenAi?: boolean; + useIntegrations?: boolean; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { plans, count, hasMore } = await ListPlansService({ + searchParam, + pageNumber + }); + + return res.json({ plans, count, hasMore }); +}; + +export const list = async (req: Request, res: Response): Promise => { + const plans: Plan[] = await FindAllPlanService(); + + return res.status(200).json(plans); +}; + +export const store = async (req: Request, res: Response): Promise => { + const newPlan: StorePlanData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string().required() + }); + + try { + await schema.validate(newPlan); + } catch (err) { + throw new AppError(err.message); + } + + const plan = await CreatePlanService(newPlan); + + // const io = getIO(); + // io.emit("plan", { + // action: "create", + // plan + // }); + + return res.status(200).json(plan); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const plan = await ShowPlanService(id); + + return res.status(200).json(plan); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const planData: UpdatePlanData = req.body; + + const schema = Yup.object().shape({ + name: Yup.string() + }); + + try { + await schema.validate(planData); + } catch (err) { + throw new AppError(err.message); + } + + const plan = await UpdatePlanService(planData); + + // const io = getIO(); + // io.emit("plan", { + // action: "update", + // plan + // }); + + return res.status(200).json(plan); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + const plan = await DeletePlanService(id); + + return res.status(200).json(plan); +}; diff --git a/backend/src/controllers/PromptController.ts b/backend/src/controllers/PromptController.ts new file mode 100644 index 0000000..d6969ba --- /dev/null +++ b/backend/src/controllers/PromptController.ts @@ -0,0 +1,114 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import CreatePromptService from "../services/PromptServices/CreatePromptService"; +import DeletePromptService from "../services/PromptServices/DeletePromptService"; +import ListPromptsService from "../services/PromptServices/ListPromptsService"; +import ShowPromptService from "../services/PromptServices/ShowPromptService"; +import UpdatePromptService from "../services/PromptServices/UpdatePromptService"; +import Whatsapp from "../models/Whatsapp"; +import { verify } from "jsonwebtoken"; +import authConfig from "../config/auth"; + +interface TokenPayload { + id: string; + username: string; + profile: string; + companyId: number; + iat: number; + exp: number; +} + +type IndexQuery = { + searchParam?: string; + pageNumber?: string | number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { pageNumber, searchParam } = req.query as IndexQuery; + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { companyId } = decoded as TokenPayload; + const { prompts, count, hasMore } = await ListPromptsService({ searchParam, pageNumber, companyId }); + + return res.status(200).json({ prompts, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { companyId } = decoded as TokenPayload; + const { name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages,voice,voiceKey,voiceRegion } = req.body; + const promptTable = await CreatePromptService({ name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages, companyId,voice,voiceKey,voiceRegion }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("prompt", { + action: "update", + prompt: promptTable + }); + + return res.status(200).json(promptTable); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { promptId } = req.params; + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { companyId } = decoded as TokenPayload; + const prompt = await ShowPromptService({ promptId, companyId }); + + return res.status(200).json(prompt); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { promptId } = req.params; + const promptData = req.body; + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { companyId } = decoded as TokenPayload; + + const prompt = await UpdatePromptService({ promptData, promptId: promptId, companyId }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("prompt", { + action: "update", + prompt + }); + + return res.status(200).json(prompt); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { promptId } = req.params; + const authHeader = req.headers.authorization; + const [, token] = authHeader.split(" "); + const decoded = verify(token, authConfig.secret); + const { companyId } = decoded as TokenPayload; + try { + const { count } = await Whatsapp.findAndCountAll({ where: { promptId: +promptId, companyId } }); + + if (count > 0) return res.status(200).json({ message: "Não foi possível excluir! Verifique se este prompt está sendo usado nas conexões Whatsapp!" }); + + await DeletePromptService(promptId, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("prompt", { + action: "delete", + intelligenceId: +promptId + }); + + return res.status(200).json({ message: "Prompt deleted" }); + } catch (err) { + return res.status(500).json({ message: "Não foi possível excluir! Verifique se este prompt está sendo usado!" }); + } +}; + diff --git a/backend/src/controllers/QueueController.ts b/backend/src/controllers/QueueController.ts new file mode 100644 index 0000000..7a0ebd2 --- /dev/null +++ b/backend/src/controllers/QueueController.ts @@ -0,0 +1,107 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import CreateQueueService from "../services/QueueService/CreateQueueService"; +import DeleteQueueService from "../services/QueueService/DeleteQueueService"; +import ListQueuesService from "../services/QueueService/ListQueuesService"; +import ShowQueueService from "../services/QueueService/ShowQueueService"; +import UpdateQueueService from "../services/QueueService/UpdateQueueService"; +import { isNil } from "lodash"; + +type QueueFilter = { + companyId: number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { companyId: userCompanyId } = req.user; + const { companyId: queryCompanyId } = req.query as unknown as QueueFilter; + let companyId = userCompanyId; + + if (!isNil(queryCompanyId)) { + companyId = +queryCompanyId; + } + + const queues = await ListQueuesService({ companyId }); + + return res.status(200).json(queues); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { name, color, greetingMessage, outOfHoursMessage, schedules, orderQueue, integrationId, promptId } = + req.body; + const { companyId } = req.user; + console.log("queue", integrationId, promptId) + const queue = await CreateQueueService({ + name, + color, + greetingMessage, + companyId, + outOfHoursMessage, + schedules, + orderQueue: orderQueue === "" ? null : orderQueue, + integrationId: integrationId === "" ? null : integrationId, + promptId: promptId === "" ? null : promptId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queue`, { + action: "update", + queue + }); + + return res.status(200).json(queue); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { queueId } = req.params; + const { companyId } = req.user; + + const queue = await ShowQueueService(queueId, companyId); + + return res.status(200).json(queue); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { queueId } = req.params; + const { companyId } = req.user; + const { name, color, greetingMessage, outOfHoursMessage, schedules, orderQueue, integrationId, promptId } = + req.body; + const queue = await UpdateQueueService(queueId, { + name, + color, + greetingMessage, + outOfHoursMessage, + schedules, + orderQueue: orderQueue === "" ? null : orderQueue, + integrationId: integrationId === "" ? null : integrationId, + promptId: promptId === "" ? null : promptId + }, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queue`, { + action: "update", + queue + }); + + return res.status(201).json(queue); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { queueId } = req.params; + const { companyId } = req.user; + + await DeleteQueueService(queueId, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queue`, { + action: "delete", + queueId: +queueId + }); + + return res.status(200).send(); +}; diff --git a/backend/src/controllers/QueueIntegrationController.ts b/backend/src/controllers/QueueIntegrationController.ts new file mode 100644 index 0000000..7fbb095 --- /dev/null +++ b/backend/src/controllers/QueueIntegrationController.ts @@ -0,0 +1,99 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import CreateQueueIntegrationService from "../services/QueueIntegrationServices/CreateQueueIntegrationService"; +import DeleteQueueIntegrationService from "../services/QueueIntegrationServices/DeleteQueueIntegrationService"; +import ListQueueIntegrationService from "../services/QueueIntegrationServices/ListQueueIntegrationService"; +import ShowQueueIntegrationService from "../services/QueueIntegrationServices/ShowQueueIntegrationService"; +import UpdateQueueIntegrationService from "../services/QueueIntegrationServices/UpdateQueueIntegrationService"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + const { companyId } = req.user; + + const { queueIntegrations, count, hasMore } = await ListQueueIntegrationService({ + searchParam, + pageNumber, + companyId + }); + + return res.status(200).json({ queueIntegrations, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { type, name, projectName, jsonContent, language, urlN8N, + typebotExpires, + typebotKeywordFinish, + typebotSlug, + typebotUnknownMessage, + typebotKeywordRestart, + typebotRestartMessage } = req.body; + const { companyId } = req.user; + const queueIntegration = await CreateQueueIntegrationService({ + type, name, projectName, jsonContent, language, urlN8N, companyId, + typebotExpires, + typebotKeywordFinish, + typebotSlug, + typebotUnknownMessage, + typebotKeywordRestart, + typebotRestartMessage + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queueIntegration`, { + action: "create", + queueIntegration + }); + + return res.status(200).json(queueIntegration); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { integrationId } = req.params; + const { companyId } = req.user; + + const queueIntegration = await ShowQueueIntegrationService(integrationId, companyId); + + return res.status(200).json(queueIntegration); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { integrationId } = req.params; + const integrationData = req.body; + const { companyId } = req.user; + + const queueIntegration = await UpdateQueueIntegrationService({ integrationData, integrationId, companyId }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queueIntegration`, { + action: "update", + queueIntegration + }); + + return res.status(201).json(queueIntegration); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { integrationId } = req.params; + const { companyId } = req.user; + + await DeleteQueueIntegrationService(integrationId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queueIntegration`, { + action: "delete", + integrationId: +integrationId + }); + + return res.status(200).send(); +}; \ No newline at end of file diff --git a/backend/src/controllers/QueueOptionController.ts b/backend/src/controllers/QueueOptionController.ts new file mode 100644 index 0000000..e3cbcae --- /dev/null +++ b/backend/src/controllers/QueueOptionController.ts @@ -0,0 +1,60 @@ +import { Request, Response } from "express"; + +import CreateService from "../services/QueueOptionService/CreateService"; +import ListService from "../services/QueueOptionService/ListService"; +import UpdateService from "../services/QueueOptionService/UpdateService"; +import ShowService from "../services/QueueOptionService/ShowService"; +import DeleteService from "../services/QueueOptionService/DeleteService"; + +type FilterList = { + queueId: string | number; + queueOptionId: string | number; + parentId: string | number | boolean; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { queueId, queueOptionId, parentId } = req.query as FilterList; + + const queueOptions = await ListService({ queueId, queueOptionId, parentId }); + + return res.json(queueOptions); +}; + +export const store = async (req: Request, res: Response): Promise => { + const queueOptionData = req.body; + + const queueOption = await CreateService(queueOptionData); + + return res.status(200).json(queueOption); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { queueOptionId } = req.params; + + const queueOption = await ShowService(queueOptionId); + + return res.status(200).json(queueOption); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { queueOptionId } = req.params + const queueOptionData = req.body; + + const queueOption = await UpdateService(queueOptionId, queueOptionData); + + return res.status(200).json(queueOption); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { queueOptionId } = req.params + + await DeleteService(queueOptionId); + + return res.status(200).json({ message: "Option Delected" }); +}; diff --git a/backend/src/controllers/QuickMessageController.ts b/backend/src/controllers/QuickMessageController.ts new file mode 100644 index 0000000..353ebf5 --- /dev/null +++ b/backend/src/controllers/QuickMessageController.ts @@ -0,0 +1,197 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListService from "../services/QuickMessageService/ListService"; +import CreateService from "../services/QuickMessageService/CreateService"; +import ShowService from "../services/QuickMessageService/ShowService"; +import UpdateService from "../services/QuickMessageService/UpdateService"; +import DeleteService from "../services/QuickMessageService/DeleteService"; +import FindService from "../services/QuickMessageService/FindService"; + +import QuickMessage from "../models/QuickMessage"; + +import { head } from "lodash"; +import fs from "fs"; +import path from "path"; + +import AppError from "../errors/AppError"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + userId: string | number; +}; + +type StoreData = { + shortcode: string; + message: string; + userId: number | number; +}; + +type FindParams = { + companyId: string; + userId: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber, userId } = req.query as IndexQuery; + const { companyId } = req.user; + + const { records, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId, + userId + }); + + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + shortcode: Yup.string().required(), + message: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await CreateService({ + ...data, + companyId, + userId: req.user.id + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + shortcode: Yup.string().required(), + message: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + userId: req.user.id, + id, + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Contact deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const params = req.query as FindParams; + const records: QuickMessage[] = await FindService(params); + + return res.status(200).json(records); +}; + +export const mediaUpload = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const files = req.files as Express.Multer.File[]; + const file = head(files); + + try { + const quickmessage = await QuickMessage.findByPk(id); + + quickmessage.update ({ + mediaPath: file.filename, + mediaName: file.originalname + }); + + return res.send({ mensagem: "Arquivo Anexado" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; + +export const deleteMedia = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user + + try { + const quickmessage = await QuickMessage.findByPk(id); + const filePath = path.resolve("public","quickMessage",quickmessage.mediaName); + const fileExists = fs.existsSync(filePath); + if (fileExists) { + fs.unlinkSync(filePath); + } + quickmessage.update ({ + mediaPath: null, + mediaName: null + }); + + return res.send({ mensagem: "Arquivo Excluído" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; \ No newline at end of file diff --git a/backend/src/controllers/QuickMessageController_OLD.ts b/backend/src/controllers/QuickMessageController_OLD.ts new file mode 100644 index 0000000..257d728 --- /dev/null +++ b/backend/src/controllers/QuickMessageController_OLD.ts @@ -0,0 +1,146 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListService from "../services/QuickMessageService/ListService"; +import CreateService from "../services/QuickMessageService/CreateService"; +import ShowService from "../services/QuickMessageService/ShowService"; +import UpdateService from "../services/QuickMessageService/UpdateService"; +import DeleteService from "../services/QuickMessageService/DeleteService"; +import FindService from "../services/QuickMessageService/FindService"; + +import QuickMessage from "../models/QuickMessage"; + +import AppError from "../errors/AppError"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + userId: string | number; +}; + +type StoreData = { + shortcode: string; + message: string; + userId: number | number; +}; + +type FindParams = { + companyId: string; + userId: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber, userId } = req.query as IndexQuery; + const { companyId } = req.user; + + const { records, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId, + userId + }); + + return res.json({ records, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const data = req.body as StoreData; + + const schema = Yup.object().shape({ + shortcode: Yup.string().required(), + message: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await CreateService({ + ...data, + companyId, + userId: req.user.id + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, { + action: "create", + record + }); + + return res.status(200).json(record); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const record = await ShowService(id); + + return res.status(200).json(record); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const data = req.body as StoreData; + const { companyId } = req.user; + + const schema = Yup.object().shape({ + shortcode: Yup.string().required(), + message: Yup.string().required() + }); + + try { + await schema.validate(data); + } catch (err: any) { + throw new AppError(err.message); + } + + const { id } = req.params; + + const record = await UpdateService({ + ...data, + userId: req.user.id, + id, + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, { + action: "update", + record + }); + + return res.status(200).json(record); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const { companyId } = req.user; + + await DeleteService(id); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, { + action: "delete", + id + }); + + return res.status(200).json({ message: "Contact deleted" }); +}; + +export const findList = async ( + req: Request, + res: Response +): Promise => { + const params = req.query as FindParams; + const records: QuickMessage[] = await FindService(params); + + return res.status(200).json(records); +}; diff --git a/backend/src/controllers/ScheduleController.ts b/backend/src/controllers/ScheduleController.ts new file mode 100644 index 0000000..51df02f --- /dev/null +++ b/backend/src/controllers/ScheduleController.ts @@ -0,0 +1,154 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import AppError from "../errors/AppError"; + +import CreateService from "../services/ScheduleServices/CreateService"; +import ListService from "../services/ScheduleServices/ListService"; +import UpdateService from "../services/ScheduleServices/UpdateService"; +import ShowService from "../services/ScheduleServices/ShowService"; +import DeleteService from "../services/ScheduleServices/DeleteService"; +import Schedule from "../models/Schedule"; +import path from "path"; +import fs from "fs"; +import { head } from "lodash"; + +type IndexQuery = { + searchParam?: string; + contactId?: number | string; + userId?: number | string; + pageNumber?: string | number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { contactId, userId, pageNumber, searchParam } = req.query as IndexQuery; + const { companyId } = req.user; + + const { schedules, count, hasMore } = await ListService({ + searchParam, + contactId, + userId, + pageNumber, + companyId + }); + + return res.json({ schedules, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { + body, + sendAt, + contactId, + userId + } = req.body; + const { companyId } = req.user; + + const schedule = await CreateService({ + body, + sendAt, + contactId, + companyId, + userId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("schedule", { + action: "create", + schedule + }); + + return res.status(200).json(schedule); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { scheduleId } = req.params; + const { companyId } = req.user; + + const schedule = await ShowService(scheduleId, companyId); + + return res.status(200).json(schedule); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + const { scheduleId } = req.params; + const scheduleData = req.body; + const { companyId } = req.user; + + const schedule = await UpdateService({ scheduleData, id: scheduleId, companyId }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("schedule", { + action: "update", + schedule + }); + + return res.status(200).json(schedule); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { scheduleId } = req.params; + const { companyId } = req.user; + + await DeleteService(scheduleId, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("schedule", { + action: "delete", + scheduleId + }); + + return res.status(200).json({ message: "Schedule deleted" }); +}; + +export const mediaUpload = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + const files = req.files as Express.Multer.File[]; + const file = head(files); + + try { + const schedule = await Schedule.findByPk(id); + schedule.mediaPath = file.filename; + schedule.mediaName = file.originalname; + + await schedule.save(); + return res.send({ mensagem: "Arquivo Anexado" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; + +export const deleteMedia = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + try { + const schedule = await Schedule.findByPk(id); + const filePath = path.resolve("public", schedule.mediaPath); + const fileExists = fs.existsSync(filePath); + if (fileExists) { + fs.unlinkSync(filePath); + } + schedule.mediaPath = null; + schedule.mediaName = null; + await schedule.save(); + return res.send({ mensagem: "Arquivo Excluído" }); + } catch (err: any) { + throw new AppError(err.message); + } +}; \ No newline at end of file diff --git a/backend/src/controllers/SessionController.ts b/backend/src/controllers/SessionController.ts new file mode 100644 index 0000000..9dabe76 --- /dev/null +++ b/backend/src/controllers/SessionController.ts @@ -0,0 +1,80 @@ +import { Request, Response } from "express"; +import AppError from "../errors/AppError"; +import { getIO } from "../libs/socket"; + +import AuthUserService from "../services/UserServices/AuthUserService"; +import { SendRefreshToken } from "../helpers/SendRefreshToken"; +import { RefreshTokenService } from "../services/AuthServices/RefreshTokenService"; +import FindUserFromToken from "../services/AuthServices/FindUserFromToken"; +import User from "../models/User"; + +export const store = async (req: Request, res: Response): Promise => { + const { email, password } = req.body; + + const { token, serializedUser, refreshToken } = await AuthUserService({ + email, + password + }); + + SendRefreshToken(res, refreshToken); + + const io = getIO(); + io.to(`user-${serializedUser.id}`).emit(`company-${serializedUser.companyId}-auth`, { + action: "update", + user: { + id: serializedUser.id, + email: serializedUser.email, + companyId: serializedUser.companyId + } + }); + + return res.status(200).json({ + token, + user: serializedUser + }); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const token: string = req.cookies.jrt; + + if (!token) { + throw new AppError("ERR_SESSION_EXPIRED", 401); + } + + const { user, newToken, refreshToken } = await RefreshTokenService( + res, + token + ); + + SendRefreshToken(res, refreshToken); + + return res.json({ token: newToken, user }); +}; + +export const me = async (req: Request, res: Response): Promise => { + const token: string = req.cookies.jrt; + const user = await FindUserFromToken(token); + const { id, profile, super: superAdmin } = user; + + if (!token) { + throw new AppError("ERR_SESSION_EXPIRED", 401); + } + + return res.json({ id, profile, super: superAdmin }); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.user; + const user = await User.findByPk(id); + await user.update({ online: false }); + + res.clearCookie("jrt"); + + return res.send(); +}; diff --git a/backend/src/controllers/SettingController.ts b/backend/src/controllers/SettingController.ts new file mode 100644 index 0000000..a5f43ae --- /dev/null +++ b/backend/src/controllers/SettingController.ts @@ -0,0 +1,45 @@ +import { Request, Response } from "express"; + +import { getIO } from "../libs/socket"; +import AppError from "../errors/AppError"; + +import UpdateSettingService from "../services/SettingServices/UpdateSettingService"; +import ListSettingsService from "../services/SettingServices/ListSettingsService"; + +export const index = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + + // if (req.user.profile !== "admin") { + // throw new AppError("ERR_NO_PERMISSION", 403); + // } + + const settings = await ListSettingsService({ companyId }); + + return res.status(200).json(settings); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + const { settingKey: key } = req.params; + const { value } = req.body; + const { companyId } = req.user; + + const setting = await UpdateSettingService({ + key, + value, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-settings`, { + action: "update", + setting + }); + + return res.status(200).json(setting); +}; diff --git a/backend/src/controllers/SubscriptionController.ts b/backend/src/controllers/SubscriptionController.ts new file mode 100644 index 0000000..c408733 --- /dev/null +++ b/backend/src/controllers/SubscriptionController.ts @@ -0,0 +1,198 @@ +import { Request, Response } from "express"; +import express from "express"; +import * as Yup from "yup"; +import Gerencianet from "gn-api-sdk-typescript"; +import AppError from "../errors/AppError"; + +import options from "../config/Gn"; +import Company from "../models/Company"; +import Invoices from "../models/Invoices"; +import Subscriptions from "../models/Subscriptions"; +import { getIO } from "../libs/socket"; +import UpdateUserService from "../services/UserServices/UpdateUserService"; + +const app = express(); + + +export const index = async (req: Request, res: Response): Promise => { + const gerencianet = Gerencianet(options); + return res.json(gerencianet.getSubscriptions()); +}; + +export const createSubscription = async ( + req: Request, + res: Response + ): Promise => { + const gerencianet = Gerencianet(options); + const { companyId } = req.user; + + const schema = Yup.object().shape({ + price: Yup.string().required(), + users: Yup.string().required(), + connections: Yup.string().required() + }); + + if (!(await schema.isValid(req.body))) { + throw new AppError("Validation fails", 400); + } + + const { + firstName, + price, + users, + connections, + address2, + city, + state, + zipcode, + country, + plan, + invoiceId + } = req.body; + + const body = { + calendario: { + expiracao: 3600 + }, + valor: { + original: price.toLocaleString("pt-br", { minimumFractionDigits: 2 }).replace(",", ".") + }, + chave: process.env.GERENCIANET_PIX_KEY, + solicitacaoPagador: `#Fatura:${invoiceId}` + }; + try { + const pix = await gerencianet.pixCreateImmediateCharge(null, body); + + const qrcode = await gerencianet.pixGenerateQRCode({ + id: pix.loc.id + }); + + const updateCompany = await Company.findOne(); + + if (!updateCompany) { + throw new AppError("Company not found", 404); + } + + +/* await Subscriptions.create({ + companyId, + isActive: false, + userPriceCents: users, + whatsPriceCents: connections, + lastInvoiceUrl: pix.location, + lastPlanChange: new Date(), + providerSubscriptionId: pix.loc.id, + expiresAt: new Date() + }); */ + +/* const { id } = req.user; + const userData = {}; + const userId = id; + const requestUserId = parseInt(id); + const user = await UpdateUserService({ userData, userId, companyId, requestUserId }); */ + + /* const io = getIO(); + io.emit("user", { + action: "update", + user + }); */ + + + return res.json({ + ...pix, + qrcode, + + }); + } catch (error) { + throw new AppError("Validation fails", 400); + } +}; + +export const createWebhook = async ( + req: Request, + res: Response +): Promise => { + const schema = Yup.object().shape({ + chave: Yup.string().required(), + url: Yup.string().required() + }); + + if (!(await schema.isValid(req.body))) { + throw new AppError("Validation fails", 400); + } + + const { chave, url } = req.body; + + const body = { + webhookUrl: url + }; + + const params = { + chave + }; + + try { + const gerencianet = Gerencianet(options); + const create = await gerencianet.pixConfigWebhook(params, body); + return res.json(create); + } catch (error) { + console.log(error); + } +}; + +export const webhook = async ( + req: Request, + res: Response + ): Promise => { + const { type } = req.params; + const { evento } = req.body; + if (evento === "teste_webhook") { + return res.json({ ok: true }); + } + if (req.body.pix) { + const gerencianet = Gerencianet(options); + req.body.pix.forEach(async (pix: any) => { + const detahe = await gerencianet.pixDetailCharge({ + txid: pix.txid + }); + + if (detahe.status === "CONCLUIDA") { + const { solicitacaoPagador } = detahe; + const invoiceID = solicitacaoPagador.replace("#Fatura:", ""); + const invoices = await Invoices.findByPk(invoiceID); + const companyId =invoices.companyId; + const company = await Company.findByPk(companyId); + + const expiresAt = new Date(company.dueDate); + expiresAt.setDate(expiresAt.getDate() + 30); + const date = expiresAt.toISOString().split("T")[0]; + + if (company) { + await company.update({ + dueDate: date + }); + const invoi = await invoices.update({ + id: invoiceID, + status: 'paid' + }); + await company.reload(); + const io = getIO(); + const companyUpdate = await Company.findOne({ + where: { + id: companyId + } + }); + + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-payment`, { + action: detahe.status, + company: companyUpdate + }); + } + + } + }); + + } + + return res.json({ ok: true }); +}; diff --git a/backend/src/controllers/TagController.ts b/backend/src/controllers/TagController.ts new file mode 100644 index 0000000..4de55cd --- /dev/null +++ b/backend/src/controllers/TagController.ts @@ -0,0 +1,128 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import AppError from "../errors/AppError"; + +import CreateService from "../services/TagServices/CreateService"; +import ListService from "../services/TagServices/ListService"; +import UpdateService from "../services/TagServices/UpdateService"; +import ShowService from "../services/TagServices/ShowService"; +import DeleteService from "../services/TagServices/DeleteService"; +import SimpleListService from "../services/TagServices/SimpleListService"; +import SyncTagService from "../services/TagServices/SyncTagsService"; +import KanbanListService from "../services/TagServices/KanbanListService"; + +type IndexQuery = { + searchParam?: string; + pageNumber?: string | number; + kanban?: number; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { pageNumber, searchParam } = req.query as IndexQuery; + const { companyId } = req.user; + + const { tags, count, hasMore } = await ListService({ + searchParam, + pageNumber, + companyId + }); + + return res.json({ tags, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { name, color, kanban } = req.body; + const { companyId } = req.user; + + const tag = await CreateService({ + name, + color, + companyId, + kanban + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit("tag", { + action: "create", + tag + }); + + return res.status(200).json(tag); +}; + +export const kanban = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + + const tags = await KanbanListService({ companyId }); + + return res.json({lista:tags}); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { tagId } = req.params; + + const tag = await ShowService(tagId); + + return res.status(200).json(tag); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + const { tagId } = req.params; + const tagData = req.body; + + const tag = await UpdateService({ tagData, id: tagId }); + + const io = getIO(); + io.to(`company-${req.user.companyId}-mainchannel`).emit("tag", { + action: "update", + tag + }); + + return res.status(200).json(tag); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { tagId } = req.params; + + await DeleteService(tagId); + + const io = getIO(); + io.to(`company-${req.user.companyId}-mainchannel`).emit("tag", { + action: "delete", + tagId + }); + + return res.status(200).json({ message: "Tag deleted" }); +}; + +export const list = async (req: Request, res: Response): Promise => { + const { searchParam } = req.query as IndexQuery; + const { companyId } = req.user; + + const tags = await SimpleListService({ searchParam, companyId }); + + return res.json(tags); +}; + +export const syncTags = async ( + req: Request, + res: Response +): Promise => { + const data = req.body; + const { companyId } = req.user; + + const tags = await SyncTagService({ ...data, companyId }); + + return res.json(tags); +}; diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts new file mode 100644 index 0000000..c1d4542 --- /dev/null +++ b/backend/src/controllers/TicketController.ts @@ -0,0 +1,223 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import Ticket from "../models/Ticket"; + +import CreateTicketService from "../services/TicketServices/CreateTicketService"; +import DeleteTicketService from "../services/TicketServices/DeleteTicketService"; +import ListTicketsService from "../services/TicketServices/ListTicketsService"; +import ShowTicketUUIDService from "../services/TicketServices/ShowTicketFromUUIDService"; +import ShowTicketService from "../services/TicketServices/ShowTicketService"; +import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; +import ListTicketsServiceKanban from "../services/TicketServices/ListTicketsServiceKanban"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; + status: string; + date: string; + updatedAt?: string; + showAll: string; + withUnreadMessages: string; + queueIds: string; + tags: string; + users: string; +}; + +interface TicketData { + contactId: number; + status: string; + queueId: number; + userId: number; + whatsappId: string; + useIntegration: boolean; + promptId: number; + integrationId: number; +} + +export const index = async (req: Request, res: Response): Promise => { + const { + pageNumber, + status, + date, + updatedAt, + searchParam, + showAll, + queueIds: queueIdsStringified, + tags: tagIdsStringified, + users: userIdsStringified, + withUnreadMessages + } = req.query as IndexQuery; + + const userId = req.user.id; + const { companyId } = req.user; + + let queueIds: number[] = []; + let tagsIds: number[] = []; + let usersIds: number[] = []; + + if (queueIdsStringified) { + queueIds = JSON.parse(queueIdsStringified); + } + + if (tagIdsStringified) { + tagsIds = JSON.parse(tagIdsStringified); + } + + if (userIdsStringified) { + usersIds = JSON.parse(userIdsStringified); + } + + const { tickets, count, hasMore } = await ListTicketsService({ + searchParam, + tags: tagsIds, + users: usersIds, + pageNumber, + status, + date, + updatedAt, + showAll, + userId, + queueIds, + withUnreadMessages, + companyId, + + + }); + return res.status(200).json({ tickets, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { contactId, status, userId, queueId, whatsappId }: TicketData = req.body; + const { companyId } = req.user; + + const ticket = await CreateTicketService({ + contactId, + status, + userId, + companyId, + queueId, + whatsappId + }); + + const io = getIO(); + io.to(ticket.status).emit(`company-${companyId}-ticket`, { + action: "update", + ticket + }); + return res.status(200).json(ticket); +}; + +export const kanban = async (req: Request, res: Response): Promise => { + const { + pageNumber, + status, + date, + updatedAt, + searchParam, + showAll, + queueIds: queueIdsStringified, + tags: tagIdsStringified, + users: userIdsStringified, + withUnreadMessages + } = req.query as IndexQuery; + + + const userId = req.user.id; + const { companyId } = req.user; + + let queueIds: number[] = []; + let tagsIds: number[] = []; + let usersIds: number[] = []; + + if (queueIdsStringified) { + queueIds = JSON.parse(queueIdsStringified); + } + + if (tagIdsStringified) { + tagsIds = JSON.parse(tagIdsStringified); + } + + if (userIdsStringified) { + usersIds = JSON.parse(userIdsStringified); + } + + const { tickets, count, hasMore } = await ListTicketsServiceKanban({ + searchParam, + tags: tagsIds, + users: usersIds, + pageNumber, + status, + date, + updatedAt, + showAll, + userId, + queueIds, + withUnreadMessages, + companyId + + }); + + return res.status(200).json({ tickets, count, hasMore }); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { ticketId } = req.params; + const { companyId } = req.user; + + const contact = await ShowTicketService(ticketId, companyId); + return res.status(200).json(contact); +}; + +export const showFromUUID = async ( + req: Request, + res: Response +): Promise => { + const { uuid } = req.params; + + const ticket: Ticket = await ShowTicketUUIDService(uuid); + + return res.status(200).json(ticket); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { ticketId } = req.params; + const ticketData: TicketData = req.body; + const { companyId } = req.user; + + const { ticket } = await UpdateTicketService({ + ticketData, + ticketId, + companyId + }); + + + return res.status(200).json(ticket); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { ticketId } = req.params; + const { companyId } = req.user; + + await ShowTicketService(ticketId, companyId); + + const ticket = await DeleteTicketService(ticketId); + + const io = getIO(); + io.to(ticketId) + .to(`company-${companyId}-${ticket.status}`) + .to(`company-${companyId}-notification`) + .to(`queue-${ticket.queueId}-${ticket.status}`) + .to(`queue-${ticket.queueId}-notification`) + .emit(`company-${companyId}-ticket`, { + action: "delete", + ticketId: +ticketId + }); + + return res.status(200).json({ message: "ticket deleted" }); +}; diff --git a/backend/src/controllers/TicketNoteController.ts b/backend/src/controllers/TicketNoteController.ts new file mode 100644 index 0000000..42a4379 --- /dev/null +++ b/backend/src/controllers/TicketNoteController.ts @@ -0,0 +1,138 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import AppError from "../errors/AppError"; +import TicketNote from "../models/TicketNote"; + +import ListTicketNotesService from "../services/TicketNoteService/ListTicketNotesService"; +import CreateTicketNoteService from "../services/TicketNoteService/CreateTicketNoteService"; +import UpdateTicketNoteService from "../services/TicketNoteService/UpdateTicketNoteService"; +import ShowTicketNoteService from "../services/TicketNoteService/ShowTicketNoteService"; +import FindAllTicketNotesService from "../services/TicketNoteService/FindAllTicketNotesService"; +import DeleteTicketNoteService from "../services/TicketNoteService/DeleteTicketNoteService"; +import FindNotesByContactIdAndTicketId from "../services/TicketNoteService/FindNotesByContactIdAndTicketId"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +type StoreTicketNoteData = { + note: string; + userId: number; + contactId: number | 0; + ticketId: number | 0; + id?: number | string; +}; + +type UpdateTicketNoteData = { + note: string; + id?: number | string; + userId?: number | 0; + contactId?: number | 0; + ticketId?: number | 0; +}; + +type QueryFilteredNotes = { + contactId: number | string; + ticketId: number | string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { ticketNotes, count, hasMore } = await ListTicketNotesService({ + searchParam, + pageNumber + }); + + return res.json({ ticketNotes, count, hasMore }); +}; + +export const list = async (req: Request, res: Response): Promise => { + const ticketNotes: TicketNote[] = await FindAllTicketNotesService(); + + return res.status(200).json(ticketNotes); +}; + +export const store = async (req: Request, res: Response): Promise => { + const newTicketNote: StoreTicketNoteData = req.body; + const { id: userId } = req.user; + + const schema = Yup.object().shape({ + note: Yup.string().required() + }); + + try { + await schema.validate(newTicketNote); + } catch (err) { + throw new AppError(err.message); + } + + const ticketNote = await CreateTicketNoteService({ + ...newTicketNote, + userId + }); + + return res.status(200).json(ticketNote); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { id } = req.params; + + const ticketNote = await ShowTicketNoteService(id); + + return res.status(200).json(ticketNote); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const ticketNote: UpdateTicketNoteData = req.body; + + const schema = Yup.object().shape({ + note: Yup.string() + }); + + try { + await schema.validate(ticketNote); + } catch (err) { + throw new AppError(err.message); + } + + const recordUpdated = await UpdateTicketNoteService(ticketNote); + + return res.status(200).json(recordUpdated); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { id } = req.params; + + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + await DeleteTicketNoteService(id); + + return res.status(200).json({ message: "Observação removida" }); +}; + +export const findFilteredList = async ( + req: Request, + res: Response +): Promise => { + try { + const { contactId, ticketId } = req.query as QueryFilteredNotes; + const notes: TicketNote[] = await FindNotesByContactIdAndTicketId({ + contactId, + ticketId + }); + + return res.status(200).json(notes); + } catch (e) { + return res.status(500).json({ message: e }); + } +}; diff --git a/backend/src/controllers/TicketTagController.ts b/backend/src/controllers/TicketTagController.ts new file mode 100644 index 0000000..c240c8b --- /dev/null +++ b/backend/src/controllers/TicketTagController.ts @@ -0,0 +1,57 @@ +import { Request, Response } from "express"; +import AppError from "../errors/AppError"; +import TicketTag from '../models/TicketTag'; +import Tag from '../models/Tag' + +export const store = async (req: Request, res: Response): Promise => { + const { ticketId, tagId } = req.params; + + try { + const ticketTag = await TicketTag.create({ ticketId, tagId }); + return res.status(201).json(ticketTag); + } catch (error) { + return res.status(500).json({ error: 'Failed to store ticket tag.' }); + } +}; + +/* +export const remove = async (req: Request, res: Response): Promise => { + const { ticketId } = req.params; + + + + try { + await TicketTag.destroy({ where: { ticketId } }); + return res.status(200).json({ message: 'Ticket tags removed successfully.' }); + } catch (error) { + return res.status(500).json({ error: 'Failed to remove ticket tags.' }); + } +}; +*/ +export const remove = async (req: Request, res: Response): Promise => { + const { ticketId } = req.params; + + + try { + // Retrieve tagIds associated with the provided ticketId from TicketTags + const ticketTags = await TicketTag.findAll({ where: { ticketId } }); + const tagIds = ticketTags.map((ticketTag) => ticketTag.tagId); + + // Find the tagIds with kanban = 1 in the Tags table + const tagsWithKanbanOne = await Tag.findAll({ + where: { + id: tagIds, + kanban: 1, + }, + }); + + // Remove the tagIds with kanban = 1 from TicketTags + const tagIdsWithKanbanOne = tagsWithKanbanOne.map((tag) => tag.id); + if (tagIdsWithKanbanOne) + await TicketTag.destroy({ where: { ticketId, tagId: tagIdsWithKanbanOne } }); + + return res.status(200).json({ message: 'Ticket tags removed successfully.' }); + } catch (error) { + return res.status(500).json({ error: 'Failed to remove ticket tags.' }); + } +}; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts new file mode 100644 index 0000000..2865b20 --- /dev/null +++ b/backend/src/controllers/UserController.ts @@ -0,0 +1,158 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import CheckSettingsHelper from "../helpers/CheckSettings"; +import AppError from "../errors/AppError"; + +import CreateUserService from "../services/UserServices/CreateUserService"; +import ListUsersService from "../services/UserServices/ListUsersService"; +import UpdateUserService from "../services/UserServices/UpdateUserService"; +import ShowUserService from "../services/UserServices/ShowUserService"; +import DeleteUserService from "../services/UserServices/DeleteUserService"; +import SimpleListService from "../services/UserServices/SimpleListService"; +import User from "../models/User"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +type ListQueryParams = { + companyId: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + const { companyId, profile } = req.user; + + const { users, count, hasMore } = await ListUsersService({ + searchParam, + pageNumber, + companyId, + profile + }); + + return res.json({ users, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { + email, + password, + name, + profile, + companyId: bodyCompanyId, + queueIds, + whatsappId, + allTicket + } = req.body; + let userCompanyId: number | null = null; + + let requestUser: User = null; + + if (req.user !== undefined) { + const { companyId: cId } = req.user; + userCompanyId = cId; + requestUser = await User.findByPk(req.user.id); + } + + const newUserCompanyId = bodyCompanyId || userCompanyId; + + if (req.url === "/signup") { + if (await CheckSettingsHelper("userCreation") === "disabled") { + throw new AppError("ERR_USER_CREATION_DISABLED", 403); + } + } else if (req.user?.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } else if (newUserCompanyId !== req.user?.companyId && !requestUser?.super) { + throw new AppError("ERR_NO_SUPER", 403); + } + + const user = await CreateUserService({ + email, + password, + name, + profile, + companyId: newUserCompanyId, + queueIds, + whatsappId, + allTicket + }); + + const io = getIO(); + io.to(`company-${userCompanyId}-mainchannel`).emit(`company-${userCompanyId}-user`, { + action: "create", + user + }); + + return res.status(200).json(user); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { userId } = req.params; + + const user = await ShowUserService(userId); + + return res.status(200).json(user); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + const { id: requestUserId, companyId } = req.user; + const { userId } = req.params; + const userData = req.body; + + const user = await UpdateUserService({ + userData, + userId, + companyId, + requestUserId: +requestUserId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-user`, { + action: "update", + user + }); + + return res.status(200).json(user); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { userId } = req.params; + const { companyId } = req.user; + + if (req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + await DeleteUserService(userId, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-user`, { + action: "delete", + userId + }); + + return res.status(200).json({ message: "User deleted" }); +}; + +export const list = async (req: Request, res: Response): Promise => { + const { companyId } = req.query; + const { companyId: userCompanyId } = req.user; + + const users = await SimpleListService({ + companyId: companyId ? +companyId : userCompanyId + }); + + return res.status(200).json(users); +}; diff --git a/backend/src/controllers/VersionController.ts b/backend/src/controllers/VersionController.ts new file mode 100644 index 0000000..bb1cfa3 --- /dev/null +++ b/backend/src/controllers/VersionController.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +export const index = async (req: Request, res: Response): Promise => { + return res.status(200).json({ + version: "4.6.5" + }); +}; diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts new file mode 100644 index 0000000..79b7e89 --- /dev/null +++ b/backend/src/controllers/WhatsAppController.ts @@ -0,0 +1,166 @@ +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; +import { removeWbot } from "../libs/wbot"; +import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; + +import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; +import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService"; +import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; +import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; +import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; + +interface WhatsappData { + name: string; + queueIds: number[]; + companyId: number; + greetingMessage?: string; + complationMessage?: string; + outOfHoursMessage?: string; + ratingMessage?: string; + status?: string; + isDefault?: boolean; + token?: string; + //sendIdQueue?: number; + //timeSendQueue?: number; + transferQueueId?: number; + timeToTransfer?: number; + promptId?: number; + maxUseBotQueues?: number; + timeUseBotQueues?: number; + expiresTicket?: number; + expiresInactiveMessage?: string; +} + +interface QueryParams { + session?: number | string; +} + +export const index = async (req: Request, res: Response): Promise => { + const { companyId } = req.user; + const { session } = req.query as QueryParams; + const whatsapps = await ListWhatsAppsService({ companyId, session }); + + return res.status(200).json(whatsapps); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { + name, + status, + isDefault, + greetingMessage, + complationMessage, + outOfHoursMessage, + queueIds, + token, + //timeSendQueue, + //sendIdQueue, + transferQueueId, + timeToTransfer, + promptId, + maxUseBotQueues, + timeUseBotQueues, + expiresTicket, + expiresInactiveMessage + }: WhatsappData = req.body; + const { companyId } = req.user; + + const { whatsapp, oldDefaultWhatsapp } = await CreateWhatsAppService({ + name, + status, + isDefault, + greetingMessage, + complationMessage, + outOfHoursMessage, + queueIds, + companyId, + token, + //timeSendQueue, + //sendIdQueue, + transferQueueId, + timeToTransfer, + promptId, + maxUseBotQueues, + timeUseBotQueues, + expiresTicket, + expiresInactiveMessage + }); + + StartWhatsAppSession(whatsapp, companyId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, { + action: "update", + whatsapp + }); + + if (oldDefaultWhatsapp) { + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, { + action: "update", + whatsapp: oldDefaultWhatsapp + }); + } + + return res.status(200).json(whatsapp); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { whatsappId } = req.params; + const { companyId } = req.user; + const { session } = req.query; + + const whatsapp = await ShowWhatsAppService(whatsappId, companyId, session); + + return res.status(200).json(whatsapp); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const { whatsappId } = req.params; + const whatsappData = req.body; + const { companyId } = req.user; + + const { whatsapp, oldDefaultWhatsapp } = await UpdateWhatsAppService({ + whatsappData, + whatsappId, + companyId + }); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, { + action: "update", + whatsapp + }); + + if (oldDefaultWhatsapp) { + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, { + action: "update", + whatsapp: oldDefaultWhatsapp + }); + } + + return res.status(200).json(whatsapp); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { whatsappId } = req.params; + const { companyId } = req.user; + + await ShowWhatsAppService(whatsappId, companyId); + + await DeleteWhatsAppService(whatsappId); + removeWbot(+whatsappId); + + const io = getIO(); + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, { + action: "delete", + whatsappId: +whatsappId + }); + + return res.status(200).json({ message: "Whatsapp deleted." }); +}; diff --git a/backend/src/controllers/WhatsAppSessionController.ts b/backend/src/controllers/WhatsAppSessionController.ts new file mode 100644 index 0000000..dffcd4a --- /dev/null +++ b/backend/src/controllers/WhatsAppSessionController.ts @@ -0,0 +1,46 @@ +import { Request, Response } from "express"; +import { getWbot } from "../libs/wbot"; +import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; +import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; +import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; + +const store = async (req: Request, res: Response): Promise => { + const { whatsappId } = req.params; + const { companyId } = req.user; + + const whatsapp = await ShowWhatsAppService(whatsappId, companyId); + await StartWhatsAppSession(whatsapp, companyId); + + return res.status(200).json({ message: "Starting session." }); +}; + +const update = async (req: Request, res: Response): Promise => { + const { whatsappId } = req.params; + const { companyId } = req.user; + + const { whatsapp } = await UpdateWhatsAppService({ + whatsappId, + companyId, + whatsappData: { session: "" } + }); + + await StartWhatsAppSession(whatsapp, companyId); + + return res.status(200).json({ message: "Starting session." }); +}; + +const remove = async (req: Request, res: Response): Promise => { + const { whatsappId } = req.params; + const { companyId } = req.user; + const whatsapp = await ShowWhatsAppService(whatsappId, companyId); + + if (whatsapp.session) { + await whatsapp.update({ status: "DISCONNECTED", session: "" }); + const wbot = getWbot(whatsapp.id); + await wbot.logout(); + } + + return res.status(200).json({ message: "Session disconnected." }); +}; + +export default { store, remove, update }; diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts new file mode 100644 index 0000000..d9b871d --- /dev/null +++ b/backend/src/database/index.ts @@ -0,0 +1,90 @@ +import { Sequelize } from "sequelize-typescript"; +import User from "../models/User"; +import Setting from "../models/Setting"; +import Contact from "../models/Contact"; +import Ticket from "../models/Ticket"; +import Whatsapp from "../models/Whatsapp"; +import ContactCustomField from "../models/ContactCustomField"; +import Message from "../models/Message"; +import Queue from "../models/Queue"; +import WhatsappQueue from "../models/WhatsappQueue"; +import UserQueue from "../models/UserQueue"; +import Company from "../models/Company"; +import Plan from "../models/Plan"; +import TicketNote from "../models/TicketNote"; +import QuickMessage from "../models/QuickMessage"; +import Help from "../models/Help"; +import TicketTraking from "../models/TicketTraking"; +import UserRating from "../models/UserRating"; +import QueueOption from "../models/QueueOption"; +import Schedule from "../models/Schedule"; +import Tag from "../models/Tag"; +import TicketTag from "../models/TicketTag"; +import ContactList from "../models/ContactList"; +import ContactListItem from "../models/ContactListItem"; +import Campaign from "../models/Campaign"; +import CampaignSetting from "../models/CampaignSetting"; +import Baileys from "../models/Baileys"; +import CampaignShipping from "../models/CampaignShipping"; +import Announcement from "../models/Announcement"; +import Chat from "../models/Chat"; +import ChatUser from "../models/ChatUser"; +import ChatMessage from "../models/ChatMessage"; +import Invoices from "../models/Invoices"; +import Subscriptions from "../models/Subscriptions"; +import BaileysChats from "../models/BaileysChats"; +import Files from "../models/Files"; +import FilesOptions from "../models/FilesOptions"; +import Prompt from "../models/Prompt"; +import QueueIntegrations from "../models/QueueIntegrations"; + +// eslint-disable-next-line +const dbConfig = require("../config/database"); +// import dbConfig from "../config/database"; + +const sequelize = new Sequelize(dbConfig); + +const models = [ + Company, + User, + Contact, + Ticket, + Message, + Whatsapp, + ContactCustomField, + Setting, + Queue, + WhatsappQueue, + UserQueue, + Plan, + TicketNote, + QuickMessage, + Help, + TicketTraking, + UserRating, + QueueOption, + Schedule, + Tag, + TicketTag, + ContactList, + ContactListItem, + Campaign, + CampaignSetting, + Baileys, + CampaignShipping, + Announcement, + Chat, + ChatUser, + ChatMessage, + Invoices, + Subscriptions, + BaileysChats, + Files, + FilesOptions, + Prompt, + QueueIntegrations, +]; + +sequelize.addModels(models); + +export default sequelize; diff --git a/backend/src/database/migrations/20200717133431-add-uuid-ossp.ts b/backend/src/database/migrations/20200717133431-add-uuid-ossp.ts new file mode 100644 index 0000000..671b11e --- /dev/null +++ b/backend/src/database/migrations/20200717133431-add-uuid-ossp.ts @@ -0,0 +1,10 @@ +import { QueryInterface, DataTypes, Sequelize } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'), + ]); + }, + +}; diff --git a/backend/src/database/migrations/20200717133438-create-users.ts b/backend/src/database/migrations/20200717133438-create-users.ts new file mode 100644 index 0000000..17e9ee9 --- /dev/null +++ b/backend/src/database/migrations/20200717133438-create-users.ts @@ -0,0 +1,39 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Users", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + passwordHash: { + type: DataTypes.STRING, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Users"); + } +}; diff --git a/backend/src/database/migrations/20200717144403-create-contacts.ts b/backend/src/database/migrations/20200717144403-create-contacts.ts new file mode 100644 index 0000000..f3fa92a --- /dev/null +++ b/backend/src/database/migrations/20200717144403-create-contacts.ts @@ -0,0 +1,37 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Contacts", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + number: { + type: DataTypes.STRING, + allowNull: false + }, + profilePicUrl: { + type: DataTypes.STRING + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Contacts"); + } +}; diff --git a/backend/src/database/migrations/20200717145643-create-tickets.ts b/backend/src/database/migrations/20200717145643-create-tickets.ts new file mode 100644 index 0000000..d5016ee --- /dev/null +++ b/backend/src/database/migrations/20200717145643-create-tickets.ts @@ -0,0 +1,46 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Tickets", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + status: { + type: DataTypes.STRING, + defaultValue: "pending", + allowNull: false + }, + lastMessage: { + type: DataTypes.STRING + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Tickets"); + } +}; diff --git a/backend/src/database/migrations/20200717151645-create-messages.ts b/backend/src/database/migrations/20200717151645-create-messages.ts new file mode 100644 index 0000000..052dfc1 --- /dev/null +++ b/backend/src/database/migrations/20200717151645-create-messages.ts @@ -0,0 +1,58 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Messages", { + id: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false + }, + body: { + type: DataTypes.TEXT, + allowNull: false + }, + ack: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + read: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + mediaType: { + type: DataTypes.STRING + }, + mediaUrl: { + type: DataTypes.STRING + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }, + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Messages"); + } +}; diff --git a/backend/src/database/migrations/20200717170223-create-whatsapps.ts b/backend/src/database/migrations/20200717170223-create-whatsapps.ts new file mode 100644 index 0000000..0686bc1 --- /dev/null +++ b/backend/src/database/migrations/20200717170223-create-whatsapps.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Whatsapps", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + session: { + type: DataTypes.TEXT + }, + qrcode: { + type: DataTypes.TEXT + }, + status: { + type: DataTypes.STRING + }, + battery: { + type: DataTypes.STRING + }, + plugged: { + type: DataTypes.BOOLEAN + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Whatsapps"); + } +}; diff --git a/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts b/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts new file mode 100644 index 0000000..c6cc7f7 --- /dev/null +++ b/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("ContactCustomFields", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + value: { + type: DataTypes.STRING, + allowNull: false + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("ContactCustomFields"); + } +}; diff --git a/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts b/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts new file mode 100644 index 0000000..cbf086d --- /dev/null +++ b/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Contacts", "email", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Contacts", "email"); + } +}; diff --git a/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts b/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts new file mode 100644 index 0000000..765619f --- /dev/null +++ b/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "userId"); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "userId", { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + } +}; diff --git a/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts b/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts new file mode 100644 index 0000000..4bdcebe --- /dev/null +++ b/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "fromMe", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "fromMe"); + } +}; diff --git a/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts b/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts new file mode 100644 index 0000000..e4248e8 --- /dev/null +++ b/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("Tickets", "lastMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("Tickets", "lastMessage", { + type: DataTypes.STRING + }); + } +}; diff --git a/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts b/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts new file mode 100644 index 0000000..b1d866d --- /dev/null +++ b/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "profile", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "admin" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "profile"); + } +}; diff --git a/backend/src/database/migrations/20200903215941-create-settings.ts b/backend/src/database/migrations/20200903215941-create-settings.ts new file mode 100644 index 0000000..b8724fc --- /dev/null +++ b/backend/src/database/migrations/20200903215941-create-settings.ts @@ -0,0 +1,29 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Settings", { + key: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false + }, + value: { + type: DataTypes.TEXT, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Settings"); + } +}; diff --git a/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts b/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts new file mode 100644 index 0000000..3d15507 --- /dev/null +++ b/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "name", { + type: DataTypes.STRING, + allowNull: false, + unique: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "name"); + } +}; diff --git a/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts b/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts new file mode 100644 index 0000000..7ec4a50 --- /dev/null +++ b/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "default", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "default"); + } +}; diff --git a/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts b/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts new file mode 100644 index 0000000..5ed102d --- /dev/null +++ b/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "whatsappId", { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "whatsappId"); + } +}; diff --git a/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts b/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts new file mode 100644 index 0000000..4821129 --- /dev/null +++ b/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts @@ -0,0 +1,11 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.renameColumn("Whatsapps", "default", "isDefault"); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.renameColumn("Whatsapps", "isDefault", "default"); + } +}; diff --git a/backend/src/database/migrations/20200927220708-add-isDeleted-column-to-messages.ts b/backend/src/database/migrations/20200927220708-add-isDeleted-column-to-messages.ts new file mode 100644 index 0000000..a3ffa86 --- /dev/null +++ b/backend/src/database/migrations/20200927220708-add-isDeleted-column-to-messages.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "isDeleted", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "isDeleted"); + } +}; diff --git a/backend/src/database/migrations/20200929145451-add-user-tokenVersion-column.ts b/backend/src/database/migrations/20200929145451-add-user-tokenVersion-column.ts new file mode 100644 index 0000000..ceb5c21 --- /dev/null +++ b/backend/src/database/migrations/20200929145451-add-user-tokenVersion-column.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "tokenVersion", { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "tokenVersion"); + } +}; diff --git a/backend/src/database/migrations/20200930162323-add-isGroup-column-to-tickets.ts b/backend/src/database/migrations/20200930162323-add-isGroup-column-to-tickets.ts new file mode 100644 index 0000000..3e7ba47 --- /dev/null +++ b/backend/src/database/migrations/20200930162323-add-isGroup-column-to-tickets.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "isGroup", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "isGroup"); + } +}; diff --git a/backend/src/database/migrations/20200930194808-add-isGroup-column-to-contacts.ts b/backend/src/database/migrations/20200930194808-add-isGroup-column-to-contacts.ts new file mode 100644 index 0000000..d2037ec --- /dev/null +++ b/backend/src/database/migrations/20200930194808-add-isGroup-column-to-contacts.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Contacts", "isGroup", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Contacts", "isGroup"); + } +}; diff --git a/backend/src/database/migrations/20201004150008-add-contactId-column-to-messages.ts b/backend/src/database/migrations/20201004150008-add-contactId-column-to-messages.ts new file mode 100644 index 0000000..4b8f111 --- /dev/null +++ b/backend/src/database/migrations/20201004150008-add-contactId-column-to-messages.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "contactId", { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "contactId"); + } +}; diff --git a/backend/src/database/migrations/20201004155719-add-vcardContactId-column-to-messages.ts b/backend/src/database/migrations/20201004155719-add-vcardContactId-column-to-messages.ts new file mode 100644 index 0000000..d897363 --- /dev/null +++ b/backend/src/database/migrations/20201004155719-add-vcardContactId-column-to-messages.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "vcardContactId", { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "vcardContactId"); + } +}; diff --git a/backend/src/database/migrations/20201004955719-remove-vcardContactId-column-to-messages.ts b/backend/src/database/migrations/20201004955719-remove-vcardContactId-column-to-messages.ts new file mode 100644 index 0000000..dac0046 --- /dev/null +++ b/backend/src/database/migrations/20201004955719-remove-vcardContactId-column-to-messages.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "vcardContactId"); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "vcardContactId", { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }); + } +}; diff --git a/backend/src/database/migrations/20201026215410-add-retries-to-whatsapps.ts b/backend/src/database/migrations/20201026215410-add-retries-to-whatsapps.ts new file mode 100644 index 0000000..57b1450 --- /dev/null +++ b/backend/src/database/migrations/20201026215410-add-retries-to-whatsapps.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "retries", { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "retries"); + } +}; diff --git a/backend/src/database/migrations/20201028124427-add-quoted-msg-to-messages.ts b/backend/src/database/migrations/20201028124427-add-quoted-msg-to-messages.ts new file mode 100644 index 0000000..8bfd56f --- /dev/null +++ b/backend/src/database/migrations/20201028124427-add-quoted-msg-to-messages.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "quotedMsgId", { + type: DataTypes.STRING, + references: { model: "Messages", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "quotedMsgId"); + } +}; diff --git a/backend/src/database/migrations/20210108001431-add-unreadMessages-to-tickets.ts b/backend/src/database/migrations/20210108001431-add-unreadMessages-to-tickets.ts new file mode 100644 index 0000000..ca5b47f --- /dev/null +++ b/backend/src/database/migrations/20210108001431-add-unreadMessages-to-tickets.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "unreadMessages", { + type: DataTypes.INTEGER + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "unreadMessages"); + } +}; diff --git a/backend/src/database/migrations/20210108164404-create-queues.ts b/backend/src/database/migrations/20210108164404-create-queues.ts new file mode 100644 index 0000000..4a404d6 --- /dev/null +++ b/backend/src/database/migrations/20210108164404-create-queues.ts @@ -0,0 +1,39 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Queues", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + color: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + greetingMessage: { + type: DataTypes.TEXT + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Queues"); + } +}; diff --git a/backend/src/database/migrations/20210108164504-add-queueId-to-tickets.ts b/backend/src/database/migrations/20210108164504-add-queueId-to-tickets.ts new file mode 100644 index 0000000..6122b32 --- /dev/null +++ b/backend/src/database/migrations/20210108164504-add-queueId-to-tickets.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "queueId", { + type: DataTypes.INTEGER, + references: { model: "Queues", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "queueId"); + } +}; diff --git a/backend/src/database/migrations/20210108174594-associate-whatsapp-queue.ts b/backend/src/database/migrations/20210108174594-associate-whatsapp-queue.ts new file mode 100644 index 0000000..0e08f71 --- /dev/null +++ b/backend/src/database/migrations/20210108174594-associate-whatsapp-queue.ts @@ -0,0 +1,28 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("WhatsappQueues", { + whatsappId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + queueId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("WhatsappQueues"); + } +}; diff --git a/backend/src/database/migrations/20210108204708-associate-users-queue.ts b/backend/src/database/migrations/20210108204708-associate-users-queue.ts new file mode 100644 index 0000000..d92496a --- /dev/null +++ b/backend/src/database/migrations/20210108204708-associate-users-queue.ts @@ -0,0 +1,28 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("UserQueues", { + userId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + queueId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("UserQueues"); + } +}; diff --git a/backend/src/database/migrations/20210109192513-add-greetingMessage-to-whatsapp.ts b/backend/src/database/migrations/20210109192513-add-greetingMessage-to-whatsapp.ts new file mode 100644 index 0000000..6d3c3be --- /dev/null +++ b/backend/src/database/migrations/20210109192513-add-greetingMessage-to-whatsapp.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "greetingMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "greetingMessage"); + } +}; diff --git a/backend/src/database/migrations/20210109192514-create-companies-table.ts b/backend/src/database/migrations/20210109192514-create-companies-table.ts new file mode 100644 index 0000000..773c786 --- /dev/null +++ b/backend/src/database/migrations/20210109192514-create-companies-table.ts @@ -0,0 +1,39 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Companies", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + phone: { + type: DataTypes.STRING, + allowNull: true + }, + email: { + type: DataTypes.STRING, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Companies"); + } +}; diff --git a/backend/src/database/migrations/20210109192515-add-column-companyId-to-Settings-table.ts b/backend/src/database/migrations/20210109192515-add-column-companyId-to-Settings-table.ts new file mode 100644 index 0000000..7ae9017 --- /dev/null +++ b/backend/src/database/migrations/20210109192515-add-column-companyId-to-Settings-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Settings", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Settings", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192516-add-column-companyId-to-Users-table.ts b/backend/src/database/migrations/20210109192516-add-column-companyId-to-Users-table.ts new file mode 100644 index 0000000..885dc8a --- /dev/null +++ b/backend/src/database/migrations/20210109192516-add-column-companyId-to-Users-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192517-add-column-companyId-to-Contacts-table.ts b/backend/src/database/migrations/20210109192517-add-column-companyId-to-Contacts-table.ts new file mode 100644 index 0000000..d348602 --- /dev/null +++ b/backend/src/database/migrations/20210109192517-add-column-companyId-to-Contacts-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Contacts", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Contacts", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192518-add-column-companyId-to-Messages-table.ts b/backend/src/database/migrations/20210109192518-add-column-companyId-to-Messages-table.ts new file mode 100644 index 0000000..fb1d711 --- /dev/null +++ b/backend/src/database/migrations/20210109192518-add-column-companyId-to-Messages-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192519-add-column-companyId-to-Queues-table.ts b/backend/src/database/migrations/20210109192519-add-column-companyId-to-Queues-table.ts new file mode 100644 index 0000000..02dee46 --- /dev/null +++ b/backend/src/database/migrations/20210109192519-add-column-companyId-to-Queues-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Queues", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Queues", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192520-add-column-companyId-to-Whatsapps-table.ts b/backend/src/database/migrations/20210109192520-add-column-companyId-to-Whatsapps-table.ts new file mode 100644 index 0000000..8628328 --- /dev/null +++ b/backend/src/database/migrations/20210109192520-add-column-companyId-to-Whatsapps-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192521-add-column-companyId-to-Tickets-table.ts b/backend/src/database/migrations/20210109192521-add-column-companyId-to-Tickets-table.ts new file mode 100644 index 0000000..d7ac711 --- /dev/null +++ b/backend/src/database/migrations/20210109192521-add-column-companyId-to-Tickets-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20210109192522-create-plans-table.ts b/backend/src/database/migrations/20210109192522-create-plans-table.ts new file mode 100644 index 0000000..96c3e5f --- /dev/null +++ b/backend/src/database/migrations/20210109192522-create-plans-table.ts @@ -0,0 +1,47 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Plans", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + users: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + connections: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + queues: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + value: { + type: DataTypes.FLOAT, + defaultValue: 0 + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Plans"); + } +}; diff --git a/backend/src/database/migrations/20210109192523-add-column-planId-to-Companies.ts b/backend/src/database/migrations/20210109192523-add-column-planId-to-Companies.ts new file mode 100644 index 0000000..020ffe8 --- /dev/null +++ b/backend/src/database/migrations/20210109192523-add-column-planId-to-Companies.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Companies", "planId", { + type: DataTypes.INTEGER, + references: { model: "Plans", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Companies", "planId"); + } +}; diff --git a/backend/src/database/migrations/20210109192523-add-column-status-and-schedules-to-Companies.ts b/backend/src/database/migrations/20210109192523-add-column-status-and-schedules-to-Companies.ts new file mode 100644 index 0000000..c9bbe71 --- /dev/null +++ b/backend/src/database/migrations/20210109192523-add-column-status-and-schedules-to-Companies.ts @@ -0,0 +1,23 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("Companies", "status", { + type: DataTypes.BOOLEAN, + defaultValue: true + }), + queryInterface.addColumn("Companies", "schedules", { + type: DataTypes.JSONB, + defaultValue: [] + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Companies", "schedules"), + queryInterface.removeColumn("Companies", "status") + ]); + } +}; diff --git a/backend/src/database/migrations/20210109192523-create-ticket-notes.ts b/backend/src/database/migrations/20210109192523-create-ticket-notes.ts new file mode 100644 index 0000000..2163d5e --- /dev/null +++ b/backend/src/database/migrations/20210109192523-create-ticket-notes.ts @@ -0,0 +1,49 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("TicketNotes", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + note: { + type: DataTypes.STRING, + allowNull: false + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Plans"); + } +}; diff --git a/backend/src/database/migrations/20210109192524-create-quick-messages.ts b/backend/src/database/migrations/20210109192524-create-quick-messages.ts new file mode 100644 index 0000000..c390b6d --- /dev/null +++ b/backend/src/database/migrations/20210109192524-create-quick-messages.ts @@ -0,0 +1,40 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("QuickMessages", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + shortcode: { + type: DataTypes.STRING, + allowNull: false + }, + message: { + type: DataTypes.STRING, + allowNull: false + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("QuickMessages"); + } +}; diff --git a/backend/src/database/migrations/20210109192525-add-column-complationMessage-to-whatsapp.ts b/backend/src/database/migrations/20210109192525-add-column-complationMessage-to-whatsapp.ts new file mode 100644 index 0000000..a3bfb4f --- /dev/null +++ b/backend/src/database/migrations/20210109192525-add-column-complationMessage-to-whatsapp.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "complationMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "complationMessage"); + } +}; diff --git a/backend/src/database/migrations/20210109192526-add-column-outOfHoursMessage-to-whatsapp .ts b/backend/src/database/migrations/20210109192526-add-column-outOfHoursMessage-to-whatsapp .ts new file mode 100644 index 0000000..c9f4589 --- /dev/null +++ b/backend/src/database/migrations/20210109192526-add-column-outOfHoursMessage-to-whatsapp .ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "outOfHoursMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "outOfHoursMessage"); + } +}; diff --git a/backend/src/database/migrations/20210109192527-add-column-super-to-Users-table.ts b/backend/src/database/migrations/20210109192527-add-column-super-to-Users-table.ts new file mode 100644 index 0000000..5a5a3b3 --- /dev/null +++ b/backend/src/database/migrations/20210109192527-add-column-super-to-Users-table.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "super", { + type: DataTypes.BOOLEAN, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "super"); + } +}; diff --git a/backend/src/database/migrations/20210109192528-change-column-message-to-quick-messages-table.ts b/backend/src/database/migrations/20210109192528-change-column-message-to-quick-messages-table.ts new file mode 100644 index 0000000..852cc57 --- /dev/null +++ b/backend/src/database/migrations/20210109192528-change-column-message-to-quick-messages-table.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("QuickMessages", "message", { + type: DataTypes.TEXT + }); + }, + down: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("QuickMessages", "message", { + type: DataTypes.STRING + }); + } +}; diff --git a/backend/src/database/migrations/20210109192529-create-helps.ts b/backend/src/database/migrations/20210109192529-create-helps.ts new file mode 100644 index 0000000..ebec49b --- /dev/null +++ b/backend/src/database/migrations/20210109192529-create-helps.ts @@ -0,0 +1,42 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Helps", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + title: { + type: DataTypes.STRING, + allowNull: false + }, + description: { + type: DataTypes.TEXT, + allowNull: true + }, + video: { + type: DataTypes.STRING, + allowNull: true + }, + link: { + type: DataTypes.TEXT, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Helps"); + } +}; diff --git a/backend/src/database/migrations/20210109192530-add-unique-constraint-to-Contacts-table.ts b/backend/src/database/migrations/20210109192530-add-unique-constraint-to-Contacts-table.ts new file mode 100644 index 0000000..32c9fcf --- /dev/null +++ b/backend/src/database/migrations/20210109192530-add-unique-constraint-to-Contacts-table.ts @@ -0,0 +1,17 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addConstraint("Contacts", ["number", "companyId"], { + type: "unique", + name: "number_companyid_unique" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeConstraint( + "Contacts", + "number_companyid_unique" + ); + } +}; diff --git a/backend/src/database/migrations/20210109192531-create-TicketTracking-table.ts b/backend/src/database/migrations/20210109192531-create-TicketTracking-table.ts new file mode 100644 index 0000000..e0f12c1 --- /dev/null +++ b/backend/src/database/migrations/20210109192531-create-TicketTracking-table.ts @@ -0,0 +1,60 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("TicketTraking", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onDelete: "SET NULL" + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onDelete: "SET NULL" + }, + whatsappId: { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onDelete: "SET NULL", + allowNull: true + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onDelete: "SET NULL", + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: true + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: true + }, + queuedAt: { + type: DataTypes.DATE, + allowNull: true + }, + startedAt: { + type: DataTypes.DATE, + allowNull: true + }, + finishedAt: { + type: DataTypes.DATE, + allowNull: true + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("TicketTraking"); + } +}; diff --git a/backend/src/database/migrations/20210109192532-add-column-online-to-Users-table.ts b/backend/src/database/migrations/20210109192532-add-column-online-to-Users-table.ts new file mode 100644 index 0000000..c7aafca --- /dev/null +++ b/backend/src/database/migrations/20210109192532-add-column-online-to-Users-table.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "online", { + type: DataTypes.BOOLEAN, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "online"); + } +}; diff --git a/backend/src/database/migrations/20210109192533-create-UserRatings-table.ts b/backend/src/database/migrations/20210109192533-create-UserRatings-table.ts new file mode 100644 index 0000000..91cd5f2 --- /dev/null +++ b/backend/src/database/migrations/20210109192533-create-UserRatings-table.ts @@ -0,0 +1,46 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("UserRatings", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onDelete: "SET NULL" + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onDelete: "SET NULL" + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onDelete: "SET NULL", + allowNull: true + }, + rate: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + createdAt: { + type: DataTypes.DATE, + allowNull: true + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: true + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("UserRatings"); + } +}; diff --git a/backend/src/database/migrations/20210109192534-add-rated-to-TicketTraking.ts b/backend/src/database/migrations/20210109192534-add-rated-to-TicketTraking.ts new file mode 100644 index 0000000..bc007b1 --- /dev/null +++ b/backend/src/database/migrations/20210109192534-add-rated-to-TicketTraking.ts @@ -0,0 +1,24 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("TicketTraking", "ratingAt", { + type: DataTypes.DATE, + allowNull: true, + defaultValue: null + }), + queryInterface.addColumn("TicketTraking", "rated", { + type: DataTypes.BOOLEAN, + defaultValue: false + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("TicketTraking", "ratingAt"), + queryInterface.removeColumn("TicketTraking", "rated") + ]); + } +}; diff --git a/backend/src/database/migrations/20210109192535-add-column-ratingMessage-to-whatsapp.ts b/backend/src/database/migrations/20210109192535-add-column-ratingMessage-to-whatsapp.ts new file mode 100644 index 0000000..7cfbb4a --- /dev/null +++ b/backend/src/database/migrations/20210109192535-add-column-ratingMessage-to-whatsapp.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "ratingMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "ratingMessage"); + } +}; diff --git a/backend/src/database/migrations/20210109192536-add-unique-constraint-to-Tickets-table.ts b/backend/src/database/migrations/20210109192536-add-unique-constraint-to-Tickets-table.ts new file mode 100644 index 0000000..217426b --- /dev/null +++ b/backend/src/database/migrations/20210109192536-add-unique-constraint-to-Tickets-table.ts @@ -0,0 +1,17 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addConstraint("Tickets", ["contactId", "companyId"], { + type: "unique", + name: "contactid_companyid_unique" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeConstraint( + "Tickets", + "contactid_companyid_unique" + ); + } +}; diff --git a/backend/src/database/migrations/20210818102606-add-uuid-to-tickets.ts b/backend/src/database/migrations/20210818102606-add-uuid-to-tickets.ts new file mode 100644 index 0000000..90669b9 --- /dev/null +++ b/backend/src/database/migrations/20210818102606-add-uuid-to-tickets.ts @@ -0,0 +1,17 @@ +import { QueryInterface, DataTypes, Sequelize } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("Tickets", "uuid", { + type: DataTypes.UUID, + allowNull: true, + defaultValue: Sequelize.literal('uuid_generate_v4()') + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "uuid"); + } +}; diff --git a/backend/src/database/migrations/20210818102607-remove-unique-indexes-to-Queues-table.ts b/backend/src/database/migrations/20210818102607-remove-unique-indexes-to-Queues-table.ts new file mode 100644 index 0000000..c6087c8 --- /dev/null +++ b/backend/src/database/migrations/20210818102607-remove-unique-indexes-to-Queues-table.ts @@ -0,0 +1,25 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeConstraint("Queues", "Queues_color_key"), + queryInterface.removeConstraint("Queues", "Queues_name_key"), + queryInterface.removeIndex("Queues", "Queues_color_key"), + queryInterface.removeIndex("Queues", "Queues_name_key"), + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addConstraint("Queues", ["color"], { + name: "Queues_color_key", + type: 'unique' + }), + queryInterface.addConstraint("Queues", ["name"], { + name: "Queues_name_key", + type: 'unique' + }), + ]); + } +}; diff --git a/backend/src/database/migrations/20210818102608-add-unique-indexes-to-Queues-table.ts b/backend/src/database/migrations/20210818102608-add-unique-indexes-to-Queues-table.ts new file mode 100644 index 0000000..9a68704 --- /dev/null +++ b/backend/src/database/migrations/20210818102608-add-unique-indexes-to-Queues-table.ts @@ -0,0 +1,23 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addConstraint("Queues", ["color", "companyId"], { + name: "Queues_color_key", + type: 'unique' + }), + queryInterface.addConstraint("Queues", ["name", "companyId"], { + name: "Queues_name_key", + type: 'unique' + }), + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeConstraint("Queues", "Queues_color_key"), + queryInterface.removeConstraint("Queues", "Queues_name_key"), + ]); + } +}; diff --git a/backend/src/database/migrations/20210818102609-add-token-to-Whatsapps.ts b/backend/src/database/migrations/20210818102609-add-token-to-Whatsapps.ts new file mode 100644 index 0000000..b17c96f --- /dev/null +++ b/backend/src/database/migrations/20210818102609-add-token-to-Whatsapps.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "token", { + type: DataTypes.TEXT, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "token"); + } +}; diff --git a/backend/src/database/migrations/20211205164404-create-queue-options.ts b/backend/src/database/migrations/20211205164404-create-queue-options.ts new file mode 100644 index 0000000..2009ca9 --- /dev/null +++ b/backend/src/database/migrations/20211205164404-create-queue-options.ts @@ -0,0 +1,51 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("QueueOptions", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + title: { + type: DataTypes.STRING, + allowNull: false, + }, + message: { + type: DataTypes.TEXT, + allowNull: true, + }, + option: { + type: DataTypes.TEXT, + allowNull: true, + }, + queueId: { + type: DataTypes.INTEGER, + references: { model: "Queues", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + parentId: { + type: DataTypes.INTEGER, + references: { model: "QueueOptions", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("QueueOptions"); + } +}; diff --git a/backend/src/database/migrations/20211212125704-add-chatbot-to-tickets.ts b/backend/src/database/migrations/20211212125704-add-chatbot-to-tickets.ts new file mode 100644 index 0000000..a1cdc94 --- /dev/null +++ b/backend/src/database/migrations/20211212125704-add-chatbot-to-tickets.ts @@ -0,0 +1,24 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("Tickets", "chatbot", { + type: DataTypes.BOOLEAN, + allowNull: true, + defaultValue: false + }), + queryInterface.addColumn("Tickets", "queueOptionId", { + type: DataTypes.INTEGER, + references: { model: "QueueOptions", key: "id" }, + onUpdate: "SET null", + onDelete: "SET null", + allowNull: true + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "chatbot"); + } +}; diff --git a/backend/src/database/migrations/20211227010200-create-schedules.ts b/backend/src/database/migrations/20211227010200-create-schedules.ts new file mode 100644 index 0000000..9ae2731 --- /dev/null +++ b/backend/src/database/migrations/20211227010200-create-schedules.ts @@ -0,0 +1,66 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Schedules", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + body: { + type: DataTypes.TEXT, + allowNull: false + }, + sendAt: { + type: DataTypes.DATE, + allowNull: true + }, + sentAt: { + type: DataTypes.DATE, + allowNull: true + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: true + }, + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onUpdate: "SET NULL", + onDelete: "SET NULL", + allowNull: true + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "SET NULL", + onDelete: "SET NULL", + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Schedules"); + } +}; diff --git a/backend/src/database/migrations/20220115114088-add-column-userId-to-QuickMessages-table.ts b/backend/src/database/migrations/20220115114088-add-column-userId-to-QuickMessages-table.ts new file mode 100644 index 0000000..22f33ed --- /dev/null +++ b/backend/src/database/migrations/20220115114088-add-column-userId-to-QuickMessages-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QuickMessages", "userId", { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QuickMessages", "userId"); + } +}; diff --git a/backend/src/database/migrations/20220117130000-create-tags.ts b/backend/src/database/migrations/20220117130000-create-tags.ts new file mode 100644 index 0000000..37870ff --- /dev/null +++ b/backend/src/database/migrations/20220117130000-create-tags.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Tags", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + color: { + type: DataTypes.STRING, + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Tags"); + } +}; diff --git a/backend/src/database/migrations/20220117134400-associate-tickets-tags.ts b/backend/src/database/migrations/20220117134400-associate-tickets-tags.ts new file mode 100644 index 0000000..6df85cb --- /dev/null +++ b/backend/src/database/migrations/20220117134400-associate-tickets-tags.ts @@ -0,0 +1,34 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("TicketTags", { + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + tagId: { + type: DataTypes.INTEGER, + references: { model: "Tags", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("TicketTags"); + } +}; diff --git a/backend/src/database/migrations/20220122160900-add-status-to-schedules.ts b/backend/src/database/migrations/20220122160900-add-status-to-schedules.ts new file mode 100644 index 0000000..15639d7 --- /dev/null +++ b/backend/src/database/migrations/20220122160900-add-status-to-schedules.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Schedules", "status", { + type: DataTypes.STRING, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Schedules", "status"); + } +}; diff --git a/backend/src/database/migrations/20220220014719-add-farewellMessage-to-whatsapp.ts b/backend/src/database/migrations/20220220014719-add-farewellMessage-to-whatsapp.ts new file mode 100644 index 0000000..40120bf --- /dev/null +++ b/backend/src/database/migrations/20220220014719-add-farewellMessage-to-whatsapp.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "farewellMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "farewellMessage"); + } +}; diff --git a/backend/src/database/migrations/20220221014717-add-provider-whatsapp.ts b/backend/src/database/migrations/20220221014717-add-provider-whatsapp.ts new file mode 100644 index 0000000..263a1a3 --- /dev/null +++ b/backend/src/database/migrations/20220221014717-add-provider-whatsapp.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "provider", { + type: DataTypes.TEXT, + defaultValue: "stable" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "provider"); + } +}; diff --git a/backend/src/database/migrations/20220221014718-add-remoteJid-messages.ts b/backend/src/database/migrations/20220221014718-add-remoteJid-messages.ts new file mode 100644 index 0000000..fd911ad --- /dev/null +++ b/backend/src/database/migrations/20220221014718-add-remoteJid-messages.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "remoteJid", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "remoteJid"); + } +}; diff --git a/backend/src/database/migrations/20220221014719-add-jsonMessage-messages.ts b/backend/src/database/migrations/20220221014719-add-jsonMessage-messages.ts new file mode 100644 index 0000000..72f23a6 --- /dev/null +++ b/backend/src/database/migrations/20220221014719-add-jsonMessage-messages.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "dataJson", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "dataJson"); + } +}; diff --git a/backend/src/database/migrations/20220221014720-add-participant-messages.ts b/backend/src/database/migrations/20220221014720-add-participant-messages.ts new file mode 100644 index 0000000..65fe5a1 --- /dev/null +++ b/backend/src/database/migrations/20220221014720-add-participant-messages.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "participant", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "participant"); + } +}; diff --git a/backend/src/database/migrations/20220221014721-create-baileys.ts b/backend/src/database/migrations/20220221014721-create-baileys.ts new file mode 100644 index 0000000..4544385 --- /dev/null +++ b/backend/src/database/migrations/20220221014721-create-baileys.ts @@ -0,0 +1,38 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Baileys", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + whatsappId: { + type: DataTypes.INTEGER, + primaryKey: true + }, + contacts: { + type: DataTypes.TEXT, + allowNull: true + }, + chats: { + type: DataTypes.TEXT, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Baileys"); + } +}; diff --git a/backend/src/database/migrations/20220315110000-create-ContactLists-table.ts b/backend/src/database/migrations/20220315110000-create-ContactLists-table.ts new file mode 100644 index 0000000..7406ef2 --- /dev/null +++ b/backend/src/database/migrations/20220315110000-create-ContactLists-table.ts @@ -0,0 +1,37 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("ContactLists", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("ContactLists"); + } +}; diff --git a/backend/src/database/migrations/20220315110001-create-ContactListItems-table.ts b/backend/src/database/migrations/20220315110001-create-ContactListItems-table.ts new file mode 100644 index 0000000..009f7a9 --- /dev/null +++ b/backend/src/database/migrations/20220315110001-create-ContactListItems-table.ts @@ -0,0 +1,56 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("ContactListItems", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + number: { + type: DataTypes.STRING, + allowNull: false + }, + email: { + type: DataTypes.STRING, + allowNull: true + }, + contactListId: { + type: DataTypes.INTEGER, + references: { model: "ContactLists", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + isWhatsappValid: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("ContactListItems"); + } +}; diff --git a/backend/src/database/migrations/20220315110002-create-Campaigns-table.ts b/backend/src/database/migrations/20220315110002-create-Campaigns-table.ts new file mode 100644 index 0000000..2d1f568 --- /dev/null +++ b/backend/src/database/migrations/20220315110002-create-Campaigns-table.ts @@ -0,0 +1,126 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Campaigns", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + message1: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + message2: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + message3: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + message4: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + message5: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + confirmationMessage1: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + confirmationMessage2: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + confirmationMessage3: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + confirmationMessage4: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + confirmationMessage5: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + status: { + type: DataTypes.STRING, + allowNull: true + }, + confirmation: { + type: DataTypes.BOOLEAN, + allowNull: true, + defaultValue: false + }, + mediaPath: { + type: DataTypes.TEXT, + allowNull: true + }, + mediaName: { + type: DataTypes.TEXT, + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + contactListId: { + type: DataTypes.INTEGER, + references: { model: "ContactLists", key: "id" }, + onUpdate: "SET NULL", + onDelete: "SET NULL", + allowNull: true + }, + whatsappId: { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "SET NULL", + onDelete: "SET NULL", + allowNull: true + }, + scheduledAt: { + type: DataTypes.DATE, + allowNull: true + }, + completedAt: { + type: DataTypes.DATE, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Campaigns"); + } +}; diff --git a/backend/src/database/migrations/20220315110004-create-CampaignSettings-table.ts b/backend/src/database/migrations/20220315110004-create-CampaignSettings-table.ts new file mode 100644 index 0000000..4d796a9 --- /dev/null +++ b/backend/src/database/migrations/20220315110004-create-CampaignSettings-table.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("CampaignSettings", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + key: { + type: DataTypes.STRING, + allowNull: false + }, + value: { + type: DataTypes.TEXT, + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("CampaignSettings"); + } +}; diff --git a/backend/src/database/migrations/20220315110005-remove-constraint-to-Settings.ts b/backend/src/database/migrations/20220315110005-remove-constraint-to-Settings.ts new file mode 100644 index 0000000..f794a99 --- /dev/null +++ b/backend/src/database/migrations/20220315110005-remove-constraint-to-Settings.ts @@ -0,0 +1,27 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.sequelize.query('DELETE FROM "Settings"'), + queryInterface.removeConstraint("Settings", "Settings_pkey"), + queryInterface.addColumn("Settings", "id", { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.sequelize.query('DELETE FROM "Settings"'), + queryInterface.removeColumn("Settings", "id"), + queryInterface.addConstraint("Settings", ["key"], { + type: "primary key", + name: "Settings_pkey" + }) + ]); + } +}; diff --git a/backend/src/database/migrations/20220321130000-create-CampaignShipping.ts b/backend/src/database/migrations/20220321130000-create-CampaignShipping.ts new file mode 100644 index 0000000..6570474 --- /dev/null +++ b/backend/src/database/migrations/20220321130000-create-CampaignShipping.ts @@ -0,0 +1,72 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("CampaignShipping", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + jobId: { + type: DataTypes.STRING, + allowNull: true + }, + number: { + type: DataTypes.STRING, + allowNull: false + }, + message: { + type: DataTypes.TEXT, + allowNull: false + }, + confirmationMessage: { + type: DataTypes.TEXT, + allowNull: true + }, + confirmation: { + type: DataTypes.BOOLEAN, + allowNull: true + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "ContactListItems", key: "id" }, + onUpdate: "SET NULL", + onDelete: "SET NULL", + allowNull: true + }, + campaignId: { + type: DataTypes.INTEGER, + references: { model: "Campaigns", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + confirmationRequestedAt: { + type: DataTypes.DATE, + allowNull: true + }, + confirmedAt: { + type: DataTypes.DATE, + allowNull: true + }, + deliveredAt: { + type: DataTypes.DATE, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("CampaignShipping"); + } +}; diff --git a/backend/src/database/migrations/20220404000000-add-column-queueId-to-Messages-table.ts b/backend/src/database/migrations/20220404000000-add-column-queueId-to-Messages-table.ts new file mode 100644 index 0000000..fc663a1 --- /dev/null +++ b/backend/src/database/migrations/20220404000000-add-column-queueId-to-Messages-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "queueId", { + type: DataTypes.INTEGER, + references: { model: "Queues", key: "id" }, + onUpdate: "SET NULL", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "queueId"); + } +}; diff --git a/backend/src/database/migrations/20220406000000-add-column-dueDate-to-Companies.ts b/backend/src/database/migrations/20220406000000-add-column-dueDate-to-Companies.ts new file mode 100644 index 0000000..e5083c7 --- /dev/null +++ b/backend/src/database/migrations/20220406000000-add-column-dueDate-to-Companies.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Companies", "dueDate", { + type: DataTypes.DATE, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Companies", "dueDate"); + } +}; diff --git a/backend/src/database/migrations/20220406000001-add-column-recurrence-to-Companies.ts b/backend/src/database/migrations/20220406000001-add-column-recurrence-to-Companies.ts new file mode 100644 index 0000000..09d8760 --- /dev/null +++ b/backend/src/database/migrations/20220406000001-add-column-recurrence-to-Companies.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Companies", "recurrence", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: "" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Companies", "recurrence"); + } +}; diff --git a/backend/src/database/migrations/20220411000000-add-column-startTime-and-endTime-to-Queues.ts b/backend/src/database/migrations/20220411000000-add-column-startTime-and-endTime-to-Queues.ts new file mode 100644 index 0000000..a90dba1 --- /dev/null +++ b/backend/src/database/migrations/20220411000000-add-column-startTime-and-endTime-to-Queues.ts @@ -0,0 +1,28 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("Queues", "startTime", { + type: DataTypes.STRING, + defaultValue: null + }), + queryInterface.addColumn("Queues", "endTime", { + type: DataTypes.STRING, + defaultValue: null + }), + queryInterface.addColumn("Queues", "outOfHoursMessage", { + type: DataTypes.TEXT, + defaultValue: null + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Queues", "startTime"), + queryInterface.removeColumn("Queues", "endTime"), + queryInterface.removeColumn("Queues", "outOfHoursMessage") + ]); + } +}; diff --git a/backend/src/database/migrations/20220411000001-remove-column-startTime-and-endTime-to-Queues.ts b/backend/src/database/migrations/20220411000001-remove-column-startTime-and-endTime-to-Queues.ts new file mode 100644 index 0000000..7f005b7 --- /dev/null +++ b/backend/src/database/migrations/20220411000001-remove-column-startTime-and-endTime-to-Queues.ts @@ -0,0 +1,28 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Queues", "startTime"), + queryInterface.removeColumn("Queues", "endTime"), + queryInterface.removeColumn("Queues", "outOfHoursMessage") + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("Queues", "startTime", { + type: DataTypes.STRING, + defaultValue: null + }), + queryInterface.addColumn("Queues", "endTime", { + type: DataTypes.STRING, + defaultValue: null + }), + queryInterface.addColumn("Queues", "outOfHoursMessage", { + type: DataTypes.TEXT, + defaultValue: null + }) + ]); + } +}; diff --git a/backend/src/database/migrations/20220411000002-add-column-schedules-and-outOfHoursMessage-to-Queues.ts b/backend/src/database/migrations/20220411000002-add-column-schedules-and-outOfHoursMessage-to-Queues.ts new file mode 100644 index 0000000..72023d0 --- /dev/null +++ b/backend/src/database/migrations/20220411000002-add-column-schedules-and-outOfHoursMessage-to-Queues.ts @@ -0,0 +1,23 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addColumn("Queues", "schedules", { + type: DataTypes.JSONB, + defaultValue: [] + }), + queryInterface.addColumn("Queues", "outOfHoursMessage", { + type: DataTypes.TEXT, + allowNull: true + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Queues", "schedules"), + queryInterface.removeColumn("Queues", "outOfHoursMessage") + ]); + } +}; diff --git a/backend/src/database/migrations/20220411000003-create-table-Announcements.ts b/backend/src/database/migrations/20220411000003-create-table-Announcements.ts new file mode 100644 index 0000000..9123242 --- /dev/null +++ b/backend/src/database/migrations/20220411000003-create-table-Announcements.ts @@ -0,0 +1,57 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Announcements", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + priority: { + type: DataTypes.INTEGER, + allowNull: true + }, + title: { + type: DataTypes.STRING, + allowNull: false + }, + text: { + type: DataTypes.TEXT, + allowNull: false + }, + mediaPath: { + type: DataTypes.TEXT, + allowNull: true + }, + mediaName: { + type: DataTypes.TEXT, + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + status: { + type: DataTypes.BOOLEAN, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Announcements"); + } +}; diff --git a/backend/src/database/migrations/20220425000000-create-table-Chats.ts b/backend/src/database/migrations/20220425000000-create-table-Chats.ts new file mode 100644 index 0000000..5659c5e --- /dev/null +++ b/backend/src/database/migrations/20220425000000-create-table-Chats.ts @@ -0,0 +1,54 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Chats", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + title: { + type: DataTypes.TEXT, + defaultValue: "", + allowNull: true + }, + uuid: { + type: DataTypes.STRING, + defaultValue: "", + allowNull: true + }, + ownerId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + lastMessage: { + type: DataTypes.TEXT, + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Chats"); + } +}; diff --git a/backend/src/database/migrations/20220425000001-create-table-ChatUsers.ts b/backend/src/database/migrations/20220425000001-create-table-ChatUsers.ts new file mode 100644 index 0000000..67adc5a --- /dev/null +++ b/backend/src/database/migrations/20220425000001-create-table-ChatUsers.ts @@ -0,0 +1,44 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("ChatUsers", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + chatId: { + type: DataTypes.INTEGER, + references: { model: "Chats", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + unreads: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("ChatUsers"); + } +}; diff --git a/backend/src/database/migrations/20220425000002-create-table-ChatMessages.ts b/backend/src/database/migrations/20220425000002-create-table-ChatMessages.ts new file mode 100644 index 0000000..1dfbbd3 --- /dev/null +++ b/backend/src/database/migrations/20220425000002-create-table-ChatMessages.ts @@ -0,0 +1,53 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("ChatMessages", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + chatId: { + type: DataTypes.INTEGER, + references: { model: "Chats", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + senderId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + message: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: "" + }, + mediaPath: { + type: DataTypes.TEXT, + allowNull: true + }, + mediaName: { + type: DataTypes.TEXT, + allowNull: true + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("ChatMessages"); + } +}; diff --git a/backend/src/database/migrations/20220512000001-create-Indexes.ts b/backend/src/database/migrations/20220512000001-create-Indexes.ts new file mode 100644 index 0000000..ae67b3b --- /dev/null +++ b/backend/src/database/migrations/20220512000001-create-Indexes.ts @@ -0,0 +1,37 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.addIndex("Schedules", ["companyId"], { + name: "idx_sched_company_id" + }), + queryInterface.addIndex("Contacts", ["companyId"], { + name: "idx_cont_company_id" + }), + queryInterface.addIndex("Tags", ["companyId"], { + name: "idx_tg_company_id" + }), + queryInterface.addIndex("Messages", ["companyId", "ticketId"], { + name: "idx_ms_company_id_ticket_id" + }), + queryInterface.addIndex("CampaignShipping", ["campaignId"], { + name: "idx_cpsh_campaign_id" + }), + queryInterface.addIndex("ContactListItems", ["contactListId"], { + name: "idx_ctli_contact_list_id" + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeIndex("Schedules", "idx_sched_company_id"), + queryInterface.removeIndex("Contacts", "idx_cont_company_id"), + queryInterface.removeIndex("Tags", "idx_tg_company_id"), + queryInterface.removeIndex("Messages", "idx_ms_company_id_ticket_id"), + queryInterface.removeIndex("CampaignShipping", "idx_cpsh_campaign_id"), + queryInterface.removeIndex("ContactListItems", "idx_ctli_contact_list_id") + ]); + } +}; diff --git a/backend/src/database/migrations/20220512000002-create-subscriptions.ts b/backend/src/database/migrations/20220512000002-create-subscriptions.ts new file mode 100644 index 0000000..2c25559 --- /dev/null +++ b/backend/src/database/migrations/20220512000002-create-subscriptions.ts @@ -0,0 +1,58 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Subscriptions", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + expiresAt: { + type: DataTypes.DATE, + allowNull: false + }, + userPriceCents: { + type: DataTypes.INTEGER + }, + whatsPriceCents: { + type: DataTypes.INTEGER + }, + lastInvoiceUrl: { + type: DataTypes.STRING, + allowNull: true + }, + lastPlanChange: { + type: DataTypes.DATE, + allowNull: true + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + providerSubscriptionId: { + type: DataTypes.STRING, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Subscriptions"); + } +}; diff --git a/backend/src/database/migrations/20220512000003-create-invoices.ts b/backend/src/database/migrations/20220512000003-create-invoices.ts new file mode 100644 index 0000000..108e24f --- /dev/null +++ b/backend/src/database/migrations/20220512000003-create-invoices.ts @@ -0,0 +1,44 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Invoices", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + detail: { + type: DataTypes.STRING, + }, + status: { + type: DataTypes.STRING, + }, + value: { + type: DataTypes.FLOAT + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + }, + dueDate: { + type: DataTypes.STRING, + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Invoices"); + } +}; diff --git a/backend/src/database/migrations/20220723000001-add-mediaPath-to-quickmessages.ts b/backend/src/database/migrations/20220723000001-add-mediaPath-to-quickmessages.ts new file mode 100644 index 0000000..89dba5f --- /dev/null +++ b/backend/src/database/migrations/20220723000001-add-mediaPath-to-quickmessages.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QuickMessages", "mediaPath", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QuickMessages", "mediaPath"); + } +}; diff --git a/backend/src/database/migrations/20220723000002-add-mediaName-to-quickemessages.ts b/backend/src/database/migrations/20220723000002-add-mediaName-to-quickemessages.ts new file mode 100644 index 0000000..968d98a --- /dev/null +++ b/backend/src/database/migrations/20220723000002-add-mediaName-to-quickemessages.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QuickMessages", "mediaName", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QuickMessages", "mediaName"); + } +}; diff --git a/backend/src/database/migrations/20222016014720-create-baileys-chats.ts b/backend/src/database/migrations/20222016014720-create-baileys-chats.ts new file mode 100644 index 0000000..e11e660 --- /dev/null +++ b/backend/src/database/migrations/20222016014720-create-baileys-chats.ts @@ -0,0 +1,44 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("BaileysChats", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + whatsappId: { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + jid: { + type: DataTypes.STRING, + allowNull: false + }, + conversationTimestamp: { + type: DataTypes.STRING, + allowNull: false + }, + unreadCount: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("BaileysChats"); + } +}; diff --git a/backend/src/database/migrations/20222016014721-create-baileys-chats Messages.ts b/backend/src/database/migrations/20222016014721-create-baileys-chats Messages.ts new file mode 100644 index 0000000..993f52b --- /dev/null +++ b/backend/src/database/migrations/20222016014721-create-baileys-chats Messages.ts @@ -0,0 +1,42 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("BaileysMessages", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + whatsappId: { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + baileysChatId: { + type: DataTypes.INTEGER, + references: { model: "BaileysChats", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + jsonMessage: { + type: DataTypes.JSON, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("BaileysMessages"); + } +}; diff --git a/backend/src/database/migrations/20230106164900-add-useCampaigns-Plans.ts b/backend/src/database/migrations/20230106164900-add-useCampaigns-Plans.ts new file mode 100644 index 0000000..d88e967 --- /dev/null +++ b/backend/src/database/migrations/20230106164900-add-useCampaigns-Plans.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Plans", "useCampaigns", { + type: DataTypes.BOOLEAN, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Plans", "useCampaigns"); + } +}; diff --git a/backend/src/database/migrations/20230106164900-add-useExternalApi-Plans.ts b/backend/src/database/migrations/20230106164900-add-useExternalApi-Plans.ts new file mode 100644 index 0000000..ebdfa22 --- /dev/null +++ b/backend/src/database/migrations/20230106164900-add-useExternalApi-Plans.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Plans", "useExternalApi", { + type: DataTypes.BOOLEAN, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Plans", "useExternalApi"); + } +}; diff --git a/backend/src/database/migrations/20230106164900-add-useInternalChat-Plans.ts b/backend/src/database/migrations/20230106164900-add-useInternalChat-Plans.ts new file mode 100644 index 0000000..950ff20 --- /dev/null +++ b/backend/src/database/migrations/20230106164900-add-useInternalChat-Plans.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Plans", "useInternalChat", { + type: DataTypes.BOOLEAN, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Plans", "useInternalChat"); + } +}; diff --git a/backend/src/database/migrations/20230106164900-add-useSchedules-Plans.ts b/backend/src/database/migrations/20230106164900-add-useSchedules-Plans.ts new file mode 100644 index 0000000..76a3e9e --- /dev/null +++ b/backend/src/database/migrations/20230106164900-add-useSchedules-Plans.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Plans", "useSchedules", { + type: DataTypes.BOOLEAN, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Plans", "useSchedules"); + } +}; diff --git a/backend/src/database/migrations/20230303223001-add-amountUsedBotQueues-to-tickets.ts b/backend/src/database/migrations/20230303223001-add-amountUsedBotQueues-to-tickets.ts new file mode 100644 index 0000000..0fa8ed9 --- /dev/null +++ b/backend/src/database/migrations/20230303223001-add-amountUsedBotQueues-to-tickets.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "amountUsedBotQueues", { + type: DataTypes.INTEGER + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "amountUsedBotQueues"); + } +}; diff --git a/backend/src/database/migrations/20230417203900-add-allTickets-user.ts b/backend/src/database/migrations/20230417203900-add-allTickets-user.ts new file mode 100644 index 0000000..6275f42 --- /dev/null +++ b/backend/src/database/migrations/20230417203900-add-allTickets-user.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "allTicket", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "desabled" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "allTicket"); + } +}; diff --git a/backend/src/database/migrations/20230603212335-create-QueueIntegrations.ts b/backend/src/database/migrations/20230603212335-create-QueueIntegrations.ts new file mode 100644 index 0000000..e7fac19 --- /dev/null +++ b/backend/src/database/migrations/20230603212335-create-QueueIntegrations.ts @@ -0,0 +1,48 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("QueueIntegrations", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + type: { + type: DataTypes.STRING, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + projectName: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + jsonContent: { + type: DataTypes.TEXT, + allowNull: false, + }, + language: { + type: DataTypes.STRING, + allowNull: false, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("QueueIntegrations"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230603212337-add-urlN8N-QueueIntegrations.ts b/backend/src/database/migrations/20230603212337-add-urlN8N-QueueIntegrations.ts new file mode 100644 index 0000000..1348c62 --- /dev/null +++ b/backend/src/database/migrations/20230603212337-add-urlN8N-QueueIntegrations.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QueueIntegrations", "urlN8N", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QueueIntegrations", "urlN8N"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230623095932-add-whatsapp-to-user.ts b/backend/src/database/migrations/20230623095932-add-whatsapp-to-user.ts new file mode 100644 index 0000000..b09393b --- /dev/null +++ b/backend/src/database/migrations/20230623095932-add-whatsapp-to-user.ts @@ -0,0 +1,17 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "whatsappId", { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL", + allowNull: true + },); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "whatsappId"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230623133903-add-chatbotAt-ticket-tracking.ts b/backend/src/database/migrations/20230623133903-add-chatbotAt-ticket-tracking.ts new file mode 100644 index 0000000..cbedb37 --- /dev/null +++ b/backend/src/database/migrations/20230623133903-add-chatbotAt-ticket-tracking.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("TicketTraking", "chatbotAt", { + type: DataTypes.DATE, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("TicketTraking", "chatbotAt"); + } +}; diff --git a/backend/src/database/migrations/20230628134807-add-orderQueue-Queue.ts b/backend/src/database/migrations/20230628134807-add-orderQueue-Queue.ts new file mode 100644 index 0000000..4ab7aad --- /dev/null +++ b/backend/src/database/migrations/20230628134807-add-orderQueue-Queue.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Queues", "orderQueue", { + type: DataTypes.INTEGER + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Queues", "orderQueue"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230711094417-add-column-companyId-to-QueueIntegrations-table.ts b/backend/src/database/migrations/20230711094417-add-column-companyId-to-QueueIntegrations-table.ts new file mode 100644 index 0000000..94a1855 --- /dev/null +++ b/backend/src/database/migrations/20230711094417-add-column-companyId-to-QueueIntegrations-table.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QueueIntegrations", "companyId", { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QueueIntegrations", "companyId"); + } +}; diff --git a/backend/src/database/migrations/20230711111701-add-sendIdQueue-to-whatsapp.ts b/backend/src/database/migrations/20230711111701-add-sendIdQueue-to-whatsapp.ts new file mode 100644 index 0000000..1d3174f --- /dev/null +++ b/backend/src/database/migrations/20230711111701-add-sendIdQueue-to-whatsapp.ts @@ -0,0 +1,13 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "sendIdQueue", { + type: DataTypes.INTEGER + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "sendIdQueue"); + } +}; diff --git a/backend/src/database/migrations/20230714113901-create-Files.ts b/backend/src/database/migrations/20230714113901-create-Files.ts new file mode 100644 index 0000000..51afb6e --- /dev/null +++ b/backend/src/database/migrations/20230714113901-create-Files.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Files", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + message: { + type: DataTypes.TEXT, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Files"); + } +}; diff --git a/backend/src/database/migrations/20230714113902-create-fileOptions.ts b/backend/src/database/migrations/20230714113902-create-fileOptions.ts new file mode 100644 index 0000000..84c66c1 --- /dev/null +++ b/backend/src/database/migrations/20230714113902-create-fileOptions.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("FilesOptions", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + path: { + type: DataTypes.STRING, + allowNull: false + }, + fileId: { + type: DataTypes.INTEGER, + references: { model: "Files", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("FilesOptions"); + } +}; diff --git a/backend/src/database/migrations/20230723301001-add-kanban-to-Tags.ts b/backend/src/database/migrations/20230723301001-add-kanban-to-Tags.ts new file mode 100644 index 0000000..421c588 --- /dev/null +++ b/backend/src/database/migrations/20230723301001-add-kanban-to-Tags.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tags", "kanban", { + type: DataTypes.INTEGER, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tags", "kanban"); + } +}; diff --git a/backend/src/database/migrations/20230801081907-add-collumns-Ticket.ts b/backend/src/database/migrations/20230801081907-add-collumns-Ticket.ts new file mode 100644 index 0000000..89f56f6 --- /dev/null +++ b/backend/src/database/migrations/20230801081907-add-collumns-Ticket.ts @@ -0,0 +1,17 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "fromMe", { + type: DataTypes.BOOLEAN, + defaultValue: false, + allowNull: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Tickets", "fromMe"), + ]) + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230813114236-change-ticket-lastMessage-column-type.ts b/backend/src/database/migrations/20230813114236-change-ticket-lastMessage-column-type.ts new file mode 100644 index 0000000..f85f44b --- /dev/null +++ b/backend/src/database/migrations/20230813114236-change-ticket-lastMessage-column-type.ts @@ -0,0 +1,17 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("Tickets", "lastMessage", { + defaultValue: "", + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("Tickets", "lastMessage", { + defaultValue: "", + type: DataTypes.TEXT + }); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230824082607-add-mediaType-FilesOptions.ts b/backend/src/database/migrations/20230824082607-add-mediaType-FilesOptions.ts new file mode 100644 index 0000000..b2a385e --- /dev/null +++ b/backend/src/database/migrations/20230824082607-add-mediaType-FilesOptions.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("FilesOptions", "mediaType", { + type: DataTypes.STRING, + defaultValue: "", + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("FilesOptions", "mediaType"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230828143411-add-Integrations-to-tickets.ts b/backend/src/database/migrations/20230828143411-add-Integrations-to-tickets.ts new file mode 100644 index 0000000..41da7b7 --- /dev/null +++ b/backend/src/database/migrations/20230828143411-add-Integrations-to-tickets.ts @@ -0,0 +1,26 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "useIntegration", { + type: DataTypes.BOOLEAN, + defaultValue: false, + allowNull: true, + + }), + queryInterface.addColumn("Tickets", "integrationId", { + references: { model: "QueueIntegrations", key: "id" }, + type: DataTypes.INTEGER, + defaultValue: null, + allowNull: true, + + }); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Tickets", "useIntegration"), + queryInterface.removeColumn("Tickets", "integrationId"), + ]) + } +}; diff --git a/backend/src/database/migrations/20230828144000-create-prompts.ts b/backend/src/database/migrations/20230828144000-create-prompts.ts new file mode 100644 index 0000000..aad49fb --- /dev/null +++ b/backend/src/database/migrations/20230828144000-create-prompts.ts @@ -0,0 +1,103 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +interface ExistingTables { + [key: string]: any; +} + +module.exports = { + up: async (queryInterface: QueryInterface) => { + const table = "Prompts"; + + const existingTables: ExistingTables = await queryInterface.showAllTables(); + + if (!existingTables.includes(table)) { + return queryInterface.createTable(table, { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.TEXT, + allowNull: false + }, + apiKey: { + type: DataTypes.TEXT, + allowNull: false + }, + prompt: { + type: DataTypes.TEXT, + allowNull: false + }, + maxTokens: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 100 + }, + maxMessages: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 10 + }, + temperature: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 1 + }, + promptTokens: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + completionTokens: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + totalTokens: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + voice: { + type: DataTypes.TEXT, + allowNull: true + }, + voiceKey: { + type: DataTypes.TEXT, + allowNull: true + }, + voiceRegion: { + type: DataTypes.TEXT, + allowNull: true + }, + queueId: { + type: DataTypes.INTEGER, + references: { model: "Queues", key: "id" }, + onUpdate: "NO ACTION", + onDelete: "NO ACTION", + allowNull: false + }, + companyId: { + type: DataTypes.INTEGER, + references: { model: "Companies", key: "id" }, + onUpdate: "NO ACTION", + onDelete: "NO ACTION", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + } + }, + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Prompts"); + } +}; diff --git a/backend/src/database/migrations/20230828144100-add-column-promptid-into-whatsapps.ts b/backend/src/database/migrations/20230828144100-add-column-promptid-into-whatsapps.ts new file mode 100644 index 0000000..0fcfba1 --- /dev/null +++ b/backend/src/database/migrations/20230828144100-add-column-promptid-into-whatsapps.ts @@ -0,0 +1,26 @@ +import { QueryInterface, DataTypes } from "sequelize"; +interface ExistingColumns { + }; + +module.exports = { + up: async (queryInterface: QueryInterface) => { + const table = "Whatsapps"; + const column = "promptId"; + + const tableInfo: ExistingColumns = await queryInterface.describeTable(table); + if (tableInfo[column]) { + return Promise.resolve(); + } + + return queryInterface.addColumn(table, column, { + type: DataTypes.INTEGER, + references: { model: "Prompts", key: "id" }, + onUpdate: "RESTRICT", + onDelete: "RESTRICT" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "promptId"); + } +}; diff --git a/backend/src/database/migrations/20230831093000-add-useKanban-Plans.ts b/backend/src/database/migrations/20230831093000-add-useKanban-Plans.ts new file mode 100644 index 0000000..0dece8e --- /dev/null +++ b/backend/src/database/migrations/20230831093000-add-useKanban-Plans.ts @@ -0,0 +1,14 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Plans", "useKanban", { + type: DataTypes.BOOLEAN, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Plans", "useKanban"); + } +}; diff --git a/backend/src/database/migrations/20230922212337-add-integrationId-Queues.ts b/backend/src/database/migrations/20230922212337-add-integrationId-Queues.ts new file mode 100644 index 0000000..7f370ef --- /dev/null +++ b/backend/src/database/migrations/20230922212337-add-integrationId-Queues.ts @@ -0,0 +1,23 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Queues", "integrationId", { + type: DataTypes.INTEGER, + references: { model: "QueueIntegrations", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }), + queryInterface.addColumn("Whatsapps", "integrationId", { + type: DataTypes.INTEGER, + references: { model: "QueueIntegrations", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }) + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Queues", "integrationId"), + queryInterface.removeColumn("Whatsapps", "integrationId"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20230924212337-add-fileListId-Campaigns.ts b/backend/src/database/migrations/20230924212337-add-fileListId-Campaigns.ts new file mode 100644 index 0000000..ebb213b --- /dev/null +++ b/backend/src/database/migrations/20230924212337-add-fileListId-Campaigns.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Campaigns", "fileListId", { + type: DataTypes.INTEGER, + references: { model: "Files", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }) + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Campaigns", "fileListId") + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231111185822-add_reset_password_column.ts b/backend/src/database/migrations/20231111185822-add_reset_password_column.ts new file mode 100644 index 0000000..8154356 --- /dev/null +++ b/backend/src/database/migrations/20231111185822-add_reset_password_column.ts @@ -0,0 +1 @@ +'use strict';module.exports={up:async(queryInterface,Sequelize)=>{await queryInterface.addColumn('Users','resetPassword',{type:Sequelize.STRING,allowNull:true,});},down:async(queryInterface,Sequelize)=>{await queryInterface.removeColumn('Users','resetPassword');},}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231117000001-add-mediaName-to-schedules.ts b/backend/src/database/migrations/20231117000001-add-mediaName-to-schedules.ts new file mode 100644 index 0000000..c8395a2 --- /dev/null +++ b/backend/src/database/migrations/20231117000001-add-mediaName-to-schedules.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Schedules", "mediaName", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Schedules", "mediaName"); + } +}; diff --git a/backend/src/database/migrations/20231117000001-add-mediaPath-to-schedules.ts b/backend/src/database/migrations/20231117000001-add-mediaPath-to-schedules.ts new file mode 100644 index 0000000..257b24d --- /dev/null +++ b/backend/src/database/migrations/20231117000001-add-mediaPath-to-schedules.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Schedules", "mediaPath", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Schedules", "mediaPath"); + } +}; diff --git a/backend/src/database/migrations/20231127113000-add-columns-Plans.ts b/backend/src/database/migrations/20231127113000-add-columns-Plans.ts new file mode 100644 index 0000000..0bd3c98 --- /dev/null +++ b/backend/src/database/migrations/20231127113000-add-columns-Plans.ts @@ -0,0 +1,19 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Plans", "useOpenAi", { + type: DataTypes.BOOLEAN, + defaultValue: true + }), + queryInterface.addColumn("Plans", "useIntegrations", { + type: DataTypes.BOOLEAN, + defaultValue: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Plans", "useOpenAi"), + queryInterface.removeColumn("Plans", "useIntegrations"); + } +}; diff --git a/backend/src/database/migrations/20231128123537-add-typebot-QueueIntegrations.ts b/backend/src/database/migrations/20231128123537-add-typebot-QueueIntegrations.ts new file mode 100644 index 0000000..6cee910 --- /dev/null +++ b/backend/src/database/migrations/20231128123537-add-typebot-QueueIntegrations.ts @@ -0,0 +1,33 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QueueIntegrations", "typebotSlug", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "" + }), + queryInterface.addColumn("QueueIntegrations", "typebotExpires", { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }), + queryInterface.addColumn("QueueIntegrations", "typebotKeywordFinish", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "" + }), + queryInterface.addColumn("QueueIntegrations", "typebotUnknownMessage", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QueueIntegrations", "typebotSlug"), + queryInterface.removeColumn("QueueIntegrations", "typebotExpires"), + queryInterface.removeColumn("QueueIntegrations", "typebotKeywordFinish"), + queryInterface.removeColumn("QueueIntegrations", "typebotUnknownMessage"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231202143411-add-typebotSessionId-to-tickets.ts b/backend/src/database/migrations/20231202143411-add-typebotSessionId-to-tickets.ts new file mode 100644 index 0000000..9805d93 --- /dev/null +++ b/backend/src/database/migrations/20231202143411-add-typebotSessionId-to-tickets.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "typebotSessionId", { + type: DataTypes.STRING, + defaultValue: null, + allowNull: true, + + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "typebotSessionId"); + } +}; diff --git a/backend/src/database/migrations/20231207080337-add-typebotDelayMessage-QueueIntegrations.ts b/backend/src/database/migrations/20231207080337-add-typebotDelayMessage-QueueIntegrations.ts new file mode 100644 index 0000000..0985f6c --- /dev/null +++ b/backend/src/database/migrations/20231207080337-add-typebotDelayMessage-QueueIntegrations.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QueueIntegrations", "typebotDelayMessage", { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 1000 + }) + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QueueIntegrations", "typebotDelayMessage") + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231207085011-add-typebotStatus-to-tickets.ts b/backend/src/database/migrations/20231207085011-add-typebotStatus-to-tickets.ts new file mode 100644 index 0000000..5679dfc --- /dev/null +++ b/backend/src/database/migrations/20231207085011-add-typebotStatus-to-tickets.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "typebotStatus", { + type: DataTypes.BOOLEAN, + defaultValue: false, + allowNull: false, + + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "typebotStatus"); + } +}; diff --git a/backend/src/database/migrations/20231214092337-add-promptId-Queues.ts b/backend/src/database/migrations/20231214092337-add-promptId-Queues.ts new file mode 100644 index 0000000..5fbbaf1 --- /dev/null +++ b/backend/src/database/migrations/20231214092337-add-promptId-Queues.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Queues", "promptId", { + type: DataTypes.INTEGER, + references: { model: "Prompts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }) + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Queues", "promptId"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231214143411-add-columns-to-whatsapps.ts b/backend/src/database/migrations/20231214143411-add-columns-to-whatsapps.ts new file mode 100644 index 0000000..048e490 --- /dev/null +++ b/backend/src/database/migrations/20231214143411-add-columns-to-whatsapps.ts @@ -0,0 +1,30 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "maxUseBotQueues", { + type: DataTypes.INTEGER, + defaultValue: 3, + allowNull: true + }), + queryInterface.addColumn("Whatsapps", "expiresTicket", { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: true + }), + queryInterface.addColumn("Whatsapps", "expiresInactiveMessage", { + type: DataTypes.STRING, + defaultValue: "", + allowNull: true + }), + queryInterface.addColumn("Whatsapps", "timeUseBotQueues", { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "companyId"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231214143411-add-promptId-to-tickets.ts b/backend/src/database/migrations/20231214143411-add-promptId-to-tickets.ts new file mode 100644 index 0000000..e59f30e --- /dev/null +++ b/backend/src/database/migrations/20231214143411-add-promptId-to-tickets.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "promptId", { + type: DataTypes.STRING, + defaultValue: null, + allowNull: true, + + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "promptId"); + } +}; diff --git a/backend/src/database/migrations/20231218160937-add-columns-QueueIntegrations.ts b/backend/src/database/migrations/20231218160937-add-columns-QueueIntegrations.ts new file mode 100644 index 0000000..cfc6ec4 --- /dev/null +++ b/backend/src/database/migrations/20231218160937-add-columns-QueueIntegrations.ts @@ -0,0 +1,21 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("QueueIntegrations", "typebotKeywordRestart", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: "" + }), + queryInterface.addColumn("QueueIntegrations", "typebotRestartMessage", { + type: DataTypes.STRING, + allowNull: true, + defaultValue: "" + }) + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("QueueIntegrations", "typebotKeywordRestart"), + queryInterface.removeColumn("QueueIntegrations", "typebotRestartMessage") + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231219153800-add-isEdited-column-to-messages.ts b/backend/src/database/migrations/20231219153800-add-isEdited-column-to-messages.ts new file mode 100644 index 0000000..ad53d35 --- /dev/null +++ b/backend/src/database/migrations/20231219153800-add-isEdited-column-to-messages.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "isEdited", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "isEdited"); + } +}; \ No newline at end of file diff --git a/backend/src/database/migrations/20231220192536-add-unique-constraint-to-Tickets-table.ts b/backend/src/database/migrations/20231220192536-add-unique-constraint-to-Tickets-table.ts new file mode 100644 index 0000000..4cebd29 --- /dev/null +++ b/backend/src/database/migrations/20231220192536-add-unique-constraint-to-Tickets-table.ts @@ -0,0 +1,18 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.removeConstraint("Tickets", "contactid_companyid_unique"), + queryInterface.addConstraint("Tickets", ["contactId", "companyId", "whatsappId"], { + type: "unique", + name: "contactid_companyid_unique" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeConstraint( + "Tickets", + "contactid_companyid_unique" + ); + } +}; diff --git a/backend/src/database/migrations/20231220223517-add-column-whatsappId-to-Contacts.ts b/backend/src/database/migrations/20231220223517-add-column-whatsappId-to-Contacts.ts new file mode 100644 index 0000000..e2154cc --- /dev/null +++ b/backend/src/database/migrations/20231220223517-add-column-whatsappId-to-Contacts.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Contacts", "whatsappId", { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Contacts", "whatsappId"); + } +}; diff --git a/backend/src/database/migrations/20232016014719-add-transferTime-and-queueIdTransfer.ts b/backend/src/database/migrations/20232016014719-add-transferTime-and-queueIdTransfer.ts new file mode 100644 index 0000000..0504915 --- /dev/null +++ b/backend/src/database/migrations/20232016014719-add-transferTime-and-queueIdTransfer.ts @@ -0,0 +1,27 @@ +import { QueryInterface, DataTypes } from "sequelize"; +// +module.exports = { + + up: (queryInterface: QueryInterface) => { + return Promise.all([ + + queryInterface.addColumn("Whatsapps", "transferQueueId", { + type: DataTypes.INTEGER, + allowNull: true, + }), + + queryInterface.addColumn("Whatsapps", "timeToTransfer", { + type: DataTypes.INTEGER, + allowNull: true, + }) + ]); + }, + + down: (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.removeColumn("Whatsapps", "timeToTransfer"), + queryInterface.removeColumn("Whatsapps", "transferQueueId") + ]); + } + +}; diff --git a/backend/src/database/seeds/20200904070005-create-default-company.ts b/backend/src/database/seeds/20200904070005-create-default-company.ts new file mode 100644 index 0000000..caea0e5 --- /dev/null +++ b/backend/src/database/seeds/20200904070005-create-default-company.ts @@ -0,0 +1,45 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.sequelize.transaction(t => { + return Promise.all([ + queryInterface.bulkInsert( + "Plans", + [ + { + name: "Plano 1", + users: 10, + connections: 10, + queues: 10, + value: 30, + createdAt: new Date(), + updatedAt: new Date() + } + ], + { transaction: t } + ), + queryInterface.bulkInsert( + "Companies", + [ + { + name: "Empresa 1", + planId: 1, + dueDate: "2093-03-14 04:00:00+01", + createdAt: new Date(), + updatedAt: new Date() + } + ], + { transaction: t } + ) + ]); + }); + }, + + down: async (queryInterface: QueryInterface) => { + return Promise.all([ + queryInterface.bulkDelete("Companies", {}), + queryInterface.bulkDelete("Plans", {}) + ]); + } +}; diff --git a/backend/src/database/seeds/20200904070006-create-default-user.ts b/backend/src/database/seeds/20200904070006-create-default-user.ts new file mode 100644 index 0000000..c670393 --- /dev/null +++ b/backend/src/database/seeds/20200904070006-create-default-user.ts @@ -0,0 +1,32 @@ +import { QueryInterface } from "sequelize"; +import { hash } from "bcryptjs"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.sequelize.transaction(async t => { + const passwordHash = await hash("123456", 8); + return Promise.all([ + queryInterface.bulkInsert( + "Users", + [ + { + name: "Admin", + email: "admin@admin.com", + profile: "admin", + passwordHash, + companyId: 1, + createdAt: new Date(), + updatedAt: new Date(), + super: true + } + ], + { transaction: t } + ) + ]); + }); + }, + + down: async (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Users", {}); + } +}; diff --git a/backend/src/database/seeds/20200904070007-create-default-settings.ts b/backend/src/database/seeds/20200904070007-create-default-settings.ts new file mode 100644 index 0000000..742584f --- /dev/null +++ b/backend/src/database/seeds/20200904070007-create-default-settings.ts @@ -0,0 +1,123 @@ +import { QueryInterface } from "sequelize"; +import { hash } from "bcryptjs"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.sequelize.transaction(async t => { + return Promise.all([ + queryInterface.bulkInsert( + "Settings", + [ + { + key: "chatBotType", + value: "text", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "sendGreetingAccepted", + value: "disabled", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date(), + + }, + { + key: "sendMsgTransfTicket", + value: "disabled", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date(), + + }, + { + key: "sendGreetingMessageOneQueues", + value: "disabled", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date(), + + }, + { + key: "userRating", + value: "disabled", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "scheduleType", + value: "queue", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "CheckMsgIsGroup", + value: "enabled", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key:"call", + value: "disabled", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "ipixc", + value: "", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "tokenixc", + value: "", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "ipmkauth", + value: "", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "clientidmkauth", + value: "", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "clientsecretmkauth", + value: "", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + { + key: "asaas", + value: "", + companyId: 1, + createdAt: new Date(), + updatedAt: new Date() + }, + + ], + { transaction: t } + ) + ]); + }); + }, + + down: async (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Settings", {}); + } +}; diff --git a/backend/src/database/seeds/20230130004700-create-alltickets-settings.ts b/backend/src/database/seeds/20230130004700-create-alltickets-settings.ts new file mode 100644 index 0000000..ffbbee8 --- /dev/null +++ b/backend/src/database/seeds/20230130004700-create-alltickets-settings.ts @@ -0,0 +1,22 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.bulkInsert( + "Settings", + [ + { + key: "allTicket", + value: "disabled", + createdAt: new Date(), + updatedAt: new Date() + } + ], + {} + ); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Settings", {}); + } +}; diff --git a/backend/src/errors/AppError.ts b/backend/src/errors/AppError.ts new file mode 100644 index 0000000..a8b1209 --- /dev/null +++ b/backend/src/errors/AppError.ts @@ -0,0 +1,12 @@ +class AppError { + public readonly message: string; + + public readonly statusCode: number; + + constructor(message: string, statusCode = 400) { + this.message = message; + this.statusCode = statusCode; + } +} + +export default AppError; diff --git a/backend/src/errors/toastError.js b/backend/src/errors/toastError.js new file mode 100644 index 0000000..8b1cb9b --- /dev/null +++ b/backend/src/errors/toastError.js @@ -0,0 +1,42 @@ +import { toast } from "react-toastify"; +import { i18n } from "../translate/i18n"; +import { isString } from 'lodash'; + +const toastError = err => { + const errorMsg = err?.response?.data?.message || err?.response?.data?.error; + if (errorMsg) { + if (i18n.exists(`backendErrors.${errorMsg}`)) { + toast.error(i18n.t(`backendErrors.${errorMsg}`), { + toastId: errorMsg, + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: false, + draggable: true, + progress: undefined, + theme: "light", + }); + return + } else { + toast.error(errorMsg, { + toastId: errorMsg, + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: false, + draggable: true, + progress: undefined, + theme: "light", + }); + return + } + } if (isString(err)) { + toast.error(err); + return + } else { + toast.error("An error occurred!"); + return + } +}; + +export default toastError; diff --git a/backend/src/helpers/CheckContactOpenTickets.ts b/backend/src/helpers/CheckContactOpenTickets.ts new file mode 100644 index 0000000..960aa9c --- /dev/null +++ b/backend/src/helpers/CheckContactOpenTickets.ts @@ -0,0 +1,31 @@ +import { Op } from "sequelize"; +import AppError from "../errors/AppError"; +import Ticket from "../models/Ticket"; + +const CheckContactOpenTickets = async (contactId: number, whatsappId?: string): Promise => { + let ticket + + if (!whatsappId) { + ticket = await Ticket.findOne({ + where: { + contactId, + status: { [Op.or]: ["open", "pending"] }, + + } + }); + } else { + ticket = await Ticket.findOne({ + where: { + contactId, + status: { [Op.or]: ["open", "pending"] }, + whatsappId + } + }); + } + console.log(ticket) + if (ticket) { + throw new AppError("ERR_OTHER_OPEN_TICKET"); + } +}; + +export default CheckContactOpenTickets; diff --git a/backend/src/helpers/CheckContactSomeTicket.ts b/backend/src/helpers/CheckContactSomeTicket.ts new file mode 100644 index 0000000..11a4042 --- /dev/null +++ b/backend/src/helpers/CheckContactSomeTicket.ts @@ -0,0 +1,18 @@ +import { Op } from "sequelize"; +import AppError from "../errors/AppError"; +import Ticket from "../models/Ticket"; + +const CheckContactSomeTickets = async ( + contactId: number, + companyId: number +): Promise => { + const ticket = await Ticket.findOne({ + where: { contactId, companyId } + }); + + if (ticket) { + throw new AppError("ERR_OTHER_OPEN_TICKET"); + } +}; + +export default CheckContactSomeTickets; diff --git a/backend/src/helpers/CheckSettings.ts b/backend/src/helpers/CheckSettings.ts new file mode 100644 index 0000000..d19122b --- /dev/null +++ b/backend/src/helpers/CheckSettings.ts @@ -0,0 +1,16 @@ +import Setting from "../models/Setting"; +import AppError from "../errors/AppError"; + +const CheckSettings = async (key: string): Promise => { + const setting = await Setting.findOne({ + where: { key } + }); + + if (!setting) { + throw new AppError("ERR_NO_SETTING_FOUND", 404); + } + + return setting.value; +}; + +export default CheckSettings; diff --git a/backend/src/helpers/CreateTokens.ts b/backend/src/helpers/CreateTokens.ts new file mode 100644 index 0000000..3f814c0 --- /dev/null +++ b/backend/src/helpers/CreateTokens.ts @@ -0,0 +1,32 @@ +import { sign } from "jsonwebtoken"; +import authConfig from "../config/auth"; +import User from "../models/User"; + +export const createAccessToken = (user: User): string => { + const { secret, expiresIn } = authConfig; + + return sign( + { + usarname: user.name, + profile: user.profile, + id: user.id, + companyId: user.companyId + }, + secret, + { + expiresIn + } + ); +}; + +export const createRefreshToken = (user: User): string => { + const { refreshSecret, refreshExpiresIn } = authConfig; + + return sign( + { id: user.id, tokenVersion: user.tokenVersion, companyId: user.companyId }, + refreshSecret, + { + expiresIn: refreshExpiresIn + } + ); +}; diff --git a/backend/src/helpers/Debounce.ts b/backend/src/helpers/Debounce.ts new file mode 100644 index 0000000..80665d9 --- /dev/null +++ b/backend/src/helpers/Debounce.ts @@ -0,0 +1,41 @@ +interface Timeout { + id: number; + timeout: NodeJS.Timeout; +} + +const timeouts: Timeout[] = []; + +const findAndClearTimeout = (ticketId: number) => { + if (timeouts.length > 0) { + const timeoutIndex = timeouts.findIndex(timeout => timeout.id === ticketId); + + if (timeoutIndex !== -1) { + clearTimeout(timeouts[timeoutIndex].timeout); + timeouts.splice(timeoutIndex, 1); + } + } +}; + +const debounce = ( + func: { (): Promise; (...args: never[]): void }, + wait: number, + ticketId: number +) => { + return function executedFunction(...args: never[]): void { + const later = () => { + findAndClearTimeout(ticketId); + func(...args); + }; + + findAndClearTimeout(ticketId); + + const newTimeout = { + id: ticketId, + timeout: setTimeout(later, wait) + }; + + timeouts.push(newTimeout); + }; +}; + +export { debounce }; diff --git a/backend/src/helpers/GetDefaultWhatsApp.ts b/backend/src/helpers/GetDefaultWhatsApp.ts new file mode 100644 index 0000000..8370575 --- /dev/null +++ b/backend/src/helpers/GetDefaultWhatsApp.ts @@ -0,0 +1,43 @@ +import AppError from "../errors/AppError"; +import Whatsapp from "../models/Whatsapp"; +import GetDefaultWhatsAppByUser from "./GetDefaultWhatsAppByUser"; + +const GetDefaultWhatsApp = async ( + companyId: number, + userId?: number +): Promise => { + let connection: Whatsapp; + + const defaultWhatsapp = await Whatsapp.findOne({ + where: { isDefault: true, companyId } + }); + + if (defaultWhatsapp?.status === 'CONNECTED') { + connection = defaultWhatsapp; + } else { + const whatsapp = await Whatsapp.findOne({ + where: { status: "CONNECTED", companyId } + }); + connection = whatsapp; + } + + if (userId) { + const whatsappByUser = await GetDefaultWhatsAppByUser(userId); + if (whatsappByUser?.status === 'CONNECTED') { + connection = whatsappByUser; + } else { + const whatsapp = await Whatsapp.findOne({ + where: { status: "CONNECTED", companyId } + }); + connection = whatsapp; + } + } + + if (!connection) { + throw new AppError(`ERR_NO_DEF_WAPP_FOUND in COMPANY ${companyId}`); + } + + return connection; +}; + +export default GetDefaultWhatsApp; \ No newline at end of file diff --git a/backend/src/helpers/GetDefaultWhatsAppByUser.ts b/backend/src/helpers/GetDefaultWhatsAppByUser.ts new file mode 100644 index 0000000..335129a --- /dev/null +++ b/backend/src/helpers/GetDefaultWhatsAppByUser.ts @@ -0,0 +1,18 @@ +import User from "../models/User"; +import Whatsapp from "../models/Whatsapp"; +import { logger } from "../utils/logger"; + +const GetDefaultWhatsAppByUser = async ( + userId: number +): Promise => { + const user = await User.findByPk(userId, {include: ["whatsapp"]}); + if( user === null || !user.whatsapp) { + return null; + } + + logger.info(`Found whatsapp linked to user '${user.name}' is '${user.whatsapp.name}'.`); + + return user.whatsapp; +}; + +export default GetDefaultWhatsAppByUser; diff --git a/backend/src/helpers/GetTicketWbot.ts b/backend/src/helpers/GetTicketWbot.ts new file mode 100644 index 0000000..5debbb6 --- /dev/null +++ b/backend/src/helpers/GetTicketWbot.ts @@ -0,0 +1,23 @@ +import { WASocket } from "@whiskeysockets/baileys"; +import { getWbot } from "../libs/wbot"; +import GetDefaultWhatsApp from "./GetDefaultWhatsApp"; +import Ticket from "../models/Ticket"; +import { Store } from "../libs/store"; + +type Session = WASocket & { + id?: number; + store?: Store; +}; + +const GetTicketWbot = async (ticket: Ticket): Promise => { + if (!ticket.whatsappId) { + const defaultWhatsapp = await GetDefaultWhatsApp(ticket.user.id); + + await ticket.$set("whatsapp", defaultWhatsapp); + } + + const wbot = getWbot(ticket.whatsappId); + return wbot; +}; + +export default GetTicketWbot; diff --git a/backend/src/helpers/GetWbotMessage.ts b/backend/src/helpers/GetWbotMessage.ts new file mode 100644 index 0000000..9b71f8f --- /dev/null +++ b/backend/src/helpers/GetWbotMessage.ts @@ -0,0 +1,42 @@ +import { proto } from "@whiskeysockets/baileys"; +import WALegacySocket from "@whiskeysockets/baileys" +import Ticket from "../models/Ticket"; +import GetTicketWbot from "./GetTicketWbot"; +import AppError from "../errors/AppError"; +import GetMessageService from "../services/MessageServices/GetMessagesService"; +import Message from "../models/Message"; + +export const GetWbotMessage = async ( + ticket: Ticket, + messageId: string +): Promise => { + const getSock = await GetTicketWbot(ticket); + + let limit = 20; + + const fetchWbotMessagesGradually = async (): Promise< + proto.WebMessageInfo | Message | null | undefined + > => { + const msgFound = await GetMessageService({ + id: messageId + }); + + return msgFound; + + + }; + + try { + const msgFound = await fetchWbotMessagesGradually(); + + if (!msgFound) { + throw new Error("Cannot found message within 100 last messages"); + } + + return msgFound; + } catch (err) { + throw new AppError("ERR_FETCH_WAPP_MSG"); + } +}; + +export default GetWbotMessage; diff --git a/backend/src/helpers/GetWhatsappWbot.ts b/backend/src/helpers/GetWhatsappWbot.ts new file mode 100644 index 0000000..24e63ba --- /dev/null +++ b/backend/src/helpers/GetWhatsappWbot.ts @@ -0,0 +1,9 @@ +import { getWbot } from "../libs/wbot"; +import Whatsapp from "../models/Whatsapp"; + +const GetWhatsappWbot = async (whatsapp: Whatsapp) => { + const wbot = await getWbot(whatsapp.id); + return wbot; +}; + +export default GetWhatsappWbot; diff --git a/backend/src/helpers/Mustache.ts b/backend/src/helpers/Mustache.ts new file mode 100644 index 0000000..2b0ad26 --- /dev/null +++ b/backend/src/helpers/Mustache.ts @@ -0,0 +1,57 @@ +import Mustache from "mustache"; +import Contact from "../models/Contact"; + +export const greeting = (): string => { + const greetings = ["Boa madrugada", "Bom dia", "Boa tarde", "Boa noite"]; + const h = new Date().getHours(); + // eslint-disable-next-line no-bitwise + return greetings[(h / 6) >> 0]; +}; + +export const firstName = (contact?: Contact): string => { + if (contact && contact?.name) { + const nameArr = contact?.name.split(' '); + return nameArr[0]; + } + return ''; +}; + +export default (body: string, contact: Contact): string => { + let ms = ""; + + const Hr = new Date(); + + const dd: string = `0${Hr.getDate()}`.slice(-2); + const mm: string = `0${Hr.getMonth() + 1}`.slice(-2); + const yy: string = Hr.getFullYear().toString(); + const hh: number = Hr.getHours(); + const min: string = `0${Hr.getMinutes()}`.slice(-2); + const ss: string = `0${Hr.getSeconds()}`.slice(-2); + + if (hh >= 6) { + ms = "Bom dia"; + } + if (hh > 11) { + ms = "Boa tarde"; + } + if (hh > 17) { + ms = "Boa noite"; + } + if (hh > 23 || hh < 6) { + ms = "Boa madrugada"; + } + + const protocol = yy + mm + dd + String(hh) + min + ss; + + const hora = `${hh}:${min}:${ss}`; + + const view = { + firstName: firstName(contact), + name: contact ? contact.name : "", + gretting: greeting(), + ms, + protocol, + hora + }; + return Mustache.render(body, view); +}; \ No newline at end of file diff --git a/backend/src/helpers/SendMessage.ts b/backend/src/helpers/SendMessage.ts new file mode 100644 index 0000000..84ce74e --- /dev/null +++ b/backend/src/helpers/SendMessage.ts @@ -0,0 +1,45 @@ +import Whatsapp from "../models/Whatsapp"; +import GetWhatsappWbot from "./GetWhatsappWbot"; +import fs from "fs"; + +import { getMessageOptions } from "../services/WbotServices/SendWhatsAppMedia"; + +export type MessageData = { + number: number | string; + body: string; + mediaPath?: string; + fileName?: string; +}; + +export const SendMessage = async ( + whatsapp: Whatsapp, + messageData: MessageData +): Promise => { + try { + const wbot = await GetWhatsappWbot(whatsapp); + const chatId = `${messageData.number}@s.whatsapp.net`; + + let message; + + if (messageData.mediaPath) { + const options = await getMessageOptions( + messageData.fileName, + messageData.mediaPath, + messageData.body + ); + if (options) { + const body = fs.readFileSync(messageData.mediaPath); + message = await wbot.sendMessage(chatId, { + ...options + }); + } + } else { + const body = `\u200e ${messageData.body}`; + message = await wbot.sendMessage(chatId, { text: body }); + } + + return message; + } catch (err: any) { + throw new Error(err); + } +}; diff --git a/backend/src/helpers/SendRefreshToken.ts b/backend/src/helpers/SendRefreshToken.ts new file mode 100644 index 0000000..4e4459a --- /dev/null +++ b/backend/src/helpers/SendRefreshToken.ts @@ -0,0 +1,5 @@ +import { Response } from "express"; + +export const SendRefreshToken = (res: Response, token: string): void => { + res.cookie("jrt", token, { httpOnly: true }); +}; diff --git a/backend/src/helpers/SerializeUser.ts b/backend/src/helpers/SerializeUser.ts new file mode 100644 index 0000000..7e50103 --- /dev/null +++ b/backend/src/helpers/SerializeUser.ts @@ -0,0 +1,30 @@ +import Queue from "../models/Queue"; +import Company from "../models/Company"; +import User from "../models/User"; +import Setting from "../models/Setting"; + +interface SerializedUser { + id: number; + name: string; + email: string; + profile: string; + companyId: number; + company: Company | null; + super: boolean; + queues: Queue[]; + allTicket: string, +} + +export const SerializeUser = async (user: User): Promise => { + return { + id: user.id, + name: user.name, + email: user.email, + profile: user.profile, + companyId: user.companyId, + company: user.company, + super: user.super, + queues: user.queues, + allTicket: user.allTicket, + }; +}; diff --git a/backend/src/helpers/SerializeWbotMsgId.ts b/backend/src/helpers/SerializeWbotMsgId.ts new file mode 100644 index 0000000..4b5886e --- /dev/null +++ b/backend/src/helpers/SerializeWbotMsgId.ts @@ -0,0 +1,12 @@ +import Message from "../models/Message"; +import Ticket from "../models/Ticket"; + +const SerializeWbotMsgId = (ticket: Ticket, message: Message): string => { + const serializedMsgId = `${message.fromMe}_${ticket.contact.number}@${ + ticket.isGroup ? "g" : "c" + }.us_${message.id}`; + + return serializedMsgId; +}; + +export default SerializeWbotMsgId; diff --git a/backend/src/helpers/SetTicketMessagesAsRead.ts b/backend/src/helpers/SetTicketMessagesAsRead.ts new file mode 100644 index 0000000..32c5f9a --- /dev/null +++ b/backend/src/helpers/SetTicketMessagesAsRead.ts @@ -0,0 +1,62 @@ +import { proto, WASocket } from "@whiskeysockets/baileys"; +// import cacheLayer from "../libs/cache"; +import { getIO } from "../libs/socket"; +import Message from "../models/Message"; +import Ticket from "../models/Ticket"; +import { logger } from "../utils/logger"; +import GetTicketWbot from "./GetTicketWbot"; + +const SetTicketMessagesAsRead = async (ticket: Ticket): Promise => { + await ticket.update({ unreadMessages: 0 }); + // await cacheLayer.set(`contacts:${ticket.contactId}:unreads`, "0"); + + try { + const wbot = await GetTicketWbot(ticket); + + const getJsonMessage = await Message.findAll({ + where: { + ticketId: ticket.id, + fromMe: false, + read: false + }, + order: [["createdAt", "DESC"]] + }); + + if (getJsonMessage.length > 0) { + const lastMessages: proto.IWebMessageInfo = JSON.parse( + JSON.stringify(getJsonMessage[0].dataJson) + ); + + if (lastMessages.key && lastMessages.key.fromMe === false) { + await (wbot as WASocket).chatModify( + { markRead: true, lastMessages: [lastMessages] }, + `${ticket.contact.number}@${ + ticket.isGroup ? "g.us" : "s.whatsapp.net" + }` + ); + } + } + + await Message.update( + { read: true }, + { + where: { + ticketId: ticket.id, + read: false + } + } + ); + } catch (err) { + logger.warn( + `Could not mark messages as read. Maybe whatsapp session disconnected? Err: ${err}` + ); + } + + const io = getIO(); + io.to(`company-${ticket.companyId}-mainchannel`).emit(`company-${ticket.companyId}-ticket`, { + action: "updateUnread", + ticketId: ticket.id + }); +}; + +export default SetTicketMessagesAsRead; diff --git a/backend/src/helpers/UpdateDeletedUserOpenTicketsStatus.ts b/backend/src/helpers/UpdateDeletedUserOpenTicketsStatus.ts new file mode 100644 index 0000000..1204974 --- /dev/null +++ b/backend/src/helpers/UpdateDeletedUserOpenTicketsStatus.ts @@ -0,0 +1,19 @@ +import Ticket from "../models/Ticket"; +import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; + +const UpdateDeletedUserOpenTicketsStatus = async ( + tickets: Ticket[], + companyId: number +): Promise => { + tickets.forEach(async t => { + const ticketId = t.id.toString(); + + await UpdateTicketService({ + ticketData: { status: "pending" }, + ticketId, + companyId + }); + }); +}; + +export default UpdateDeletedUserOpenTicketsStatus; diff --git a/backend/src/helpers/authState.ts b/backend/src/helpers/authState.ts new file mode 100644 index 0000000..db8304b --- /dev/null +++ b/backend/src/helpers/authState.ts @@ -0,0 +1,77 @@ +import type { + AuthenticationCreds, + AuthenticationState, + SignalDataTypeMap +} from "@whiskeysockets/baileys"; +import { BufferJSON, initAuthCreds, proto } from "@whiskeysockets/baileys"; +import Whatsapp from "../models/Whatsapp"; + +const KEY_MAP: { [T in keyof SignalDataTypeMap]: string } = { + "pre-key": "preKeys", + session: "sessions", + "sender-key": "senderKeys", + "app-state-sync-key": "appStateSyncKeys", + "app-state-sync-version": "appStateVersions", + "sender-key-memory": "senderKeyMemory" +}; + +const authState = async ( + whatsapp: Whatsapp +): Promise<{ state: AuthenticationState; saveState: () => void }> => { + let creds: AuthenticationCreds; + let keys: any = {}; + + const saveState = async () => { + try { + await whatsapp.update({ + session: JSON.stringify({ creds, keys }, BufferJSON.replacer, 0) + }); + } catch (error) { + console.log(error); + } + }; + + // const getSessionDatabase = await whatsappById(whatsapp.id); + + if (whatsapp.session && whatsapp.session !== null) { + const result = JSON.parse(whatsapp.session, BufferJSON.reviver); + creds = result.creds; + keys = result.keys; + } else { + creds = initAuthCreds(); + keys = {}; + } + + return { + state: { + creds, + keys: { + get: (type, ids) => { + const key = KEY_MAP[type]; + return ids.reduce((dict: any, id) => { + let value = keys[key]?.[id]; + if (value) { + if (type === "app-state-sync-key") { + value = proto.Message.AppStateSyncKeyData.fromObject(value); + } + dict[id] = value; + } + return dict; + }, {}); + }, + set: (data: any) => { + // eslint-disable-next-line no-restricted-syntax, guard-for-in + for (const i in data) { + const key = KEY_MAP[i as keyof SignalDataTypeMap]; + keys[key] = keys[key] || {}; + Object.assign(keys[key], data[i]); + } + saveState(); + } + } + }, + saveState + }; +}; + +export default authState; diff --git a/backend/src/libs/cache.ts b/backend/src/libs/cache.ts new file mode 100644 index 0000000..ce38792 --- /dev/null +++ b/backend/src/libs/cache.ts @@ -0,0 +1,82 @@ +import Redis from "ioredis"; +import { REDIS_URI_CONNECTION } from "../config/redis"; +import util from "util"; +import * as crypto from "crypto"; + +const redis = new Redis(REDIS_URI_CONNECTION); + +function encryptParams(params: any) { + const str = JSON.stringify(params); + return crypto.createHash("sha256").update(str).digest("base64"); +} + +export function setFromParams( + key: string, + params: any, + value: string, + option?: string, + optionValue?: string | number +) { + const finalKey = `${key}:${encryptParams(params)}`; + if (option !== undefined && optionValue !== undefined) { + return set(finalKey, value, option, optionValue); + } + return set(finalKey, value); +} + +export function getFromParams(key: string, params: any) { + const finalKey = `${key}:${encryptParams(params)}`; + return get(finalKey); +} + +export function delFromParams(key: string, params: any) { + const finalKey = `${key}:${encryptParams(params)}`; + return del(finalKey); +} + +export function set( + key: string, + value: string, + option?: string, + optionValue?: string | number +) { + const setPromisefy = util.promisify(redis.set).bind(redis); + if (option !== undefined && optionValue !== undefined) { + return setPromisefy(key, value, option, optionValue); + } + + return setPromisefy(key, value); +} + +export function get(key: string) { + const getPromisefy = util.promisify(redis.get).bind(redis); + return getPromisefy(key); +} + +export function getKeys(pattern: string) { + const getKeysPromisefy = util.promisify(redis.keys).bind(redis); + return getKeysPromisefy(pattern); +} + +export function del(key: string) { + const delPromisefy = util.promisify(redis.del).bind(redis); + return delPromisefy(key); +} + +export async function delFromPattern(pattern: string) { + const all = await getKeys(pattern); + for (let item of all) { + del(item); + } +} + +export const cacheLayer = { + set, + setFromParams, + get, + getFromParams, + getKeys, + del, + delFromParams, + delFromPattern +}; diff --git a/backend/src/libs/counter.ts b/backend/src/libs/counter.ts new file mode 100644 index 0000000..77a7971 --- /dev/null +++ b/backend/src/libs/counter.ts @@ -0,0 +1,33 @@ +// I ❤️ Chat GPT + +export type Counter = { + name: string; + value: number; +}; + +export type CounterMap = Record; + +export class CounterManager { + private counters: CounterMap = {}; + + // Function to increment the value of a counter and return the current value + incrementCounter(name: string, amount: number = 1): number { + if (!this.counters[name]) { + this.counters[name] = { name, value: 0 }; + } + this.counters[name].value += amount; + return this.counters[name].value; + } + + // Function to decrement the value of a counter and return the current value + decrementCounter(name: string, amount: number = 1): number { + if (this.counters[name]) { + this.counters[name].value -= amount; + if (this.counters[name].value < 0) { + this.counters[name].value = 0; // Ensure the counter doesn't go below zero + } + return this.counters[name].value; + } + return 0; // Counter doesn't exist, return 0 + } +} diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts new file mode 100644 index 0000000..9fb04ea --- /dev/null +++ b/backend/src/libs/socket.ts @@ -0,0 +1,177 @@ +import { Server as SocketIO } from "socket.io"; +import { Server } from "http"; +import AppError from "../errors/AppError"; +import { logger } from "../utils/logger"; +import User from "../models/User"; +import Queue from "../models/Queue"; +import Ticket from "../models/Ticket"; +import { verify } from "jsonwebtoken"; +import authConfig from "../config/auth"; +import { CounterManager } from "./counter"; + +let io: SocketIO; + +export const initIO = (httpServer: Server): SocketIO => { + io = new SocketIO(httpServer, { + cors: { + origin: process.env.FRONTEND_URL + } + }); + + io.on("connection", async socket => { + logger.info("Client Connected"); + const { token } = socket.handshake.query; + let tokenData = null; + try { + tokenData = verify(token as string, authConfig.secret); + logger.debug(tokenData, "io-onConnection: tokenData"); + } catch (error) { + logger.warn(`[libs/socket.ts] Error decoding token: ${error?.message}`); + socket.disconnect(); + return io; + } + const counters = new CounterManager(); + + let user: User = null; + let userId = tokenData.id; + + if (userId && userId !== "undefined" && userId !== "null") { + user = await User.findByPk(userId, { include: [ Queue ] }); + if (user) { + user.online = true; + await user.save(); + } else { + logger.info(`onConnect: User ${userId} not found`); + socket.disconnect(); + return io; + } + } else { + logger.info("onConnect: Missing userId"); + socket.disconnect(); + return io; + } + + socket.join(`company-${user.companyId}-mainchannel`); + socket.join(`user-${user.id}`); + + socket.on("joinChatBox", async (ticketId: string) => { + if (!ticketId || ticketId === "undefined") { + return; + } + Ticket.findByPk(ticketId).then( + (ticket) => { + if (ticket && ticket.companyId === user.companyId + && (ticket.userId === user.id || user.profile === "admin")) { + let c: number; + if ((c = counters.incrementCounter(`ticket-${ticketId}`)) === 1) { + socket.join(ticketId); + } + logger.debug(`joinChatbox[${c}]: Channel: ${ticketId} by user ${user.id}`) + } else { + logger.info(`Invalid attempt to join channel of ticket ${ticketId} by user ${user.id}`) + } + }, + (error) => { + logger.error(error, `Error fetching ticket ${ticketId}`); + } + ); + }); + + socket.on("leaveChatBox", async (ticketId: string) => { + if (!ticketId || ticketId === "undefined") { + return; + } + + let c: number; + // o último que sair apaga a luz + + if ((c = counters.decrementCounter(`ticket-${ticketId}`)) === 0) { + socket.leave(ticketId); + } + logger.debug(`leaveChatbox[${c}]: Channel: ${ticketId} by user ${user.id}`) + }); + + socket.on("joinNotification", async () => { + let c: number; + if ((c = counters.incrementCounter("notification")) === 1) { + if (user.profile === "admin") { + socket.join(`company-${user.companyId}-notification`); + } else { + user.queues.forEach((queue) => { + logger.debug(`User ${user.id} of company ${user.companyId} joined queue ${queue.id} channel.`); + socket.join(`queue-${queue.id}-notification`); + }); + if (user.allTicket === "enabled") { + socket.join("queue-null-notification"); + } + + } + } + logger.debug(`joinNotification[${c}]: User: ${user.id}`); + }); + + socket.on("leaveNotification", async () => { + let c: number; + if ((c = counters.decrementCounter("notification")) === 0) { + if (user.profile === "admin") { + socket.leave(`company-${user.companyId}-notification`); + } else { + user.queues.forEach((queue) => { + logger.debug(`User ${user.id} of company ${user.companyId} leaved queue ${queue.id} channel.`); + socket.leave(`queue-${queue.id}-notification`); + }); + if (user.allTicket === "enabled") { + socket.leave("queue-null-notification"); + } + } + } + logger.debug(`leaveNotification[${c}]: User: ${user.id}`); + }); + + socket.on("joinTickets", (status: string) => { + if (counters.incrementCounter(`status-${status}`) === 1) { + if (user.profile === "admin") { + logger.debug(`Admin ${user.id} of company ${user.companyId} joined ${status} tickets channel.`); + socket.join(`company-${user.companyId}-${status}`); + } else if (status === "pending") { + user.queues.forEach((queue) => { + logger.debug(`User ${user.id} of company ${user.companyId} joined queue ${queue.id} pending tickets channel.`); + socket.join(`queue-${queue.id}-pending`); + }); + if (user.allTicket === "enabled") { + socket.join("queue-null-pending"); + } + } else { + logger.debug(`User ${user.id} cannot subscribe to ${status}`); + } + } + }); + + socket.on("leaveTickets", (status: string) => { + if (counters.decrementCounter(`status-${status}`) === 0) { + if (user.profile === "admin") { + logger.debug(`Admin ${user.id} of company ${user.companyId} leaved ${status} tickets channel.`); + socket.leave(`company-${user.companyId}-${status}`); + } else if (status === "pending") { + user.queues.forEach((queue) => { + logger.debug(`User ${user.id} of company ${user.companyId} leaved queue ${queue.id} pending tickets channel.`); + socket.leave(`queue-${queue.id}-pending`); + }); + if (user.allTicket === "enabled") { + socket.leave("queue-null-pending"); + } + } + } + }); + + socket.emit("ready"); + }); + return io; +}; + +export const getIO = (): SocketIO => { + if (!io) { + throw new AppError("Socket IO not initialized"); + } + return io; +}; diff --git a/backend/src/libs/store.d.ts b/backend/src/libs/store.d.ts new file mode 100644 index 0000000..1599036 --- /dev/null +++ b/backend/src/libs/store.d.ts @@ -0,0 +1,116 @@ +import { + AnyWASocket, + BaileysEventEmitter, + Chat, + ConnectionState, + Contact, + GroupMetadata, + PresenceData, + proto, + WAMessageCursor, + WAMessageKey, + WALegacySocket +} from "@adiwajshing/baileys"; +import KeyedDB from "@adiwajshing/keyed-db"; + +export interface Store { + chats: KeyedDB; + contacts: { + [_: string]: Contact; + }; + messages: { + [_: string]: { + array: proto.IWebMessageInfo[]; + get: (id: string) => proto.IWebMessageInfo; + upsert: (item: proto.IWebMessageInfo, mode: "append" | "prepend") => void; + update: (item: proto.IWebMessageInfo) => boolean; + remove: (item: proto.IWebMessageInfo) => boolean; + updateAssign: ( + id: string, + update: Partial + ) => boolean; + clear: () => void; + filter: (contain: (item: proto.IWebMessageInfo) => boolean) => void; + toJSON: () => proto.IWebMessageInfo[]; + fromJSON: (newItems: proto.IWebMessageInfo[]) => void; + }; + }; + groupMetadata: { + [_: string]: GroupMetadata; + }; + state: ConnectionState; + presences: { + [id: string]: { + [participant: string]: PresenceData; + }; + }; + bind: (ev: BaileysEventEmitter) => void; + loadMessages: ( + jid: string, + count: number, + cursor: WAMessageCursor, + sock: WALegacySocket | undefined + ) => Promise; + loadMessage: ( + jid: string, + id: string, + sock: WALegacySocket | undefined + ) => Promise; + mostRecentMessage: ( + jid: string, + sock: WALegacySocket | undefined + ) => Promise; + fetchImageUrl: ( + jid: string, + sock: AnyWASocket | undefined + ) => Promise; + fetchGroupMetadata: ( + jid: string, + sock: AnyWASocket | undefined + ) => Promise; + fetchBroadcastListInfo: ( + jid: string, + sock: WALegacySocket | undefined + ) => Promise; + fetchMessageReceipts: ( + { remoteJid, id }: WAMessageKey, + sock: WALegacySocket | undefined + ) => Promise; + toJSON: () => { + chats: KeyedDB; + contacts: { + [_: string]: Contact; + }; + messages: { + [_: string]: { + array: proto.IWebMessageInfo[]; + get: (id: string) => proto.IWebMessageInfo; + upsert: ( + item: proto.IWebMessageInfo, + mode: "append" | "prepend" + ) => void; + update: (item: proto.IWebMessageInfo) => boolean; + remove: (item: proto.IWebMessageInfo) => boolean; + updateAssign: ( + id: string, + update: Partial + ) => boolean; + clear: () => void; + filter: (contain: (item: proto.IWebMessageInfo) => boolean) => void; + toJSON: () => proto.IWebMessageInfo[]; + fromJSON: (newItems: proto.IWebMessageInfo[]) => void; + }; + }; + }; + fromJSON: (json: { + chats: Chat[]; + contacts: { + [id: string]: Contact; + }; + messages: { + [id: string]: proto.IWebMessageInfo[]; + }; + }) => void; + writeToFile: (path: string) => void; + readFromFile: (path: string) => void; +} diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts new file mode 100644 index 0000000..d152267 --- /dev/null +++ b/backend/src/libs/wbot.ts @@ -0,0 +1,263 @@ +import * as Sentry from "@sentry/node"; +import makeWASocket, { + WASocket, + Browsers, + DisconnectReason, + fetchLatestBaileysVersion, + makeCacheableSignalKeyStore, + makeInMemoryStore, + isJidBroadcast, + CacheStore +} from "@whiskeysockets/baileys"; +import makeWALegacySocket from "@whiskeysockets/baileys"; +import P from "pino"; + +import Whatsapp from "../models/Whatsapp"; +import { logger } from "../utils/logger"; +import MAIN_LOGGER from "@whiskeysockets/baileys/lib/Utils/logger"; +import authState from "../helpers/authState"; +import { Boom } from "@hapi/boom"; +import AppError from "../errors/AppError"; +import { getIO } from "./socket"; +import { Store } from "./store"; +import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; +import DeleteBaileysService from "../services/BaileysServices/DeleteBaileysService"; +import NodeCache from 'node-cache'; + +const loggerBaileys = MAIN_LOGGER.child({}); +loggerBaileys.level = "error"; + +type Session = WASocket & { + id?: number; + store?: Store; +}; + +const sessions: Session[] = []; + +const retriesQrCodeMap = new Map(); + +export const getWbot = (whatsappId: number): Session => { + const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + + if (sessionIndex === -1) { + throw new AppError("ERR_WAPP_NOT_INITIALIZED"); + } + return sessions[sessionIndex]; +}; + +export const removeWbot = async ( + whatsappId: number, + isLogout = true +): Promise => { + try { + const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + if (sessionIndex !== -1) { + if (isLogout) { + sessions[sessionIndex].logout(); + sessions[sessionIndex].ws.close(); + } + + sessions.splice(sessionIndex, 1); + } + } catch (err) { + logger.error(err); + } +}; + +export const initWASocket = async (whatsapp: Whatsapp): Promise => { + return new Promise(async (resolve, reject) => { + try { + (async () => { + const io = getIO(); + + const whatsappUpdate = await Whatsapp.findOne({ + where: { id: whatsapp.id } + }); + + if (!whatsappUpdate) return; + + const { id, name, provider } = whatsappUpdate; + + const { version, isLatest } = await fetchLatestBaileysVersion(); + const isLegacy = provider === "stable" ? true : false; + + logger.info(`using WA v${version.join(".")}, isLatest: ${isLatest}`); + logger.info(`isLegacy: ${isLegacy}`); + logger.info(`Starting session ${name}`); + let retriesQrCode = 0; + + let wsocket: Session = null; + const store = makeInMemoryStore({ + logger: loggerBaileys + }); + + const { state, saveState } = await authState(whatsapp); + + const msgRetryCounterCache = new NodeCache(); + const userDevicesCache: CacheStore = new NodeCache(); + + wsocket = makeWASocket({ + logger: loggerBaileys, + printQRInTerminal: false, + browser: Browsers.appropriate("Desktop"), + auth: { + creds: state.creds, + keys: makeCacheableSignalKeyStore(state.keys, logger), + }, + version, + // defaultQueryTimeoutMs: 60000, + // retryRequestDelayMs: 250, + // keepAliveIntervalMs: 1000 * 60 * 10 * 3, + msgRetryCounterCache, + shouldIgnoreJid: jid => isJidBroadcast(jid), + }); + + // wsocket = makeWASocket({ + // version, + // logger: loggerBaileys, + // printQRInTerminal: false, + // auth: state as AuthenticationState, + // generateHighQualityLinkPreview: false, + // shouldIgnoreJid: jid => isJidBroadcast(jid), + // browser: ["Chat", "Chrome", "10.15.7"], + // patchMessageBeforeSending: (message) => { + // const requiresPatch = !!( + // message.buttonsMessage || + // // || message.templateMessage + // message.listMessage + // ); + // if (requiresPatch) { + // message = { + // viewOnceMessage: { + // message: { + // messageContextInfo: { + // deviceListMetadataVersion: 2, + // deviceListMetadata: {}, + // }, + // ...message, + // }, + // }, + // }; + // } + + // return message; + // }, + // }) + + wsocket.ev.on( + "connection.update", + async ({ connection, lastDisconnect, qr }) => { + logger.info( + `Socket ${name} Connection Update ${connection || ""} ${lastDisconnect || "" + }` + ); + + if (connection === "close") { + if ((lastDisconnect?.error as Boom)?.output?.statusCode === 403) { + await whatsapp.update({ status: "PENDING", session: "" }); + await DeleteBaileysService(whatsapp.id); + io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { + action: "update", + session: whatsapp + }); + removeWbot(id, false); + } + if ( + (lastDisconnect?.error as Boom)?.output?.statusCode !== + DisconnectReason.loggedOut + ) { + removeWbot(id, false); + setTimeout( + () => StartWhatsAppSession(whatsapp, whatsapp.companyId), + 2000 + ); + } else { + await whatsapp.update({ status: "PENDING", session: "" }); + await DeleteBaileysService(whatsapp.id); + io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { + action: "update", + session: whatsapp + }); + removeWbot(id, false); + setTimeout( + () => StartWhatsAppSession(whatsapp, whatsapp.companyId), + 2000 + ); + } + } + + if (connection === "open") { + await whatsapp.update({ + status: "CONNECTED", + qrcode: "", + retries: 0 + }); + + io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { + action: "update", + session: whatsapp + }); + + const sessionIndex = sessions.findIndex( + s => s.id === whatsapp.id + ); + if (sessionIndex === -1) { + wsocket.id = whatsapp.id; + sessions.push(wsocket); + } + + resolve(wsocket); + } + + if (qr !== undefined) { + if (retriesQrCodeMap.get(id) && retriesQrCodeMap.get(id) >= 3) { + await whatsappUpdate.update({ + status: "DISCONNECTED", + qrcode: "" + }); + await DeleteBaileysService(whatsappUpdate.id); + io.to(`company-${whatsapp.companyId}-mainchannel`).emit("whatsappSession", { + action: "update", + session: whatsappUpdate + }); + wsocket.ev.removeAllListeners("connection.update"); + wsocket.ws.close(); + wsocket = null; + retriesQrCodeMap.delete(id); + } else { + logger.info(`Session QRCode Generate ${name}`); + retriesQrCodeMap.set(id, (retriesQrCode += 1)); + + await whatsapp.update({ + qrcode: qr, + status: "qrcode", + retries: 0 + }); + const sessionIndex = sessions.findIndex( + s => s.id === whatsapp.id + ); + + if (sessionIndex === -1) { + wsocket.id = whatsapp.id; + sessions.push(wsocket); + } + + io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { + action: "update", + session: whatsapp + }); + } + } + } + ); + wsocket.ev.on("creds.update", saveState); + + store.bind(wsocket.ev); + })(); + } catch (error) { + Sentry.captureException(error); + console.log(error); + reject(error); + } + }); +}; diff --git a/backend/src/middleware/envTokenAuth.ts b/backend/src/middleware/envTokenAuth.ts new file mode 100644 index 0000000..99e7617 --- /dev/null +++ b/backend/src/middleware/envTokenAuth.ts @@ -0,0 +1,32 @@ +import { Request, Response, NextFunction } from "express"; + +import AppError from "../errors/AppError"; + +type TokenPayload = { + token: string | undefined; +}; + +const envTokenAuth = ( + req: Request, + res: Response, + next: NextFunction +): void => { + try { + const { token: bodyToken } = req.body as TokenPayload; + const { token: queryToken } = req.query as TokenPayload; + + if (queryToken === process.env.ENV_TOKEN) { + return next(); + } + + if (bodyToken === process.env.ENV_TOKEN) { + return next(); + } + } catch (e) { + console.log(e); + } + + throw new AppError("Token inválido", 403); +}; + +export default envTokenAuth; diff --git a/backend/src/middleware/isAuth.ts b/backend/src/middleware/isAuth.ts new file mode 100644 index 0000000..1898f35 --- /dev/null +++ b/backend/src/middleware/isAuth.ts @@ -0,0 +1,40 @@ +import { verify } from "jsonwebtoken"; +import { Request, Response, NextFunction } from "express"; +import { logger } from "../utils/logger"; +import AppError from "../errors/AppError"; +import authConfig from "../config/auth"; + +interface TokenPayload { + id: string; + username: string; + profile: string; + companyId: number; + iat: number; + exp: number; +} + +const isAuth = (req: Request, res: Response, next: NextFunction): void => { + const authHeader = req.headers.authorization; + + if (!authHeader) { + throw new AppError("ERR_SESSION_EXPIRED", 401); + } + + const [, token] = authHeader.split(" "); + + try { + const decoded = verify(token, authConfig.secret); + const { id, profile, companyId } = decoded as TokenPayload; + req.user = { + id, + profile, + companyId + }; + } catch (err) { + throw new AppError("Invalid token. We'll try to assign a new one on next request", 403 ); + } + + return next(); +}; + +export default isAuth; diff --git a/backend/src/middleware/isSuper.ts b/backend/src/middleware/isSuper.ts new file mode 100644 index 0000000..f30dd17 --- /dev/null +++ b/backend/src/middleware/isSuper.ts @@ -0,0 +1,17 @@ +import { Request, Response, NextFunction } from "express"; +import AppError from "../errors/AppError"; +import User from "../models/User"; + +const isSuper = async (req: Request, res: Response, next: NextFunction): Promise => { + const { super:isSuper } = await User.findByPk(req.user.id); + if(!isSuper){ + throw new AppError( + "Acesso não permitido", + 401 + ); + } + + return next(); +} + +export default isSuper; diff --git a/backend/src/middleware/tokenAuth.ts b/backend/src/middleware/tokenAuth.ts new file mode 100644 index 0000000..ac883a6 --- /dev/null +++ b/backend/src/middleware/tokenAuth.ts @@ -0,0 +1,31 @@ +import { Request, Response, NextFunction } from "express"; + +import AppError from "../errors/AppError"; +import Whatsapp from "../models/Whatsapp"; + +type HeaderParams = { + Bearer: string; +}; + +const tokenAuth = async (req: Request, res: Response, next: NextFunction): Promise => { + try { + const token = req.headers.authorization.replace('Bearer ', ''); + const whatsapp = await Whatsapp.findOne({ where: { token } }); + if (whatsapp) { + req.params = { + whatsappId: whatsapp.id.toString() + } + } else { + throw new Error(); + } + } catch (err) { + throw new AppError( + "Acesso não permitido", + 401 + ); + } + + return next(); +}; + +export default tokenAuth; diff --git a/backend/src/models/Announcement.ts b/backend/src/models/Announcement.ts new file mode 100644 index 0000000..94cc873 --- /dev/null +++ b/backend/src/models/Announcement.ts @@ -0,0 +1,54 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + DataType, + BelongsTo, + ForeignKey +} from "sequelize-typescript"; +import Company from "./Company"; + +@Table +class Announcement extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + priority: number; //1 - alta, 2 - média, 3 - baixa + + @Column + title: string; + + @Column(DataType.TEXT) + text: string; + + @Column + mediaPath: string; + + @Column + mediaName: string; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @Column + status: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Company) + company: Company; +} + +export default Announcement; diff --git a/backend/src/models/Baileys.ts b/backend/src/models/Baileys.ts new file mode 100644 index 0000000..b64c2d9 --- /dev/null +++ b/backend/src/models/Baileys.ts @@ -0,0 +1,40 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + Default, + ForeignKey +} from "sequelize-typescript"; +import Whatsapp from "./Whatsapp"; + +@Table +class Baileys extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Default(null) + @Column + contacts: string; + + @Default(null) + @Column + chats: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; +} + +export default Baileys; diff --git a/backend/src/models/BaileysChats.ts b/backend/src/models/BaileysChats.ts new file mode 100644 index 0000000..a1d63e8 --- /dev/null +++ b/backend/src/models/BaileysChats.ts @@ -0,0 +1,45 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + Default, + ForeignKey, + DataType, + AllowNull, + AutoIncrement +} from "sequelize-typescript"; +import Company from "./Company"; +import Whatsapp from "./Whatsapp"; + +@Table +class BaileysChats extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + jid: string; + + @Column + conversationTimestamp: number; + + @Default(0) + @Column + unreadCount: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Whatsapp) + @Column(DataType.INTEGER) + whatsappId: string; +} + +export default BaileysChats; diff --git a/backend/src/models/Campaign.ts b/backend/src/models/Campaign.ts new file mode 100644 index 0000000..91b5a96 --- /dev/null +++ b/backend/src/models/Campaign.ts @@ -0,0 +1,115 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo, + HasMany +} from "sequelize-typescript"; +import CampaignShipping from "./CampaignShipping"; +import Company from "./Company"; +import ContactList from "./ContactList"; +import Whatsapp from "./Whatsapp"; +import Files from "./Files"; + +@Table({ tableName: "Campaigns" }) +class Campaign extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @Column({ defaultValue: "" }) + message1: string; + + @Column({ defaultValue: "" }) + message2: string; + + @Column({ defaultValue: "" }) + message3: string; + + @Column({ defaultValue: "" }) + message4: string; + + @Column({ defaultValue: "" }) + message5: string; + + @Column({ defaultValue: "" }) + confirmationMessage1: string; + + @Column({ defaultValue: "" }) + confirmationMessage2: string; + + @Column({ defaultValue: "" }) + confirmationMessage3: string; + + @Column({ defaultValue: "" }) + confirmationMessage4: string; + + @Column({ defaultValue: "" }) + confirmationMessage5: string; + + @Column({ defaultValue: "INATIVA" }) + status: string; // INATIVA, PROGRAMADA, EM_ANDAMENTO, CANCELADA, FINALIZADA + + @Column + confirmation: boolean; + + @Column + mediaPath: string; + + @Column + mediaName: string; + + @Column + scheduledAt: Date; + + @Column + completedAt: Date; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @ForeignKey(() => ContactList) + @Column + contactListId: number; + + @BelongsTo(() => ContactList) + contactList: ContactList; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @BelongsTo(() => Whatsapp) + whatsapp: Whatsapp; + + @ForeignKey(() => Files) + @Column + fileListId: number; + + @BelongsTo(() => Files) + fileList: Files; + + @HasMany(() => CampaignShipping) + shipping: CampaignShipping[]; +} + +export default Campaign; diff --git a/backend/src/models/CampaignSetting.ts b/backend/src/models/CampaignSetting.ts new file mode 100644 index 0000000..ce9cc61 --- /dev/null +++ b/backend/src/models/CampaignSetting.ts @@ -0,0 +1,41 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Company from "./Company"; + +@Table({ tableName: "CampaignSettings" }) +class CampaignSetting extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + key: string; + + @Column + value: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; +} + +export default CampaignSetting; diff --git a/backend/src/models/CampaignShipping.ts b/backend/src/models/CampaignShipping.ts new file mode 100644 index 0000000..f7849db --- /dev/null +++ b/backend/src/models/CampaignShipping.ts @@ -0,0 +1,67 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Campaign from "./Campaign"; +import ContactListItem from "./ContactListItem"; + +@Table({ tableName: "CampaignShipping" }) +class CampaignShipping extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + jobId: string; + + @Column + number: string; + + @Column + message: string; + + @Column + confirmationMessage: string; + + @Column + confirmation: boolean; + + @ForeignKey(() => ContactListItem) + @Column + contactId: number; + + @ForeignKey(() => Campaign) + @Column + campaignId: number; + + @Column + confirmationRequestedAt: Date; + + @Column + confirmedAt: Date; + + @Column + deliveredAt: Date; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => ContactListItem) + contact: ContactListItem; + + @BelongsTo(() => Campaign) + campaign: Campaign; +} + +export default CampaignShipping; diff --git a/backend/src/models/Chat.ts b/backend/src/models/Chat.ts new file mode 100644 index 0000000..a84dd4f --- /dev/null +++ b/backend/src/models/Chat.ts @@ -0,0 +1,72 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + HasMany, + BelongsTo, + ForeignKey, + BeforeCreate, + Default +} from "sequelize-typescript"; + +import { v4 as uuidv4 } from "uuid"; + +import ChatMessage from "./ChatMessage"; +import ChatUser from "./ChatUser"; +import Company from "./Company"; +import User from "./User"; + +@Table({ tableName: "Chats" }) +class Chat extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Default(uuidv4()) + @Column + uuid: string; + + @Column({ defaultValue: "" }) + title: string; + + @ForeignKey(() => User) + @Column + ownerId: number; + + @Column({ defaultValue: "" }) + lastMessage: string; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Company) + company: Company; + + @BelongsTo(() => User) + owner: User; + + @HasMany(() => ChatUser) + users: ChatUser[]; + + @HasMany(() => ChatMessage) + messages: ChatMessage[]; + + @BeforeCreate + static setUUID(chat: Chat) { + chat.uuid = uuidv4(); + } +} + +export default Chat; diff --git a/backend/src/models/ChatMessage.ts b/backend/src/models/ChatMessage.ts new file mode 100644 index 0000000..9cf9e33 --- /dev/null +++ b/backend/src/models/ChatMessage.ts @@ -0,0 +1,52 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + BelongsTo, + ForeignKey +} from "sequelize-typescript"; +import User from "./User"; +import Chat from "./Chat"; + +@Table({ tableName: "ChatMessages" }) +class ChatMessage extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @ForeignKey(() => Chat) + @Column + chatId: number; + + @ForeignKey(() => User) + @Column + senderId: number; + + @Column({ defaultValue: "" }) + message: string; + + @Column + mediaPath: string; + + @Column + mediaName: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Chat) + chat: Chat; + + @BelongsTo(() => User) + sender: User; +} + +export default ChatMessage; diff --git a/backend/src/models/ChatUser.ts b/backend/src/models/ChatUser.ts new file mode 100644 index 0000000..ecddcaa --- /dev/null +++ b/backend/src/models/ChatUser.ts @@ -0,0 +1,46 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + BelongsTo, + ForeignKey +} from "sequelize-typescript"; +import User from "./User"; +import Chat from "./Chat"; + +@Table({ tableName: "ChatUsers" }) +class ChatUser extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @ForeignKey(() => Chat) + @Column + chatId: number; + + @ForeignKey(() => User) + @Column + userId: number; + + @Column + unreads: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Chat) + chat: Chat; + + @BelongsTo(() => User) + user: User; +} + +export default ChatUser; diff --git a/backend/src/models/Company.ts b/backend/src/models/Company.ts new file mode 100644 index 0000000..a4e75f5 --- /dev/null +++ b/backend/src/models/Company.ts @@ -0,0 +1,133 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo, + DataType, + HasMany +} from "sequelize-typescript"; +import Contact from "./Contact"; +import Message from "./Message"; + +import Plan from "./Plan"; +import Queue from "./Queue"; +import Setting from "./Setting"; +import Ticket from "./Ticket"; +import TicketTraking from "./TicketTraking"; +import User from "./User"; +import UserRating from "./UserRating"; +import Whatsapp from "./Whatsapp"; + +@Table +class Company extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @Column + phone: string; + + @Column + email: string; + + @Column + status: boolean; + + @Column + dueDate: string; + + @Column + recurrence: string; + + @Column({ + type: DataType.JSONB + }) + schedules: []; + + @ForeignKey(() => Plan) + @Column + planId: number; + + @BelongsTo(() => Plan) + plan: Plan; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @HasMany(() => User, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + users: User[]; + + @HasMany(() => UserRating, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + userRatings: UserRating[]; + + @HasMany(() => Queue, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + queues: Queue[]; + + @HasMany(() => Whatsapp, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + whatsapps: Whatsapp[]; + + @HasMany(() => Message, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + messages: Message[]; + + @HasMany(() => Contact, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + contacts: Contact[]; + + @HasMany(() => Setting, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + settings: Setting[]; + + @HasMany(() => Ticket, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + tickets: Ticket[]; + + @HasMany(() => TicketTraking, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + ticketTrankins: TicketTraking[]; +} + +export default Company; diff --git a/backend/src/models/Contact.ts b/backend/src/models/Contact.ts new file mode 100644 index 0000000..9ef41d9 --- /dev/null +++ b/backend/src/models/Contact.ts @@ -0,0 +1,84 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + AllowNull, + Unique, + Default, + HasMany, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import ContactCustomField from "./ContactCustomField"; +import Ticket from "./Ticket"; +import Company from "./Company"; +import Schedule from "./Schedule"; +import Whatsapp from "./Whatsapp"; + +@Table +class Contact extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @AllowNull(false) + @Unique + @Column + number: string; + + @AllowNull(false) + @Default("") + @Column + email: string; + + @Default("") + @Column + profilePicUrl: string; + + @Default(false) + @Column + isGroup: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @HasMany(() => Ticket) + tickets: Ticket[]; + + @HasMany(() => ContactCustomField) + extraInfo: ContactCustomField[]; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @HasMany(() => Schedule, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + schedules: Schedule[]; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @BelongsTo(() => Whatsapp) + whatsapp: Whatsapp; +} + +export default Contact; diff --git a/backend/src/models/ContactCustomField.ts b/backend/src/models/ContactCustomField.ts new file mode 100644 index 0000000..f4a9ebe --- /dev/null +++ b/backend/src/models/ContactCustomField.ts @@ -0,0 +1,41 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Contact from "./Contact"; + +@Table +class ContactCustomField extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @Column + value: string; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @BelongsTo(() => Contact) + contact: Contact; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default ContactCustomField; diff --git a/backend/src/models/ContactList.ts b/backend/src/models/ContactList.ts new file mode 100644 index 0000000..c1b500c --- /dev/null +++ b/backend/src/models/ContactList.ts @@ -0,0 +1,47 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo, + HasMany +} from "sequelize-typescript"; +import Company from "./Company"; +import ContactListItem from "./ContactListItem"; + +@Table({ tableName: "ContactLists" }) +class ContactList extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @HasMany(() => ContactListItem, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + contacts: ContactListItem[]; +} + +export default ContactList; diff --git a/backend/src/models/ContactListItem.ts b/backend/src/models/ContactListItem.ts new file mode 100644 index 0000000..7eecc98 --- /dev/null +++ b/backend/src/models/ContactListItem.ts @@ -0,0 +1,61 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + AllowNull, + Default, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Company from "./Company"; +import ContactList from "./ContactList"; + +@Table({ tableName: "ContactListItems" }) +class ContactListItem extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @AllowNull(false) + @Column + name: string; + + @AllowNull(false) + @Column + number: string; + + @AllowNull(false) + @Default("") + @Column + email: string; + + @Column + isWhatsappValid: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @ForeignKey(() => ContactList) + @Column + contactListId: number; + + @BelongsTo(() => ContactList) + contactList: ContactList; +} + +export default ContactListItem; diff --git a/backend/src/models/Files.ts b/backend/src/models/Files.ts new file mode 100644 index 0000000..2693b79 --- /dev/null +++ b/backend/src/models/Files.ts @@ -0,0 +1,44 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + HasMany, + ForeignKey +} from "sequelize-typescript"; +import Company from "./Company"; +import FilesOptions from "./FilesOptions"; + +@Table({ + tableName: "Files" +}) +class Files extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @Column + name: string; + + @Column + message: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @HasMany(() => FilesOptions) + options: FilesOptions[]; +} + +export default Files; diff --git a/backend/src/models/FilesOptions.ts b/backend/src/models/FilesOptions.ts new file mode 100644 index 0000000..d4ce956 --- /dev/null +++ b/backend/src/models/FilesOptions.ts @@ -0,0 +1,46 @@ +import { + Table, + Column, + Model, + ForeignKey, + PrimaryKey, + AutoIncrement, + CreatedAt, + UpdatedAt, + BelongsTo +} from "sequelize-typescript"; +import Files from "./Files"; + +@Table({ + tableName: "FilesOptions" +}) +class FilesOptions extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @ForeignKey(() => Files) + @Column + fileId: number; + + @Column + name: string; + + @Column + path: string; + + @Column + mediaType: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Files) + file: Files; +} + +export default FilesOptions; diff --git a/backend/src/models/Help.ts b/backend/src/models/Help.ts new file mode 100644 index 0000000..cbbf17e --- /dev/null +++ b/backend/src/models/Help.ts @@ -0,0 +1,39 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement +} from "sequelize-typescript"; + +@Table({ + tableName: "Helps" +}) +class Help extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + title: string; + + @Column + description: string; + + @Column + video: string; + + @Column + link: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default Help; diff --git a/backend/src/models/Invoices.ts b/backend/src/models/Invoices.ts new file mode 100644 index 0000000..8a855a5 --- /dev/null +++ b/backend/src/models/Invoices.ts @@ -0,0 +1,44 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + AllowNull, + HasMany, + Unique +} from "sequelize-typescript"; + +@Table({ tableName: "Invoices" }) +class Invoices extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + detail: string; + + @Column + status: string; + + @Column + value: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @Column + dueDate: string; + + @Column + companyId: number; + +} + +export default Invoices; diff --git a/backend/src/models/Message.ts b/backend/src/models/Message.ts new file mode 100644 index 0000000..b354fec --- /dev/null +++ b/backend/src/models/Message.ts @@ -0,0 +1,113 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey, + Default, + BelongsTo, + ForeignKey +} from "sequelize-typescript"; +import Contact from "./Contact"; +import Ticket from "./Ticket"; +import Company from "./Company"; +import Queue from "./Queue"; + +@Table +class Message extends Model { + @PrimaryKey + @Column + id: string; + + @Column(DataType.STRING) + remoteJid: string; + + @Column(DataType.STRING) + participant: string; + + @Column(DataType.STRING) + dataJson: string; + + @Default(0) + @Column + ack: number; + + @Default(false) + @Column + read: boolean; + + @Default(false) + @Column + fromMe: boolean; + + @Column(DataType.TEXT) + body: string; + + @Column(DataType.STRING) + get mediaUrl(): string | null { + if (this.getDataValue("mediaUrl")) { + return `${process.env.BACKEND_URL}/public/${this.getDataValue( + "mediaUrl" + )}`; + } + return null; + } + + @Column + mediaType: string; + + @Default(false) + @Column + isDeleted: boolean; + + @CreatedAt + @Column(DataType.DATE(6)) + createdAt: Date; + + @UpdatedAt + @Column(DataType.DATE(6)) + updatedAt: Date; + + @ForeignKey(() => Message) + @Column + quotedMsgId: string; + + @BelongsTo(() => Message, "quotedMsgId") + quotedMsg: Message; + + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @BelongsTo(() => Ticket) + ticket: Ticket; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @BelongsTo(() => Contact, "contactId") + contact: Contact; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @ForeignKey(() => Queue) + @Column + queueId: number; + + @BelongsTo(() => Queue) + queue: Queue; + + @Default(false) + @Column + isEdited: boolean; +} + +export default Message; diff --git a/backend/src/models/Plan.ts b/backend/src/models/Plan.ts new file mode 100644 index 0000000..d6ece13 --- /dev/null +++ b/backend/src/models/Plan.ts @@ -0,0 +1,65 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + AllowNull, + Unique +} from "sequelize-typescript"; + +@Table +class Plan extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @AllowNull(false) + @Unique + @Column + name: string; + + @Column + users: number; + + @Column + connections: number; + + @Column + queues: number; + + @Column + value: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @Column + useSchedules: boolean; + + @Column + useCampaigns: boolean; + + @Column + useInternalChat: boolean; + + @Column + useExternalApi: boolean; + + @Column + useKanban: boolean; + + @Column + useOpenAi: boolean; + + @Column + useIntegrations: boolean; +} + +export default Plan; diff --git a/backend/src/models/Prompt.ts b/backend/src/models/Prompt.ts new file mode 100644 index 0000000..88a9375 --- /dev/null +++ b/backend/src/models/Prompt.ts @@ -0,0 +1,87 @@ +import { + AllowNull, + AutoIncrement, + BelongsTo, + Column, + CreatedAt, + ForeignKey, + Model, + PrimaryKey, + Table, + UpdatedAt +} from "sequelize-typescript"; +import Queue from "./Queue"; +import Company from "./Company"; + +@Table +class Prompt extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @AllowNull(false) + @Column + name: string; + + @AllowNull(false) + @Column + prompt: string; + + @AllowNull(false) + @Column + apiKey: string; + + @Column({ defaultValue: 10 }) + maxMessages: number; + + @Column({ defaultValue: 100 }) + maxTokens: number; + + @Column({ defaultValue: 1 }) + temperature: number; + + @Column({ defaultValue: 0 }) + promptTokens: number; + + @Column({ defaultValue: 0 }) + completionTokens: number; + + @Column({ defaultValue: 0 }) + totalTokens: number; + + @AllowNull(false) + @Column + voice: string; + + @AllowNull(true) + @Column + voiceKey:string; + + @AllowNull(true) + @Column + voiceRegion:string; + + @AllowNull + @ForeignKey(() => Queue) + @Column + queueId: number; + + @BelongsTo(() => Queue) + queue: Queue; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default Prompt; diff --git a/backend/src/models/Queue.ts b/backend/src/models/Queue.ts new file mode 100644 index 0000000..c32a16b --- /dev/null +++ b/backend/src/models/Queue.ts @@ -0,0 +1,103 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + AllowNull, + Unique, + BelongsToMany, + BelongsTo, + ForeignKey, + HasMany, + DataType, + Default +} from "sequelize-typescript"; +import User from "./User"; +import UserQueue from "./UserQueue"; +import Company from "./Company"; + +import Whatsapp from "./Whatsapp"; +import WhatsappQueue from "./WhatsappQueue"; +import QueueOption from "./QueueOption"; +import Prompt from "./Prompt"; +import QueueIntegrations from "./QueueIntegrations"; + +@Table +class Queue extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @AllowNull(false) + @Unique + @Column + name: string; + + @AllowNull(false) + @Unique + @Column + color: string; + + @Default("") + @Column + greetingMessage: string; + + @Default("") + @Column + outOfHoursMessage: string; + + @Column({ + type: DataType.JSONB + }) + schedules: []; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @BelongsToMany(() => Whatsapp, () => WhatsappQueue) + whatsapps: Array; + + @BelongsToMany(() => User, () => UserQueue) + users: Array; + + @HasMany(() => QueueOption, { + onDelete: "DELETE", + onUpdate: "DELETE", + hooks: true + }) + options: QueueOption[]; + + @Column + orderQueue: number; + + + @ForeignKey(() => QueueIntegrations) + @Column + integrationId: number; + + @BelongsTo(() => QueueIntegrations) + queueIntegrations: QueueIntegrations; + + @ForeignKey(() => Prompt) + @Column + promptId: number; + + @BelongsTo(() => Prompt) + prompt: Prompt; +} + +export default Queue; diff --git a/backend/src/models/QueueIntegrations.ts b/backend/src/models/QueueIntegrations.ts new file mode 100644 index 0000000..9412094 --- /dev/null +++ b/backend/src/models/QueueIntegrations.ts @@ -0,0 +1,82 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey, + HasMany, + AutoIncrement, + BelongsTo, + ForeignKey, + Default +} from "sequelize-typescript"; +import Queue from "./Queue"; +import Company from "./Company"; + +@Table +class QueueIntegrations extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column(DataType.TEXT) + type: string; + + @Column(DataType.TEXT) + name: string; + + @Column(DataType.TEXT) + projectName: string; + + @Column(DataType.TEXT) + jsonContent: string; + + @Column(DataType.TEXT) + urlN8N: string; + + @Column(DataType.TEXT) + language: string; + + @CreatedAt + @Column(DataType.DATE(6)) + createdAt: Date; + + @UpdatedAt + @Column(DataType.DATE(6)) + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @Column + typebotSlug: string; + + @Default(0) + @Column + typebotExpires: number; + + @Column + typebotKeywordFinish: string; + + @Column + typebotUnknownMessage: string; + + @Default(1000) + @Column + typebotDelayMessage: number + + @Column + typebotKeywordRestart: string; + + @Column + typebotRestartMessage: string; +} + +export default QueueIntegrations; \ No newline at end of file diff --git a/backend/src/models/QueueOption.ts b/backend/src/models/QueueOption.ts new file mode 100644 index 0000000..c8f0cce --- /dev/null +++ b/backend/src/models/QueueOption.ts @@ -0,0 +1,54 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo, + AllowNull +} from "sequelize-typescript"; +import Queue from "./Queue"; + +@Table +class QueueOption extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + title: string; + + @AllowNull + @Column + message: string; + + @AllowNull + @Column + option: string; + + @ForeignKey(() => Queue) + @Column + queueId: number; + + @ForeignKey(() => QueueOption) + @Column + parentId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Queue) + queue: Queue; + + @BelongsTo(() => QueueOption, { foreignKey: 'parentId' }) + parent: QueueOption; +} + +export default QueueOption; diff --git a/backend/src/models/QuickMessage.ts b/backend/src/models/QuickMessage.ts new file mode 100644 index 0000000..3aebb3b --- /dev/null +++ b/backend/src/models/QuickMessage.ts @@ -0,0 +1,63 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + ForeignKey, + BelongsTo, + AutoIncrement +} from "sequelize-typescript"; + +import Company from "./Company"; +import User from "./User"; + +@Table +class QuickMessage extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + shortcode: string; + + @Column + message: string; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => Company) + company: Company; + + @BelongsTo(() => User) + user: User; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @Column + get mediaPath(): string | null { + if (this.getDataValue("mediaPath")) { + + return `${process.env.BACKEND_URL}${process.env.PROXY_PORT ?`:${process.env.PROXY_PORT}`:""}/public/quickMessage/${this.getDataValue("mediaPath")}`; + + } + return null; + } + + @Column + mediaName: string; +} + +export default QuickMessage; diff --git a/backend/src/models/Schedule.ts b/backend/src/models/Schedule.ts new file mode 100644 index 0000000..3dca703 --- /dev/null +++ b/backend/src/models/Schedule.ts @@ -0,0 +1,78 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + DataType, + BelongsTo, + ForeignKey +} from "sequelize-typescript"; +import Company from "./Company"; +import Contact from "./Contact"; +import Ticket from "./Ticket"; +import User from "./User"; + +@Table +class Schedule extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column(DataType.TEXT) + body: string; + + @Column + sendAt: Date; + + @Column + sentAt: Date; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @ForeignKey(() => User) + @Column + userId: number; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @Column(DataType.STRING) + status: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Contact, "contactId") + contact: Contact; + + @BelongsTo(() => Ticket) + ticket: Ticket; + + @BelongsTo(() => User) + user: User; + + @BelongsTo(() => Company) + company: Company; + + @Column + mediaPath: string; + + @Column + mediaName: string; +} + +export default Schedule; diff --git a/backend/src/models/Setting.ts b/backend/src/models/Setting.ts new file mode 100644 index 0000000..9b74978 --- /dev/null +++ b/backend/src/models/Setting.ts @@ -0,0 +1,42 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + ForeignKey, + BelongsTo, + AutoIncrement +} from "sequelize-typescript"; + +import Company from "./Company"; + +@Table +class Setting extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + key: string; + + @Column + value: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; +} + +export default Setting; diff --git a/backend/src/models/Subscriptions.ts b/backend/src/models/Subscriptions.ts new file mode 100644 index 0000000..e099d03 --- /dev/null +++ b/backend/src/models/Subscriptions.ts @@ -0,0 +1,56 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + AllowNull +} from "sequelize-typescript"; + +@Table +class Subscriptions extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + isActive: boolean; + + @AllowNull(true) + @Column + userPriceCents: number; + + @AllowNull(true) + @Column + whatsPriceCents: number; + + @AllowNull(true) + @Column + lastInvoiceUrl: string; + + @AllowNull(true) + @Column + lastPlanChange: Date; + + @AllowNull(true) + @Column + expiresAt: Date; + + @AllowNull(true) + @Column + providerSubscriptionId: string; + + @Column + companyId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default Subscriptions; diff --git a/backend/src/models/Tag.ts b/backend/src/models/Tag.ts new file mode 100644 index 0000000..793f231 --- /dev/null +++ b/backend/src/models/Tag.ts @@ -0,0 +1,54 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + BelongsToMany, + ForeignKey, + BelongsTo, + HasMany +} from "sequelize-typescript"; +import Company from "./Company"; +import Ticket from "./Ticket"; +import TicketTag from "./TicketTag"; + +@Table +class Tag extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @Column + color: string; + + @HasMany(() => TicketTag) + ticketTags: TicketTag[]; + + @BelongsToMany(() => Ticket, () => TicketTag) + tickets: Ticket[]; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @Column + kanban: number; +} + +export default Tag; diff --git a/backend/src/models/Ticket.ts b/backend/src/models/Ticket.ts new file mode 100644 index 0000000..2e350eb --- /dev/null +++ b/backend/src/models/Ticket.ts @@ -0,0 +1,154 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + ForeignKey, + BelongsTo, + HasMany, + AutoIncrement, + Default, + BeforeCreate, + BelongsToMany, + AllowNull +} from "sequelize-typescript"; +import { v4 as uuidv4 } from "uuid"; + +import Contact from "./Contact"; +import Message from "./Message"; +import Queue from "./Queue"; +import User from "./User"; +import Whatsapp from "./Whatsapp"; +import Company from "./Company"; +import QueueOption from "./QueueOption"; +import Tag from "./Tag"; +import TicketTag from "./TicketTag"; +import QueueIntegrations from "./QueueIntegrations"; +import Prompt from "./Prompt"; + +@Table +class Ticket extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column({ defaultValue: "pending" }) + status: string; + + @Column + unreadMessages: number; + + @Column + lastMessage: string; + + @Default(false) + @Column + isGroup: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => User) + user: User; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @BelongsTo(() => Contact) + contact: Contact; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @BelongsTo(() => Whatsapp) + whatsapp: Whatsapp; + + @ForeignKey(() => Queue) + @Column + queueId: number; + + @BelongsTo(() => Queue) + queue: Queue; + + @Column + chatbot: boolean; + + @ForeignKey(() => QueueOption) + @Column + queueOptionId: number; + + @BelongsTo(() => QueueOption) + queueOption: QueueOption; + + @HasMany(() => Message) + messages: Message[]; + + @HasMany(() => TicketTag) + ticketTags: TicketTag[]; + + @BelongsToMany(() => Tag, () => TicketTag) + tags: Tag[]; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @Default(uuidv4()) + @Column + uuid: string; + + @BeforeCreate + static setUUID(ticket: Ticket) { + ticket.uuid = uuidv4(); + } + + @Default(false) + @Column + useIntegration: boolean; + + @ForeignKey(() => QueueIntegrations) + @Column + integrationId: number; + + @BelongsTo(() => QueueIntegrations) + queueIntegration: QueueIntegrations; + + @Column + typebotSessionId: string; + + @Default(false) + @Column + typebotStatus: boolean + + @ForeignKey(() => Prompt) + @Column + promptId: number; + + @BelongsTo(() => Prompt) + prompt: Prompt; + + @Column + fromMe: boolean; + + @AllowNull(false) + @Default(0) + @Column + amountUsedBotQueues: number; +} + +export default Ticket; diff --git a/backend/src/models/TicketNote.ts b/backend/src/models/TicketNote.ts new file mode 100644 index 0000000..621a15b --- /dev/null +++ b/backend/src/models/TicketNote.ts @@ -0,0 +1,55 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + ForeignKey, + BelongsTo, + AutoIncrement +} from "sequelize-typescript"; + +import Contact from "./Contact"; +import User from "./User"; +import Ticket from "./Ticket"; + +@Table +class TicketNote extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + note: string; + + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => User) + user: User; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @BelongsTo(() => Contact) + contact: Contact; + + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @BelongsTo(() => Ticket) + ticket: Ticket; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default TicketNote; diff --git a/backend/src/models/TicketTag.ts b/backend/src/models/TicketTag.ts new file mode 100644 index 0000000..55b4bb0 --- /dev/null +++ b/backend/src/models/TicketTag.ts @@ -0,0 +1,38 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Tag from "./Tag"; +import Ticket from "./Ticket"; + +@Table({ + tableName: 'TicketTags' +}) +class TicketTag extends Model { + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @ForeignKey(() => Tag) + @Column + tagId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Ticket) + ticket: Ticket; + + @BelongsTo(() => Tag) + tag: Tag; +} + +export default TicketTag; diff --git a/backend/src/models/TicketTraking.ts b/backend/src/models/TicketTraking.ts new file mode 100644 index 0000000..d523102 --- /dev/null +++ b/backend/src/models/TicketTraking.ts @@ -0,0 +1,80 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + ForeignKey, + BelongsTo, + AutoIncrement +} from "sequelize-typescript"; + +import Company from "./Company"; +import User from "./User"; +import Ticket from "./Ticket"; +import Whatsapp from "./Whatsapp"; + +@Table({ + tableName: "TicketTraking" +}) +class TicketTraking extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @BelongsTo(() => Ticket) + ticket: Ticket; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @BelongsTo(() => Whatsapp) + whatsapp: Whatsapp; + + @ForeignKey(() => User) + @Column + userId: number; + + @Column + rated: boolean; + + @BelongsTo(() => User) + user: User; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @Column + startedAt: Date; + + @Column + queuedAt: Date; + + @Column + finishedAt: Date; + + @Column + ratingAt: Date; + + @Column + chatbotAt: Date; +} + +export default TicketTraking; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts new file mode 100644 index 0000000..8da2ae3 --- /dev/null +++ b/backend/src/models/User.ts @@ -0,0 +1,108 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + BeforeCreate, + BeforeUpdate, + PrimaryKey, + AutoIncrement, + Default, + HasMany, + BelongsToMany, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import { hash, compare } from "bcryptjs"; +import Ticket from "./Ticket"; +import Queue from "./Queue"; +import UserQueue from "./UserQueue"; +import Company from "./Company"; +import QuickMessage from "./QuickMessage"; +import Whatsapp from "./Whatsapp"; + +@Table +class User extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @Column + email: string; + + @Column + allTicket: string; + + @Column(DataType.VIRTUAL) + password: string; + + @Column + passwordHash: string; + + @Default(0) + @Column + tokenVersion: number; + + @Default("admin") + @Column + profile: string; + + @Column + super: boolean; + + @Column + online: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @HasMany(() => Ticket) + tickets: Ticket[]; + + @BelongsToMany(() => Queue, () => UserQueue) + queues: Queue[]; + + @HasMany(() => QuickMessage, { + onUpdate: "CASCADE", + onDelete: "CASCADE", + hooks: true + }) + quickMessages: QuickMessage[]; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @BelongsTo(() => Whatsapp) + whatsapp: Whatsapp; + + @BeforeUpdate + @BeforeCreate + static hashPassword = async (instance: User): Promise => { + if (instance.password) { + instance.passwordHash = await hash(instance.password, 8); + } + }; + + public checkPassword = async (password: string): Promise => { + return compare(password, this.getDataValue("passwordHash")); + }; +} + +export default User; diff --git a/backend/src/models/UserQueue.ts b/backend/src/models/UserQueue.ts new file mode 100644 index 0000000..17528c2 --- /dev/null +++ b/backend/src/models/UserQueue.ts @@ -0,0 +1,29 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + ForeignKey +} from "sequelize-typescript"; +import Queue from "./Queue"; +import User from "./User"; + +@Table +class UserQueue extends Model { + @ForeignKey(() => User) + @Column + userId: number; + + @ForeignKey(() => Queue) + @Column + queueId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default UserQueue; diff --git a/backend/src/models/UserRating.ts b/backend/src/models/UserRating.ts new file mode 100644 index 0000000..9f033e4 --- /dev/null +++ b/backend/src/models/UserRating.ts @@ -0,0 +1,58 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + ForeignKey, + BelongsTo, + AutoIncrement + } from "sequelize-typescript"; + + import Company from "./Company"; + import User from "./User"; + import Ticket from "./Ticket"; + + @Table({ + tableName: "UserRatings" + }) + class UserRating extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @BelongsTo(() => Ticket) + ticket: Ticket; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => User) + user: User; + + @Column + rate: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + } + + export default UserRating; + \ No newline at end of file diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts new file mode 100644 index 0000000..8f0d076 --- /dev/null +++ b/backend/src/models/Whatsapp.ts @@ -0,0 +1,148 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey, + AutoIncrement, + Default, + AllowNull, + HasMany, + Unique, + BelongsToMany, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Queue from "./Queue"; +import Ticket from "./Ticket"; +import WhatsappQueue from "./WhatsappQueue"; +import Company from "./Company"; +import Prompt from "./Prompt"; +import QueueIntegrations from "./QueueIntegrations"; + +@Table +class Whatsapp extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @AllowNull + @Unique + @Column(DataType.TEXT) + name: string; + + @Column(DataType.TEXT) + session: string; + + @Column(DataType.TEXT) + qrcode: string; + + @Column + status: string; + + @Column + battery: string; + + @Column + plugged: boolean; + + @Column + retries: number; + + @Default("") + @Column(DataType.TEXT) + greetingMessage: string; + + @Default("") + @Column(DataType.TEXT) + farewellMessage: string; + + @Default("") + @Column(DataType.TEXT) + complationMessage: string; + + @Default("") + @Column(DataType.TEXT) + outOfHoursMessage: string; + + @Default("") + @Column(DataType.TEXT) + ratingMessage: string; + + @Column({ defaultValue: "stable" }) + provider: string; + + @Default(false) + @AllowNull + @Column + isDefault: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @HasMany(() => Ticket) + tickets: Ticket[]; + + @BelongsToMany(() => Queue, () => WhatsappQueue) + queues: Array; + + @HasMany(() => WhatsappQueue) + whatsappQueues: WhatsappQueue[]; + + @ForeignKey(() => Company) + @Column + companyId: number; + + @BelongsTo(() => Company) + company: Company; + + @Column + token: string; + + //@Default(0) + //@Column + //timeSendQueue: number; + + //@Column + //sendIdQueue: number; + + @Column + transferQueueId: number; + + @Column + timeToTransfer: number; + + @ForeignKey(() => Prompt) + @Column + promptId: number; + + @BelongsTo(() => Prompt) + prompt: Prompt; + + @ForeignKey(() => QueueIntegrations) + @Column + integrationId: number; + + @BelongsTo(() => QueueIntegrations) + queueIntegrations: QueueIntegrations; + + @Column + maxUseBotQueues: number; + + @Column + timeUseBotQueues: string; + + @Column + expiresTicket: number; + + @Column + expiresInactiveMessage: string; +} + +export default Whatsapp; diff --git a/backend/src/models/WhatsappQueue.ts b/backend/src/models/WhatsappQueue.ts new file mode 100644 index 0000000..b68aaa0 --- /dev/null +++ b/backend/src/models/WhatsappQueue.ts @@ -0,0 +1,33 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Queue from "./Queue"; +import Whatsapp from "./Whatsapp"; + +@Table +class WhatsappQueue extends Model { + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @ForeignKey(() => Queue) + @Column + queueId: number; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @BelongsTo(() => Queue) + queue: Queue; +} + +export default WhatsappQueue; diff --git a/backend/src/queues.ts b/backend/src/queues.ts new file mode 100644 index 0000000..b2d9fe9 --- /dev/null +++ b/backend/src/queues.ts @@ -0,0 +1,930 @@ +import * as Sentry from "@sentry/node"; +import BullQueue from "bull"; +import { MessageData, SendMessage } from "./helpers/SendMessage"; +import Whatsapp from "./models/Whatsapp"; +import { logger } from "./utils/logger"; +import moment from "moment"; +import Schedule from "./models/Schedule"; +import Contact from "./models/Contact"; +import { Op, QueryTypes, Sequelize } from "sequelize"; +import GetDefaultWhatsApp from "./helpers/GetDefaultWhatsApp"; +import Campaign from "./models/Campaign"; +import ContactList from "./models/ContactList"; +import ContactListItem from "./models/ContactListItem"; +import { isEmpty, isNil, isArray } from "lodash"; +import CampaignSetting from "./models/CampaignSetting"; +import CampaignShipping from "./models/CampaignShipping"; +import GetWhatsappWbot from "./helpers/GetWhatsappWbot"; +import sequelize from "./database"; +import { getMessageOptions } from "./services/WbotServices/SendWhatsAppMedia"; +import { getIO } from "./libs/socket"; +import path from "path"; +import User from "./models/User"; +import Company from "./models/Company"; +import Plan from "./models/Plan"; +import Ticket from "./models/Ticket"; +import ShowFileService from "./services/FileServices/ShowService"; +import FilesOptions from './models/FilesOptions'; +import { addSeconds, differenceInSeconds } from "date-fns"; +import formatBody from "./helpers/Mustache"; +import { ClosedAllOpenTickets } from "./services/WbotServices/wbotClosedTickets"; + + +const nodemailer = require('nodemailer'); +const CronJob = require('cron').CronJob; + +const connection = process.env.REDIS_URI || ""; +const limiterMax = process.env.REDIS_OPT_LIMITER_MAX || 1; +const limiterDuration = process.env.REDIS_OPT_LIMITER_DURATION || 3000; + +interface ProcessCampaignData { + id: number; + delay: number; +} + +interface PrepareContactData { + contactId: number; + campaignId: number; + delay: number; + variables: any[]; +} + +interface DispatchCampaignData { + campaignId: number; + campaignShippingId: number; + contactListItemId: number; +} + +export const userMonitor = new BullQueue("UserMonitor", connection); + +export const queueMonitor = new BullQueue("QueueMonitor", connection); + +export const messageQueue = new BullQueue("MessageQueue", connection, { + limiter: { + max: limiterMax as number, + duration: limiterDuration as number + } +}); + +export const scheduleMonitor = new BullQueue("ScheduleMonitor", connection); +export const sendScheduledMessages = new BullQueue( + "SendSacheduledMessages", + connection +); + +export const campaignQueue = new BullQueue("CampaignQueue", connection); + +async function handleSendMessage(job) { + try { + const { data } = job; + + const whatsapp = await Whatsapp.findByPk(data.whatsappId); + + if (whatsapp == null) { + throw Error("Whatsapp não identificado"); + } + + const messageData: MessageData = data.data; + + await SendMessage(whatsapp, messageData); + } catch (e: any) { + Sentry.captureException(e); + logger.error("MessageQueue -> SendMessage: error", e.message); + throw e; + } +} + +{/*async function handleVerifyQueue(job) { + logger.info("Buscando atendimentos perdidos nas filas"); + try { + const companies = await Company.findAll({ + attributes: ['id', 'name'], + where: { + status: true, + dueDate: { + [Op.gt]: Sequelize.literal('CURRENT_DATE') + } + }, + include: [ + { + model: Whatsapp, attributes: ["id", "name", "status", "timeSendQueue", "sendIdQueue"], where: { + timeSendQueue: { + [Op.gt]: 0 + } + } + }, + ] + }); */} + +{/* companies.map(async c => { + c.whatsapps.map(async w => { + + if (w.status === "CONNECTED") { + + var companyId = c.id; + + const moveQueue = w.timeSendQueue ? w.timeSendQueue : 0; + const moveQueueId = w.sendIdQueue; + const moveQueueTime = moveQueue; + const idQueue = moveQueueId; + const timeQueue = moveQueueTime; + + if (moveQueue > 0) { + + if (!isNaN(idQueue) && Number.isInteger(idQueue) && !isNaN(timeQueue) && Number.isInteger(timeQueue)) { + + const tempoPassado = moment().subtract(timeQueue, "minutes").utc().format(); + // const tempoAgora = moment().utc().format(); + + const { count, rows: tickets } = await Ticket.findAndCountAll({ + where: { + status: "pending", + queueId: null, + companyId: companyId, + whatsappId: w.id, + updatedAt: { + [Op.lt]: tempoPassado + } + }, + include: [ + { + model: Contact, + as: "contact", + attributes: ["id", "name", "number", "email", "profilePicUrl"], + include: ["extraInfo"] + } + ] + }); + + if (count > 0) { + tickets.map(async ticket => { + await ticket.update({ + queueId: idQueue + }); + + await ticket.reload(); + + const io = getIO(); + io.to(ticket.status) + .to("notification") + .to(ticket.id.toString()) + .emit(`company-${companyId}-ticket`, { + action: "update", + ticket, + ticketId: ticket.id + }); + + // io.to("pending").emit(`company-${companyId}-ticket`, { + // action: "update", + // ticket, + // }); + + logger.info(`Atendimento Perdido: ${ticket.id} - Empresa: ${companyId}`); + }); + } else { + logger.info(`Nenhum atendimento perdido encontrado - Empresa: ${companyId}`); + } + } else { + logger.info(`Condição não respeitada - Empresa: ${companyId}`); + } + } + } + }); + }); + } catch (e: any) { + Sentry.captureException(e); + logger.error("SearchForQueue -> VerifyQueue: error", e.message); + throw e; + } +}; */} + +async function handleCloseTicketsAutomatic() { + const job = new CronJob('*/1 * * * *', async () => { + const companies = await Company.findAll(); + companies.map(async c => { + + try { + const companyId = c.id; + await ClosedAllOpenTickets(companyId); + } catch (e: any) { + Sentry.captureException(e); + logger.error("ClosedAllOpenTickets -> Verify: error", e.message); + throw e; + } + + }); + }); + job.start() +} + +async function handleVerifySchedules(job) { + try { + const { count, rows: schedules } = await Schedule.findAndCountAll({ + where: { + status: "PENDENTE", + sentAt: null, + sendAt: { + [Op.gte]: moment().format("YYYY-MM-DD HH:mm:ss"), + [Op.lte]: moment().add("30", "seconds").format("YYYY-MM-DD HH:mm:ss") + } + }, + include: [{ model: Contact, as: "contact" }] + }); + if (count > 0) { + schedules.map(async schedule => { + await schedule.update({ + status: "AGENDADA" + }); + sendScheduledMessages.add( + "SendMessage", + { schedule }, + { delay: 40000 } + ); + logger.info(`Disparo agendado para: ${schedule.contact.name}`); + }); + } + } catch (e: any) { + Sentry.captureException(e); + logger.error("SendScheduledMessage -> Verify: error", e.message); + throw e; + } +} + +async function handleSendScheduledMessage(job) { + const { + data: { schedule } + } = job; + let scheduleRecord: Schedule | null = null; + + try { + scheduleRecord = await Schedule.findByPk(schedule.id); + } catch (e) { + Sentry.captureException(e); + logger.info(`Erro ao tentar consultar agendamento: ${schedule.id}`); + } + + try { + const whatsapp = await GetDefaultWhatsApp(schedule.companyId); + + let filePath = null; + if (schedule.mediaPath) { + filePath = path.resolve("public", schedule.mediaPath); + } + + await SendMessage(whatsapp, { + number: schedule.contact.number, + body: formatBody(schedule.body, schedule.contact), + mediaPath: filePath + }); + + await scheduleRecord?.update({ + sentAt: moment().format("YYYY-MM-DD HH:mm"), + status: "ENVIADA" + }); + + logger.info(`Mensagem agendada enviada para: ${schedule.contact.name}`); + sendScheduledMessages.clean(15000, "completed"); + } catch (e: any) { + Sentry.captureException(e); + await scheduleRecord?.update({ + status: "ERRO" + }); + logger.error("SendScheduledMessage -> SendMessage: error", e.message); + throw e; + } +} + +async function handleVerifyCampaigns(job) { + /** + * @todo + * Implementar filtro de campanhas + */ + const campaigns: { id: number; scheduledAt: string }[] = + await sequelize.query( + `select id, "scheduledAt" from "Campaigns" c + where "scheduledAt" between now() and now() + '1 hour'::interval and status = 'PROGRAMADA'`, + { type: QueryTypes.SELECT } + ); + + if (campaigns.length > 0) + logger.info(`Campanhas encontradas: ${campaigns.length}`); + + for (let campaign of campaigns) { + try { + const now = moment(); + const scheduledAt = moment(campaign.scheduledAt); + const delay = scheduledAt.diff(now, "milliseconds"); + logger.info( + `Campanha enviada para a fila de processamento: Campanha=${campaign.id}, Delay Inicial=${delay}` + ); + campaignQueue.add( + "ProcessCampaign", + { + id: campaign.id, + delay + }, + { + removeOnComplete: true + } + ); + } catch (err: any) { + Sentry.captureException(err); + } + } +} + +async function getCampaign(id) { + return await Campaign.findByPk(id, { + include: [ + { + model: ContactList, + as: "contactList", + attributes: ["id", "name"], + include: [ + { + model: ContactListItem, + as: "contacts", + attributes: ["id", "name", "number", "email", "isWhatsappValid"], + where: { isWhatsappValid: true } + } + ] + }, + { + model: Whatsapp, + as: "whatsapp", + attributes: ["id", "name"] + }, + { + model: CampaignShipping, + as: "shipping", + include: [{ model: ContactListItem, as: "contact" }] + } + ] + }); +} + +async function getContact(id) { + return await ContactListItem.findByPk(id, { + attributes: ["id", "name", "number", "email"] + }); +} + +async function getSettings(campaign) { + const settings = await CampaignSetting.findAll({ + where: { companyId: campaign.companyId }, + attributes: ["key", "value"] + }); + + let messageInterval: number = 20; + let longerIntervalAfter: number = 20; + let greaterInterval: number = 60; + let variables: any[] = []; + + settings.forEach(setting => { + if (setting.key === "messageInterval") { + messageInterval = JSON.parse(setting.value); + } + if (setting.key === "longerIntervalAfter") { + longerIntervalAfter = JSON.parse(setting.value); + } + if (setting.key === "greaterInterval") { + greaterInterval = JSON.parse(setting.value); + } + if (setting.key === "variables") { + variables = JSON.parse(setting.value); + } + }); + + return { + messageInterval, + longerIntervalAfter, + greaterInterval, + variables + }; +} + +export function parseToMilliseconds(seconds) { + return seconds * 1000; +} + +async function sleep(seconds) { + logger.info( + `Sleep de ${seconds} segundos iniciado: ${moment().format("HH:mm:ss")}` + ); + return new Promise(resolve => { + setTimeout(() => { + logger.info( + `Sleep de ${seconds} segundos finalizado: ${moment().format( + "HH:mm:ss" + )}` + ); + resolve(true); + }, parseToMilliseconds(seconds)); + }); +} + +function getCampaignValidMessages(campaign) { + const messages = []; + + if (!isEmpty(campaign.message1) && !isNil(campaign.message1)) { + messages.push(campaign.message1); + } + + if (!isEmpty(campaign.message2) && !isNil(campaign.message2)) { + messages.push(campaign.message2); + } + + if (!isEmpty(campaign.message3) && !isNil(campaign.message3)) { + messages.push(campaign.message3); + } + + if (!isEmpty(campaign.message4) && !isNil(campaign.message4)) { + messages.push(campaign.message4); + } + + if (!isEmpty(campaign.message5) && !isNil(campaign.message5)) { + messages.push(campaign.message5); + } + + return messages; +} + +function getCampaignValidConfirmationMessages(campaign) { + const messages = []; + + if ( + !isEmpty(campaign.confirmationMessage1) && + !isNil(campaign.confirmationMessage1) + ) { + messages.push(campaign.confirmationMessage1); + } + + if ( + !isEmpty(campaign.confirmationMessage2) && + !isNil(campaign.confirmationMessage2) + ) { + messages.push(campaign.confirmationMessage2); + } + + if ( + !isEmpty(campaign.confirmationMessage3) && + !isNil(campaign.confirmationMessage3) + ) { + messages.push(campaign.confirmationMessage3); + } + + if ( + !isEmpty(campaign.confirmationMessage4) && + !isNil(campaign.confirmationMessage4) + ) { + messages.push(campaign.confirmationMessage4); + } + + if ( + !isEmpty(campaign.confirmationMessage5) && + !isNil(campaign.confirmationMessage5) + ) { + messages.push(campaign.confirmationMessage5); + } + + return messages; +} + +function getProcessedMessage(msg: string, variables: any[], contact: any) { + let finalMessage = msg; + + if (finalMessage.includes("{nome}")) { + finalMessage = finalMessage.replace(/{nome}/g, contact.name); + } + + if (finalMessage.includes("{email}")) { + finalMessage = finalMessage.replace(/{email}/g, contact.email); + } + + if (finalMessage.includes("{numero}")) { + finalMessage = finalMessage.replace(/{numero}/g, contact.number); + } + + variables.forEach(variable => { + if (finalMessage.includes(`{${variable.key}}`)) { + const regex = new RegExp(`{${variable.key}}`, "g"); + finalMessage = finalMessage.replace(regex, variable.value); + } + }); + + return finalMessage; +} + +export function randomValue(min, max) { + return Math.floor(Math.random() * max) + min; +} + +async function verifyAndFinalizeCampaign(campaign) { + const { contacts } = campaign.contactList; + + const count1 = contacts.length; + const count2 = await CampaignShipping.count({ + where: { + campaignId: campaign.id, + deliveredAt: { + [Op.not]: null + } + } + }); + + if (count1 === count2) { + await campaign.update({ status: "FINALIZADA", completedAt: moment() }); + } + + const io = getIO(); + io.to(`company-${campaign.companyId}-mainchannel`).emit(`company-${campaign.companyId}-campaign`, { + action: "update", + record: campaign + }); +} + +function calculateDelay(index, baseDelay, longerIntervalAfter, greaterInterval, messageInterval) { + const diffSeconds = differenceInSeconds(baseDelay, new Date()); + if (index > longerIntervalAfter) { + return diffSeconds * 1000 + greaterInterval + } else { + return diffSeconds * 1000 + messageInterval + } +} + +async function handleProcessCampaign(job) { + try { + const { id }: ProcessCampaignData = job.data; + const campaign = await getCampaign(id); + const settings = await getSettings(campaign); + if (campaign) { + const { contacts } = campaign.contactList; + if (isArray(contacts)) { + const contactData = contacts.map(contact => ({ + contactId: contact.id, + campaignId: campaign.id, + variables: settings.variables, + })); + + // const baseDelay = job.data.delay || 0; + const longerIntervalAfter = parseToMilliseconds(settings.longerIntervalAfter); + const greaterInterval = parseToMilliseconds(settings.greaterInterval); + const messageInterval = settings.messageInterval; + + let baseDelay = campaign.scheduledAt; + + const queuePromises = []; + for (let i = 0; i < contactData.length; i++) { + baseDelay = addSeconds(baseDelay, i > longerIntervalAfter ? greaterInterval : messageInterval); + + const { contactId, campaignId, variables } = contactData[i]; + const delay = calculateDelay(i, baseDelay, longerIntervalAfter, greaterInterval, messageInterval); + const queuePromise = campaignQueue.add( + "PrepareContact", + { contactId, campaignId, variables, delay }, + { removeOnComplete: true } + ); + queuePromises.push(queuePromise); + logger.info(`Registro enviado pra fila de disparo: Campanha=${campaign.id};Contato=${contacts[i].name};delay=${delay}`); + } + await Promise.all(queuePromises); + await campaign.update({ status: "EM_ANDAMENTO" }); + } + } + } catch (err: any) { + Sentry.captureException(err); + } +} + +async function handlePrepareContact(job) { + try { + const { contactId, campaignId, delay, variables }: PrepareContactData = + job.data; + const campaign = await getCampaign(campaignId); + const contact = await getContact(contactId); + + const campaignShipping: any = {}; + campaignShipping.number = contact.number; + campaignShipping.contactId = contactId; + campaignShipping.campaignId = campaignId; + + const messages = getCampaignValidMessages(campaign); + if (messages.length) { + const radomIndex = randomValue(0, messages.length); + const message = getProcessedMessage( + messages[radomIndex], + variables, + contact + ); + campaignShipping.message = `\u200c ${message}`; + } + + if (campaign.confirmation) { + const confirmationMessages = + getCampaignValidConfirmationMessages(campaign); + if (confirmationMessages.length) { + const radomIndex = randomValue(0, confirmationMessages.length); + const message = getProcessedMessage( + confirmationMessages[radomIndex], + variables, + contact + ); + campaignShipping.confirmationMessage = `\u200c ${message}`; + } + } + + const [record, created] = await CampaignShipping.findOrCreate({ + where: { + campaignId: campaignShipping.campaignId, + contactId: campaignShipping.contactId + }, + defaults: campaignShipping + }); + + if ( + !created && + record.deliveredAt === null && + record.confirmationRequestedAt === null + ) { + record.set(campaignShipping); + await record.save(); + } + + if ( + record.deliveredAt === null && + record.confirmationRequestedAt === null + ) { + const nextJob = await campaignQueue.add( + "DispatchCampaign", + { + campaignId: campaign.id, + campaignShippingId: record.id, + contactListItemId: contactId + }, + { + delay + } + ); + + await record.update({ jobId: nextJob.id }); + } + + await verifyAndFinalizeCampaign(campaign); + } catch (err: any) { + Sentry.captureException(err); + logger.error(`campaignQueue -> PrepareContact -> error: ${err.message}`); + } +} + +async function handleDispatchCampaign(job) { + try { + const { data } = job; + const { campaignShippingId, campaignId }: DispatchCampaignData = data; + const campaign = await getCampaign(campaignId); + const wbot = await GetWhatsappWbot(campaign.whatsapp); + + if (!wbot) { + logger.error(`campaignQueue -> DispatchCampaign -> error: wbot not found`); + return; + } + + if (!campaign.whatsapp) { + logger.error(`campaignQueue -> DispatchCampaign -> error: whatsapp not found`); + return; + } + + if (!wbot?.user?.id) { + logger.error(`campaignQueue -> DispatchCampaign -> error: wbot user not found`); + return; + } + + logger.info( + `Disparo de campanha solicitado: Campanha=${campaignId};Registro=${campaignShippingId}` + ); + + const campaignShipping = await CampaignShipping.findByPk( + campaignShippingId, + { + include: [{ model: ContactListItem, as: "contact" }] + } + ); + + const chatId = `${campaignShipping.number}@s.whatsapp.net`; + + let body = campaignShipping.message; + + if (campaign.confirmation && campaignShipping.confirmation === null) { + body = campaignShipping.confirmationMessage + } + + if (!isNil(campaign.fileListId)) { + try { + const publicFolder = path.resolve(__dirname, "..", "public"); + const files = await ShowFileService(campaign.fileListId, campaign.companyId) + const folder = path.resolve(publicFolder, "fileList", String(files.id)) + for (const [index, file] of files.options.entries()) { + const options = await getMessageOptions(file.path, path.resolve(folder, file.path), file.name); + await wbot.sendMessage(chatId, { ...options }); + }; + } catch (error) { + logger.info(error); + } + } + + if (campaign.mediaPath) { + const publicFolder = path.resolve(__dirname, "..", "public"); + const filePath = path.join(publicFolder, campaign.mediaPath); + + const options = await getMessageOptions(campaign.mediaName, filePath, body); + if (Object.keys(options).length) { + await wbot.sendMessage(chatId, { ...options }); + } + } + else { + if (campaign.confirmation && campaignShipping.confirmation === null) { + await wbot.sendMessage(chatId, { + text: body + }); + await campaignShipping.update({ confirmationRequestedAt: moment() }); + } else { + + await wbot.sendMessage(chatId, { + text: body + }); + } + } + await campaignShipping.update({ deliveredAt: moment() }); + + await verifyAndFinalizeCampaign(campaign); + + const io = getIO(); + io.to(`company-${campaign.companyId}-mainchannel`).emit(`company-${campaign.companyId}-campaign`, { + action: "update", + record: campaign + }); + + logger.info( + `Campanha enviada para: Campanha=${campaignId};Contato=${campaignShipping.contact.name}` + ); + } catch (err: any) { + Sentry.captureException(err); + logger.error(err.message); + console.log(err.stack); + } +} + +async function handleLoginStatus(job) { + const users: { id: number }[] = await sequelize.query( + `select id from "Users" where "updatedAt" < now() - '5 minutes'::interval and online = true`, + { type: QueryTypes.SELECT } + ); + for (let item of users) { + try { + const user = await User.findByPk(item.id); + await user.update({ online: false }); + logger.info(`Usuário passado para offline: ${item.id}`); + } catch (e: any) { + Sentry.captureException(e); + } + } +} + + +async function handleInvoiceCreate() { + logger.info("Iniciando geração de boletos"); + const job = new CronJob('*/5 * * * * *', async () => { + + + const companies = await Company.findAll(); + companies.map(async c => { + var dueDate = c.dueDate; + const date = moment(dueDate).format(); + const timestamp = moment().format(); + const hoje = moment(moment()).format("DD/MM/yyyy"); + var vencimento = moment(dueDate).format("DD/MM/yyyy"); + + var diff = moment(vencimento, "DD/MM/yyyy").diff(moment(hoje, "DD/MM/yyyy")); + var dias = moment.duration(diff).asDays(); + + if (dias < 20) { + const plan = await Plan.findByPk(c.planId); + + const sql = `SELECT COUNT(*) mycount FROM "Invoices" WHERE "companyId" = ${c.id} AND "dueDate"::text LIKE '${moment(dueDate).format("yyyy-MM-DD")}%';` + const invoice = await sequelize.query(sql, + { type: QueryTypes.SELECT } + ); + if (invoice[0]['mycount'] > 0) { + + } else { + const sql = `INSERT INTO "Invoices" (detail, status, value, "updatedAt", "createdAt", "dueDate", "companyId") + VALUES ('${plan.name}', 'open', '${plan.value}', '${timestamp}', '${timestamp}', '${date}', ${c.id});` + + const invoiceInsert = await sequelize.query(sql, + { type: QueryTypes.INSERT } + ); + + /* let transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: 'email@gmail.com', + pass: 'senha' + } + }); + + const mailOptions = { + from: 'heenriquega@gmail.com', // sender address + to: `${c.email}`, // receiver (use array of string for a list) + subject: 'Fatura gerada - Sistema', // Subject line + html: `Olá ${c.name} esté é um email sobre sua fatura!
+
+ Vencimento: ${vencimento}
+ Valor: ${plan.value}
+ Link: ${process.env.FRONTEND_URL}/financeiro
+
+ Qualquer duvida estamos a disposição! + `// plain text body + }; + + transporter.sendMail(mailOptions, (err, info) => { + if (err) + console.log(err) + else + console.log(info); + }); */ + + } + + + + + + } + + }); + }); + job.start() +} + +handleCloseTicketsAutomatic() + +handleInvoiceCreate() + +export async function startQueueProcess() { + logger.info("Iniciando processamento de filas"); + + messageQueue.process("SendMessage", handleSendMessage); + + scheduleMonitor.process("Verify", handleVerifySchedules); + + sendScheduledMessages.process("SendMessage", handleSendScheduledMessage); + + campaignQueue.process("VerifyCampaigns", handleVerifyCampaigns); + + campaignQueue.process("ProcessCampaign", handleProcessCampaign); + + campaignQueue.process("PrepareContact", handlePrepareContact); + + campaignQueue.process("DispatchCampaign", handleDispatchCampaign); + + userMonitor.process("VerifyLoginStatus", handleLoginStatus); + + //queueMonitor.process("VerifyQueueStatus", handleVerifyQueue); + + + + scheduleMonitor.add( + "Verify", + {}, + { + repeat: { cron: "*/5 * * * * *", key: "verify" }, + removeOnComplete: true + } + ); + + campaignQueue.add( + "VerifyCampaigns", + {}, + { + repeat: { cron: "*/20 * * * * *", key: "verify-campaing" }, + removeOnComplete: true + } + ); + + userMonitor.add( + "VerifyLoginStatus", + {}, + { + repeat: { cron: "* * * * *", key: "verify-login" }, + removeOnComplete: true + } + ); + + queueMonitor.add( + "VerifyQueueStatus", + {}, + { + repeat: { cron: "*/20 * * * * *" }, + removeOnComplete: true + } + ); +} diff --git a/backend/src/routes/announcementRoutes.ts b/backend/src/routes/announcementRoutes.ts new file mode 100644 index 0000000..3b64f0d --- /dev/null +++ b/backend/src/routes/announcementRoutes.ts @@ -0,0 +1,38 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import isSuper from "../middleware/isSuper"; + +import * as AnnouncementController from "../controllers/AnnouncementController"; +import multer from "multer"; +import uploadConfig from "../config/upload"; + +const upload = multer(uploadConfig); + +const routes = express.Router(); + +routes.get("/announcements/list", isAuth, AnnouncementController.findList); + +routes.get("/announcements", isAuth, AnnouncementController.index); + +routes.get("/announcements/:id", isAuth, AnnouncementController.show); + +routes.post("/announcements", isAuth, isSuper, AnnouncementController.store); + +routes.put("/announcements/:id", isAuth, isSuper, AnnouncementController.update); + +routes.delete("/announcements/:id", isAuth, isSuper, AnnouncementController.remove); + +routes.post( + "/announcements/:id/media-upload", + isAuth, isSuper, + upload.array("file"), + AnnouncementController.mediaUpload +); + +routes.delete( + "/announcements/:id/media-upload", + isAuth, isSuper, + AnnouncementController.deleteMedia +); + +export default routes; diff --git a/backend/src/routes/authRoutes.ts b/backend/src/routes/authRoutes.ts new file mode 100644 index 0000000..8a5c3b0 --- /dev/null +++ b/backend/src/routes/authRoutes.ts @@ -0,0 +1,15 @@ +import { Router } from "express"; +import * as SessionController from "../controllers/SessionController"; +import * as UserController from "../controllers/UserController"; +import isAuth from "../middleware/isAuth"; +import envTokenAuth from "../middleware/envTokenAuth"; + +const authRoutes = Router(); + +authRoutes.post("/signup", envTokenAuth, UserController.store); +authRoutes.post("/login", SessionController.store); +authRoutes.post("/refresh_token", SessionController.update); +authRoutes.delete("/logout", isAuth, SessionController.remove); +authRoutes.get("/me", isAuth, SessionController.me); + +export default authRoutes; diff --git a/backend/src/routes/campaignRoutes.ts b/backend/src/routes/campaignRoutes.ts new file mode 100644 index 0000000..2a794fd --- /dev/null +++ b/backend/src/routes/campaignRoutes.ts @@ -0,0 +1,41 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as CampaignController from "../controllers/CampaignController"; +import multer from "multer"; +import uploadConfig from "../config/upload"; + +const upload = multer(uploadConfig); + +const routes = express.Router(); + +routes.get("/campaigns/list", isAuth, CampaignController.findList); + +routes.get("/campaigns", isAuth, CampaignController.index); + +routes.get("/campaigns/:id", isAuth, CampaignController.show); + +routes.post("/campaigns", isAuth, CampaignController.store); + +routes.put("/campaigns/:id", isAuth, CampaignController.update); + +routes.delete("/campaigns/:id", isAuth, CampaignController.remove); + +routes.post("/campaigns/:id/cancel", isAuth, CampaignController.cancel); + +routes.post("/campaigns/:id/restart", isAuth, CampaignController.restart); + +routes.post( + "/campaigns/:id/media-upload", + isAuth, + upload.array("file"), + CampaignController.mediaUpload +); + +routes.delete( + "/campaigns/:id/media-upload", + isAuth, + CampaignController.deleteMedia +); + +export default routes; diff --git a/backend/src/routes/campaignSettingRoutes.ts b/backend/src/routes/campaignSettingRoutes.ts new file mode 100644 index 0000000..572ea57 --- /dev/null +++ b/backend/src/routes/campaignSettingRoutes.ts @@ -0,0 +1,16 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as CampaignSettingController from "../controllers/CampaignSettingController"; +import multer from "multer"; +import uploadConfig from "../config/upload"; + +const upload = multer(uploadConfig); + +const routes = express.Router(); + +routes.get("/campaign-settings", isAuth, CampaignSettingController.index); + +routes.post("/campaign-settings", isAuth, CampaignSettingController.store); + +export default routes; diff --git a/backend/src/routes/chatRoutes.ts b/backend/src/routes/chatRoutes.ts new file mode 100644 index 0000000..1d27994 --- /dev/null +++ b/backend/src/routes/chatRoutes.ts @@ -0,0 +1,24 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as ChatController from "../controllers/ChatController"; + +const routes = express.Router(); + +routes.get("/chats", isAuth, ChatController.index); + +routes.get("/chats/:id", isAuth, ChatController.show); + +routes.get("/chats/:id/messages", isAuth, ChatController.messages); + +routes.post("/chats/:id/messages", isAuth, ChatController.saveMessage); + +routes.post("/chats/:id/read", isAuth, ChatController.checkAsRead); + +routes.post("/chats", isAuth, ChatController.store); + +routes.put("/chats/:id", isAuth, ChatController.update); + +routes.delete("/chats/:id", isAuth, ChatController.remove); + +export default routes; diff --git a/backend/src/routes/companyRoutes.ts b/backend/src/routes/companyRoutes.ts new file mode 100644 index 0000000..99aa631 --- /dev/null +++ b/backend/src/routes/companyRoutes.ts @@ -0,0 +1,22 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import isSuper from "../middleware/isSuper"; + +import * as CompanyController from "../controllers/CompanyController"; + +const companyRoutes = express.Router(); + +companyRoutes.get("/companies/list", isAuth, isSuper, CompanyController.list); +companyRoutes.get("/companies", isAuth, isSuper, CompanyController.index); +companyRoutes.get("/companies/:id", isAuth, CompanyController.show); +companyRoutes.post("/companies", isAuth, isSuper, CompanyController.store); +companyRoutes.put("/companies/:id", isAuth, isSuper, CompanyController.update); +companyRoutes.put("/companies/:id/schedules",isAuth,CompanyController.updateSchedules); +companyRoutes.delete("/companies/:id", isAuth, isSuper, CompanyController.remove); +companyRoutes.post("/companies/cadastro", CompanyController.store); + +// Rota para listar o plano da empresa +companyRoutes.get("/companies/listPlan/:id", isAuth, CompanyController.listPlan); +companyRoutes.get("/companiesPlan", isAuth, CompanyController.indexPlan); + +export default companyRoutes; diff --git a/backend/src/routes/contactListItemRoutes.ts b/backend/src/routes/contactListItemRoutes.ts new file mode 100644 index 0000000..7f91592 --- /dev/null +++ b/backend/src/routes/contactListItemRoutes.ts @@ -0,0 +1,28 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as ContactListItemController from "../controllers/ContactListItemController"; + +const routes = express.Router(); + +routes.get( + "/contact-list-items/list", + isAuth, + ContactListItemController.findList +); + +routes.get("/contact-list-items", isAuth, ContactListItemController.index); + +routes.get("/contact-list-items/:id", isAuth, ContactListItemController.show); + +routes.post("/contact-list-items", isAuth, ContactListItemController.store); + +routes.put("/contact-list-items/:id", isAuth, ContactListItemController.update); + +routes.delete( + "/contact-list-items/:id", + isAuth, + ContactListItemController.remove +); + +export default routes; diff --git a/backend/src/routes/contactListRoutes.ts b/backend/src/routes/contactListRoutes.ts new file mode 100644 index 0000000..574e567 --- /dev/null +++ b/backend/src/routes/contactListRoutes.ts @@ -0,0 +1,31 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import uploadConfig from "../config/upload"; + +import * as ContactListController from "../controllers/ContactListController"; +import multer from "multer"; + +const routes = express.Router(); + +const upload = multer(uploadConfig); + +routes.get("/contact-lists/list", isAuth, ContactListController.findList); + +routes.get("/contact-lists", isAuth, ContactListController.index); + +routes.get("/contact-lists/:id", isAuth, ContactListController.show); + +routes.post("/contact-lists", isAuth, ContactListController.store); + +routes.post( + "/contact-lists/:id/upload", + isAuth, + upload.array("file"), + ContactListController.upload +); + +routes.put("/contact-lists/:id", isAuth, ContactListController.update); + +routes.delete("/contact-lists/:id", isAuth, ContactListController.remove); + +export default routes; diff --git a/backend/src/routes/contactRoutes.ts b/backend/src/routes/contactRoutes.ts new file mode 100644 index 0000000..fbacf80 --- /dev/null +++ b/backend/src/routes/contactRoutes.ts @@ -0,0 +1,27 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as ContactController from "../controllers/ContactController"; +import * as ImportPhoneContactsController from "../controllers/ImportPhoneContactsController"; + +const contactRoutes = express.Router(); + +contactRoutes.post( + "/contacts/import", + isAuth, + ImportPhoneContactsController.store +); + +contactRoutes.get("/contacts", isAuth, ContactController.index); + +contactRoutes.get("/contacts/list", isAuth, ContactController.list); + +contactRoutes.get("/contacts/:contactId", isAuth, ContactController.show); + +contactRoutes.post("/contacts", isAuth, ContactController.store); + +contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update); + +contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove); + +export default contactRoutes; diff --git a/backend/src/routes/dashboardRoutes.ts b/backend/src/routes/dashboardRoutes.ts new file mode 100644 index 0000000..aeec79a --- /dev/null +++ b/backend/src/routes/dashboardRoutes.ts @@ -0,0 +1,12 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as DashboardController from "../controllers/DashbardController"; + +const routes = express.Router(); + +routes.get("/dashboard", isAuth, DashboardController.index); +routes.get("/dashboard/ticketsUsers", DashboardController.reportsUsers); +routes.get("/dashboard/ticketsDay", DashboardController.reportsDay); + +export default routes; diff --git a/backend/src/routes/filesRoutes.ts b/backend/src/routes/filesRoutes.ts new file mode 100644 index 0000000..1b2814c --- /dev/null +++ b/backend/src/routes/filesRoutes.ts @@ -0,0 +1,20 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import uploadConfig from "../config/upload"; +import multer from "multer"; + +import * as FilesController from "../controllers/FilesController"; + +const upload = multer(uploadConfig); + +const filesRoutes = express.Router(); + +filesRoutes.get("/files/list", isAuth, FilesController.list); +filesRoutes.get("/files", isAuth, FilesController.index); +filesRoutes.post("/files", isAuth, FilesController.store); +filesRoutes.put("/files/:fileId", isAuth, FilesController.update); +filesRoutes.get("/files/:fileId", isAuth, FilesController.show); +filesRoutes.delete("/files/:fileId", isAuth, FilesController.remove); +filesRoutes.delete("/files", isAuth, FilesController.removeAll); +filesRoutes.post("/files/uploadList/:fileListId", isAuth, upload.array("files"), FilesController.uploadMedias); +export default filesRoutes; diff --git a/backend/src/routes/forgotPasswordRoutes.ts b/backend/src/routes/forgotPasswordRoutes.ts new file mode 100644 index 0000000..5fae897 --- /dev/null +++ b/backend/src/routes/forgotPasswordRoutes.ts @@ -0,0 +1,10 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import * as ForgotController from "../controllers/ForgotController"; +const forgotsRoutes = express.Router(); +forgotsRoutes.post("/forgetpassword/:email", ForgotController.store); +forgotsRoutes.post( + "/resetpasswords/:email/:token/:password", + ForgotController.resetPasswords +); +export default forgotsRoutes; diff --git a/backend/src/routes/helpRoutes.ts b/backend/src/routes/helpRoutes.ts new file mode 100644 index 0000000..c3c4512 --- /dev/null +++ b/backend/src/routes/helpRoutes.ts @@ -0,0 +1,21 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import isSuper from "../middleware/isSuper"; + +import * as HelpController from "../controllers/HelpController"; + +const routes = express.Router(); + +routes.get("/helps/list", isAuth, HelpController.findList); + +routes.get("/helps", isAuth, HelpController.index); + +routes.get("/helps/:id", isAuth, HelpController.show); + +routes.post("/helps", isAuth, isSuper, HelpController.store); + +routes.put("/helps/:id", isAuth, isSuper, HelpController.update); + +routes.delete("/helps/:id", isAuth, isSuper, HelpController.remove); + +export default routes; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts new file mode 100644 index 0000000..147100c --- /dev/null +++ b/backend/src/routes/index.ts @@ -0,0 +1,71 @@ +import { Router } from "express"; + +import userRoutes from "./userRoutes"; +import authRoutes from "./authRoutes"; +import settingRoutes from "./settingRoutes"; +import contactRoutes from "./contactRoutes"; +import ticketRoutes from "./ticketRoutes"; +import whatsappRoutes from "./whatsappRoutes"; +import messageRoutes from "./messageRoutes"; +import whatsappSessionRoutes from "./whatsappSessionRoutes"; +import queueRoutes from "./queueRoutes"; +import companyRoutes from "./companyRoutes"; +import planRoutes from "./planRoutes"; +import ticketNoteRoutes from "./ticketNoteRoutes"; +import quickMessageRoutes from "./quickMessageRoutes"; +import helpRoutes from "./helpRoutes"; +import dashboardRoutes from "./dashboardRoutes"; +import queueOptionRoutes from "./queueOptionRoutes"; +import scheduleRoutes from "./scheduleRoutes"; +import tagRoutes from "./tagRoutes"; +import contactListRoutes from "./contactListRoutes"; +import contactListItemRoutes from "./contactListItemRoutes"; +import campaignRoutes from "./campaignRoutes"; +import campaignSettingRoutes from "./campaignSettingRoutes"; +import announcementRoutes from "./announcementRoutes"; +import chatRoutes from "./chatRoutes"; +import invoiceRoutes from "./invoicesRoutes"; +import subscriptionRoutes from "./subScriptionRoutes"; +import ticketTagRoutes from "./ticketTagRoutes"; +import filesRoutes from "./filesRoutes"; +import promptRoutes from "./promptRouter"; +import queueIntegrationRoutes from "./queueIntegrationRoutes"; +import forgotsRoutes from "./forgotPasswordRoutes"; +import versionRouter from "./versionRoutes"; +const routes = Router(); + +routes.use(userRoutes); +routes.use("/auth", authRoutes); +routes.use(settingRoutes); +routes.use(contactRoutes); +routes.use(ticketRoutes); +routes.use(whatsappRoutes); +routes.use(messageRoutes); +routes.use(messageRoutes); +routes.use(whatsappSessionRoutes); +routes.use(queueRoutes); +routes.use(companyRoutes); +routes.use(planRoutes); +routes.use(ticketNoteRoutes); +routes.use(quickMessageRoutes); +routes.use(helpRoutes); +routes.use(dashboardRoutes); +routes.use(queueOptionRoutes); +routes.use(scheduleRoutes); +routes.use(tagRoutes); +routes.use(contactListRoutes); +routes.use(contactListItemRoutes); +routes.use(campaignRoutes); +routes.use(campaignSettingRoutes); +routes.use(announcementRoutes); +routes.use(chatRoutes); +routes.use(subscriptionRoutes); +routes.use(invoiceRoutes); +routes.use(ticketTagRoutes); +routes.use(filesRoutes); +routes.use(promptRoutes); +routes.use(queueIntegrationRoutes); +routes.use(forgotsRoutes); +routes.use(versionRouter); + +export default routes; diff --git a/backend/src/routes/invoicesRoutes.ts b/backend/src/routes/invoicesRoutes.ts new file mode 100644 index 0000000..e1ffef0 --- /dev/null +++ b/backend/src/routes/invoicesRoutes.ts @@ -0,0 +1,14 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import * as QueueOptionController from "../controllers/QueueOptionController"; +import * as InvoicesController from "../controllers/InvoicesController" + +const invoiceRoutes = express.Router(); + +invoiceRoutes.get("/invoices", isAuth, InvoicesController.index); +invoiceRoutes.get("/invoices/list", InvoicesController.list); +invoiceRoutes.get("/invoices/all", isAuth, InvoicesController.list); +invoiceRoutes.get("/invoices/:Invoiceid", isAuth, InvoicesController.show); +invoiceRoutes.put("/invoices/:id", isAuth, InvoicesController.update); + +export default invoiceRoutes; diff --git a/backend/src/routes/messageRoutes.ts b/backend/src/routes/messageRoutes.ts new file mode 100644 index 0000000..8e10279 --- /dev/null +++ b/backend/src/routes/messageRoutes.ts @@ -0,0 +1,18 @@ +import { Router } from "express"; +import multer from "multer"; +import isAuth from "../middleware/isAuth"; +import uploadConfig from "../config/upload"; +import tokenAuth from "../middleware/tokenAuth"; + +import * as MessageController from "../controllers/MessageController"; + +const messageRoutes = Router(); + +const upload = multer(uploadConfig); + +messageRoutes.get("/messages/:ticketId", isAuth, MessageController.index); +messageRoutes.post("/messages/:ticketId", isAuth, upload.array("medias"), MessageController.store); +messageRoutes.delete("/messages/:messageId", isAuth, MessageController.remove); +messageRoutes.post("/api/messages/send", tokenAuth, upload.array("medias"), MessageController.send); + +export default messageRoutes; diff --git a/backend/src/routes/planRoutes.ts b/backend/src/routes/planRoutes.ts new file mode 100644 index 0000000..b522f41 --- /dev/null +++ b/backend/src/routes/planRoutes.ts @@ -0,0 +1,23 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; +import isSuper from "../middleware/isSuper"; + +import * as PlanController from "../controllers/PlanController"; + +const planRoutes = express.Router(); + +planRoutes.get("/plans", isAuth, PlanController.index); + +planRoutes.get("/plans/list", PlanController.list); + +planRoutes.get("/plans/all", PlanController.list); + +planRoutes.get("/plans/:id", isAuth, PlanController.show); + +planRoutes.post("/plans", isAuth, isSuper, PlanController.store); + +planRoutes.put("/plans/:id", isAuth, isSuper, PlanController.update); + +planRoutes.delete("/plans/:id", isAuth, isSuper, PlanController.remove); + +export default planRoutes; diff --git a/backend/src/routes/promptRouter.ts b/backend/src/routes/promptRouter.ts new file mode 100644 index 0000000..1b140a6 --- /dev/null +++ b/backend/src/routes/promptRouter.ts @@ -0,0 +1,18 @@ +import { Router } from "express"; +import * as PromptController from "../controllers/PromptController"; +import isAuth from "../middleware/isAuth"; + + +const promptRoutes = Router(); + +promptRoutes.get("/prompt", isAuth, PromptController.index); + +promptRoutes.post("/prompt", isAuth, PromptController.store); + +promptRoutes.get("/prompt/:promptId", isAuth, PromptController.show); + +promptRoutes.put("/prompt/:promptId", isAuth, PromptController.update); + +promptRoutes.delete("/prompt/:promptId", isAuth, PromptController.remove); + +export default promptRoutes; diff --git a/backend/src/routes/queueIntegrationRoutes.ts b/backend/src/routes/queueIntegrationRoutes.ts new file mode 100644 index 0000000..3be2ef6 --- /dev/null +++ b/backend/src/routes/queueIntegrationRoutes.ts @@ -0,0 +1,18 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import * as QueueIntegrationController from "../controllers/QueueIntegrationController"; + +const queueIntegrationRoutes = Router(); + +queueIntegrationRoutes.get("/queueIntegration", isAuth, QueueIntegrationController.index); + +queueIntegrationRoutes.post("/queueIntegration", isAuth, QueueIntegrationController.store); + +queueIntegrationRoutes.get("/queueIntegration/:integrationId", isAuth, QueueIntegrationController.show); + +queueIntegrationRoutes.put("/queueIntegration/:integrationId", isAuth, QueueIntegrationController.update); + +queueIntegrationRoutes.delete("/queueIntegration/:integrationId", isAuth, QueueIntegrationController.remove); + +export default queueIntegrationRoutes; \ No newline at end of file diff --git a/backend/src/routes/queueOptionRoutes.ts b/backend/src/routes/queueOptionRoutes.ts new file mode 100644 index 0000000..711828d --- /dev/null +++ b/backend/src/routes/queueOptionRoutes.ts @@ -0,0 +1,18 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import * as QueueOptionController from "../controllers/QueueOptionController"; + +const queueOptionRoutes = Router(); + +queueOptionRoutes.get("/queue-options", isAuth, QueueOptionController.index); + +queueOptionRoutes.post("/queue-options", isAuth, QueueOptionController.store); + +queueOptionRoutes.get("/queue-options/:queueOptionId", isAuth, QueueOptionController.show); + +queueOptionRoutes.put("/queue-options/:queueOptionId", isAuth, QueueOptionController.update); + +queueOptionRoutes.delete("/queue-options/:queueOptionId", isAuth, QueueOptionController.remove); + +export default queueOptionRoutes; diff --git a/backend/src/routes/queueRoutes.ts b/backend/src/routes/queueRoutes.ts new file mode 100644 index 0000000..a85f5e3 --- /dev/null +++ b/backend/src/routes/queueRoutes.ts @@ -0,0 +1,18 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import * as QueueController from "../controllers/QueueController"; + +const queueRoutes = Router(); + +queueRoutes.get("/queue", isAuth, QueueController.index); + +queueRoutes.post("/queue", isAuth, QueueController.store); + +queueRoutes.get("/queue/:queueId", isAuth, QueueController.show); + +queueRoutes.put("/queue/:queueId", isAuth, QueueController.update); + +queueRoutes.delete("/queue/:queueId", isAuth, QueueController.remove); + +export default queueRoutes; diff --git a/backend/src/routes/quickMessageRoutes.ts b/backend/src/routes/quickMessageRoutes.ts new file mode 100644 index 0000000..412ef1b --- /dev/null +++ b/backend/src/routes/quickMessageRoutes.ts @@ -0,0 +1,37 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as QuickMessageController from "../controllers/QuickMessageController"; +import multer from "multer"; +import uploadConfig from "../config/upload"; + +const upload = multer(uploadConfig); + +const routes = express.Router(); + +routes.get("/quick-messages/list", isAuth, QuickMessageController.findList); + +routes.get("/quick-messages", isAuth, QuickMessageController.index); + +routes.get("/quick-messages/:id", isAuth, QuickMessageController.show); + +routes.post("/quick-messages", isAuth, QuickMessageController.store); + +routes.put("/quick-messages/:id", isAuth, QuickMessageController.update); + +routes.delete("/quick-messages/:id", isAuth, QuickMessageController.remove); + +routes.post( + "/quick-messages/:id/media-upload", + isAuth, + upload.array("file"), + QuickMessageController.mediaUpload + ); + + routes.delete( + "/quick-messages/:id/media-upload", + isAuth, + QuickMessageController.deleteMedia + ); + +export default routes; diff --git a/backend/src/routes/scheduleRoutes.ts b/backend/src/routes/scheduleRoutes.ts new file mode 100644 index 0000000..1f9e6a6 --- /dev/null +++ b/backend/src/routes/scheduleRoutes.ts @@ -0,0 +1,26 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as ScheduleController from "../controllers/ScheduleController"; +import multer from "multer"; +import uploadConfig from "../config/upload"; + +const upload = multer(uploadConfig); + +const scheduleRoutes = express.Router(); + +scheduleRoutes.get("/schedules", isAuth, ScheduleController.index); + +scheduleRoutes.post("/schedules", isAuth, ScheduleController.store); + +scheduleRoutes.put("/schedules/:scheduleId", isAuth, ScheduleController.update); + +scheduleRoutes.get("/schedules/:scheduleId", isAuth, ScheduleController.show); + +scheduleRoutes.delete("/schedules/:scheduleId", isAuth, ScheduleController.remove); + +scheduleRoutes.post("/schedules/:id/media-upload", isAuth, upload.array("file"), ScheduleController.mediaUpload); + +scheduleRoutes.delete("/schedules/:id/media-upload", isAuth, ScheduleController.deleteMedia); + +export default scheduleRoutes; diff --git a/backend/src/routes/settingRoutes.ts b/backend/src/routes/settingRoutes.ts new file mode 100644 index 0000000..625864a --- /dev/null +++ b/backend/src/routes/settingRoutes.ts @@ -0,0 +1,15 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import * as SettingController from "../controllers/SettingController"; + +const settingRoutes = Router(); + +settingRoutes.get("/settings", isAuth, SettingController.index); + +// routes.get("/settings/:settingKey", isAuth, SettingsController.show); + +// change setting key to key in future +settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update); + +export default settingRoutes; diff --git a/backend/src/routes/subScriptionRoutes.ts b/backend/src/routes/subScriptionRoutes.ts new file mode 100644 index 0000000..8817b6d --- /dev/null +++ b/backend/src/routes/subScriptionRoutes.ts @@ -0,0 +1,11 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as SubscriptionController from "../controllers/SubscriptionController"; + +const subscriptionRoutes = express.Router(); +subscriptionRoutes.post("/subscription", isAuth, SubscriptionController.createSubscription); +subscriptionRoutes.post("/subscription/create/webhook", SubscriptionController.createWebhook); +subscriptionRoutes.post("/subscription/webhook/:type?", SubscriptionController.webhook); + +export default subscriptionRoutes; diff --git a/backend/src/routes/tagRoutes.ts b/backend/src/routes/tagRoutes.ts new file mode 100644 index 0000000..6889ceb --- /dev/null +++ b/backend/src/routes/tagRoutes.ts @@ -0,0 +1,25 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as TagController from "../controllers/TagController"; + +const tagRoutes = express.Router(); + +tagRoutes.get("/tags/list", isAuth, TagController.list); + +tagRoutes.get("/tags", isAuth, TagController.index); + +tagRoutes.get("/tags/kanban", isAuth, TagController.kanban); + +tagRoutes.post("/tags", isAuth, TagController.store); + +tagRoutes.put("/tags/:tagId", isAuth, TagController.update); + +tagRoutes.get("/tags/:tagId", isAuth, TagController.show); + +tagRoutes.delete("/tags/:tagId", isAuth, TagController.remove); + +tagRoutes.post("/tags/sync", isAuth, TagController.syncTags); + + +export default tagRoutes; diff --git a/backend/src/routes/ticketNoteRoutes.ts b/backend/src/routes/ticketNoteRoutes.ts new file mode 100644 index 0000000..88462bb --- /dev/null +++ b/backend/src/routes/ticketNoteRoutes.ts @@ -0,0 +1,28 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as TicketNoteController from "../controllers/TicketNoteController"; + +const ticketNoteRoutes = express.Router(); + +ticketNoteRoutes.get( + "/ticket-notes/list", + isAuth, + TicketNoteController.findFilteredList +); + +ticketNoteRoutes.get("/ticket-notes", isAuth, TicketNoteController.index); + +ticketNoteRoutes.get("/ticket-notes/:id", isAuth, TicketNoteController.show); + +ticketNoteRoutes.post("/ticket-notes", isAuth, TicketNoteController.store); + +ticketNoteRoutes.put("/ticket-notes/:id", isAuth, TicketNoteController.update); + +ticketNoteRoutes.delete( + "/ticket-notes/:id", + isAuth, + TicketNoteController.remove +); + +export default ticketNoteRoutes; diff --git a/backend/src/routes/ticketRoutes.ts b/backend/src/routes/ticketRoutes.ts new file mode 100644 index 0000000..0ee02e7 --- /dev/null +++ b/backend/src/routes/ticketRoutes.ts @@ -0,0 +1,22 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as TicketController from "../controllers/TicketController"; + +const ticketRoutes = express.Router(); + +ticketRoutes.get("/tickets", isAuth, TicketController.index); + +ticketRoutes.get("/tickets/:ticketId", isAuth, TicketController.show); + +ticketRoutes.get("/ticket/kanban", isAuth, TicketController.kanban); + +ticketRoutes.get("/tickets/u/:uuid", isAuth, TicketController.showFromUUID); + +ticketRoutes.post("/tickets", isAuth, TicketController.store); + +ticketRoutes.put("/tickets/:ticketId", isAuth, TicketController.update); + +ticketRoutes.delete("/tickets/:ticketId", isAuth, TicketController.remove); + +export default ticketRoutes; diff --git a/backend/src/routes/ticketTagRoutes.ts b/backend/src/routes/ticketTagRoutes.ts new file mode 100644 index 0000000..759f203 --- /dev/null +++ b/backend/src/routes/ticketTagRoutes.ts @@ -0,0 +1,11 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as TicketTagController from "../controllers/TicketTagController"; + +const ticketTagRoutes = express.Router(); + +ticketTagRoutes.put("/ticket-tags/:ticketId/:tagId", isAuth, TicketTagController.store); +ticketTagRoutes.delete("/ticket-tags/:ticketId", isAuth, TicketTagController.remove); + +export default ticketTagRoutes; diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts new file mode 100644 index 0000000..4fbf2c1 --- /dev/null +++ b/backend/src/routes/userRoutes.ts @@ -0,0 +1,20 @@ +import { Router } from "express"; + +import isAuth from "../middleware/isAuth"; +import * as UserController from "../controllers/UserController"; + +const userRoutes = Router(); + +userRoutes.get("/users", isAuth, UserController.index); + +userRoutes.get("/users/list", isAuth, UserController.list); + +userRoutes.post("/users", isAuth, UserController.store); + +userRoutes.put("/users/:userId", isAuth, UserController.update); + +userRoutes.get("/users/:userId", isAuth, UserController.show); + +userRoutes.delete("/users/:userId", isAuth, UserController.remove); + +export default userRoutes; diff --git a/backend/src/routes/versionRoutes.ts b/backend/src/routes/versionRoutes.ts new file mode 100644 index 0000000..9ff2cbe --- /dev/null +++ b/backend/src/routes/versionRoutes.ts @@ -0,0 +1,9 @@ +import { Router } from "express"; + +import * as VerssionController from "../controllers/VersionController"; + +const versionRouter = Router(); + +versionRouter.get("/version", VerssionController.index); + +export default versionRouter; diff --git a/backend/src/routes/whatsappRoutes.ts b/backend/src/routes/whatsappRoutes.ts new file mode 100644 index 0000000..dc187a7 --- /dev/null +++ b/backend/src/routes/whatsappRoutes.ts @@ -0,0 +1,22 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as WhatsAppController from "../controllers/WhatsAppController"; + +const whatsappRoutes = express.Router(); + +whatsappRoutes.get("/whatsapp/", isAuth, WhatsAppController.index); + +whatsappRoutes.post("/whatsapp/", isAuth, WhatsAppController.store); + +whatsappRoutes.get("/whatsapp/:whatsappId", isAuth, WhatsAppController.show); + +whatsappRoutes.put("/whatsapp/:whatsappId", isAuth, WhatsAppController.update); + +whatsappRoutes.delete( + "/whatsapp/:whatsappId", + isAuth, + WhatsAppController.remove +); + +export default whatsappRoutes; diff --git a/backend/src/routes/whatsappSessionRoutes.ts b/backend/src/routes/whatsappSessionRoutes.ts new file mode 100644 index 0000000..731d847 --- /dev/null +++ b/backend/src/routes/whatsappSessionRoutes.ts @@ -0,0 +1,26 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import WhatsAppSessionController from "../controllers/WhatsAppSessionController"; + +const whatsappSessionRoutes = Router(); + +whatsappSessionRoutes.post( + "/whatsappsession/:whatsappId", + isAuth, + WhatsAppSessionController.store +); + +whatsappSessionRoutes.put( + "/whatsappsession/:whatsappId", + isAuth, + WhatsAppSessionController.update +); + +whatsappSessionRoutes.delete( + "/whatsappsession/:whatsappId", + isAuth, + WhatsAppSessionController.remove +); + +export default whatsappSessionRoutes; diff --git a/backend/src/server.ts b/backend/src/server.ts new file mode 100644 index 0000000..5f386b2 --- /dev/null +++ b/backend/src/server.ts @@ -0,0 +1,40 @@ +import gracefulShutdown from "http-graceful-shutdown"; +import app from "./app"; +import { initIO } from "./libs/socket"; +import { logger } from "./utils/logger"; +import { StartAllWhatsAppsSessions } from "./services/WbotServices/StartAllWhatsAppsSessions"; +import Company from "./models/Company"; +import { startQueueProcess } from "./queues"; +import { TransferTicketQueue } from "./wbotTransferTicketQueue"; +import cron from "node-cron"; + +const server = app.listen(process.env.PORT, async () => { + const companies = await Company.findAll(); + const allPromises: any[] = []; + companies.map(async c => { + const promise = StartAllWhatsAppsSessions(c.id); + allPromises.push(promise); + }); + + Promise.all(allPromises).then(() => { + startQueueProcess(); + }); + logger.info(`Server started on port: ${process.env.PORT}`); +}); + +cron.schedule("* * * * *", async () => { + + try { + // console.log("Running a job at 01:00 at America/Sao_Paulo timezone") + logger.info(`Serviço de transferencia de tickets iniciado`); + + await TransferTicketQueue(); + } + catch (error) { + logger.error(error); + } + +}); + +initIO(server); +gracefulShutdown(server); diff --git a/backend/src/services/AnnouncementService/CreateService.ts b/backend/src/services/AnnouncementService/CreateService.ts new file mode 100644 index 0000000..0a81193 --- /dev/null +++ b/backend/src/services/AnnouncementService/CreateService.ts @@ -0,0 +1,32 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Announcement from "../../models/Announcement"; + +interface Data { + priority: string; + title: string; + text: string; + status: string; + companyId: number; +} + +const CreateService = async (data: Data): Promise => { + const { title, text } = data; + + const ticketnoteSchema = Yup.object().shape({ + title: Yup.string().required("ERR_ANNOUNCEMENT_REQUIRED"), + text: Yup.string().required("ERR_ANNOUNCEMENT_REQUIRED") + }); + + try { + await ticketnoteSchema.validate({ title, text }); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await Announcement.create(data); + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/AnnouncementService/DeleteService.ts b/backend/src/services/AnnouncementService/DeleteService.ts new file mode 100644 index 0000000..c3aee30 --- /dev/null +++ b/backend/src/services/AnnouncementService/DeleteService.ts @@ -0,0 +1,16 @@ +import Announcement from "../../models/Announcement"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await Announcement.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_ANNOUNCEMENT_FOUND", 404); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/AnnouncementService/FindAllService.ts b/backend/src/services/AnnouncementService/FindAllService.ts new file mode 100644 index 0000000..13209f7 --- /dev/null +++ b/backend/src/services/AnnouncementService/FindAllService.ts @@ -0,0 +1,10 @@ +import Announcement from "../../models/Announcement"; + +const FindAllService = async (): Promise => { + const records: Announcement[] = await Announcement.findAll({ + order: [["createdAt", "DESC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/AnnouncementService/FindService.ts b/backend/src/services/AnnouncementService/FindService.ts new file mode 100644 index 0000000..5125672 --- /dev/null +++ b/backend/src/services/AnnouncementService/FindService.ts @@ -0,0 +1,20 @@ +import Announcement from "../../models/Announcement"; +import Company from "../../models/Company"; + +type Params = { + companyId: string; +}; + +const FindService = async ({ companyId }: Params): Promise => { + const notes: Announcement[] = await Announcement.findAll({ + where: { + companyId + }, + include: [{ model: Company, as: "company", attributes: ["id", "name"] }], + order: [["createdAt", "DESC"]] + }); + + return notes; +}; + +export default FindService; diff --git a/backend/src/services/AnnouncementService/ListService.ts b/backend/src/services/AnnouncementService/ListService.ts new file mode 100644 index 0000000..474a761 --- /dev/null +++ b/backend/src/services/AnnouncementService/ListService.ts @@ -0,0 +1,58 @@ +import { Op, fn, col, where } from "sequelize"; +import { isEmpty } from "lodash"; +import Announcement from "../../models/Announcement"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + records: Announcement[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + let whereCondition: any = { + status: true + }; + + if (!isEmpty(searchParam)) { + whereCondition = { + ...whereCondition, + [Op.or]: [ + { + title: where( + fn("LOWER", col("Announcement.title")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + } + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await Announcement.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + records.length; + + return { + records, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/AnnouncementService/ShowService.ts b/backend/src/services/AnnouncementService/ShowService.ts new file mode 100644 index 0000000..d00e28f --- /dev/null +++ b/backend/src/services/AnnouncementService/ShowService.ts @@ -0,0 +1,14 @@ +import Announcement from "../../models/Announcement"; +import AppError from "../../errors/AppError"; + +const ShowService = async (id: string | number): Promise => { + const record = await Announcement.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_ANNOUNCEMENT_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/AnnouncementService/UpdateService.ts b/backend/src/services/AnnouncementService/UpdateService.ts new file mode 100644 index 0000000..7389cee --- /dev/null +++ b/backend/src/services/AnnouncementService/UpdateService.ts @@ -0,0 +1,27 @@ +import AppError from "../../errors/AppError"; +import Announcement from "../../models/Announcement"; + +interface Data { + id: number | string; + priority: string; + title: string; + text: string; + status: string; + companyId: number; +} + +const UpdateService = async (data: Data): Promise => { + const { id } = data; + + const record = await Announcement.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_ANNOUNCEMENT_FOUND", 404); + } + + await record.update(data); + + return record; +}; + +export default UpdateService; diff --git a/backend/src/services/AuthServices/FindUserFromToken.ts b/backend/src/services/AuthServices/FindUserFromToken.ts new file mode 100644 index 0000000..20d57f4 --- /dev/null +++ b/backend/src/services/AuthServices/FindUserFromToken.ts @@ -0,0 +1,18 @@ +import { verify } from "jsonwebtoken"; +import ShowUserService from "../UserServices/ShowUserService"; +import authConfig from "../../config/auth"; +import User from "../../models/User"; + +interface RefreshTokenPayload { + id: string; + tokenVersion: number; + companyId: number; +} + +export default async function FindUserFromToken(token: string): Promise { + const decoded = verify(token, authConfig.refreshSecret); + const { id } = decoded as RefreshTokenPayload; + + const user = await ShowUserService(id); + return user; +} diff --git a/backend/src/services/AuthServices/RefreshTokenService.ts b/backend/src/services/AuthServices/RefreshTokenService.ts new file mode 100644 index 0000000..fb19102 --- /dev/null +++ b/backend/src/services/AuthServices/RefreshTokenService.ts @@ -0,0 +1,48 @@ +import { verify } from "jsonwebtoken"; +import { Response as Res } from "express"; + +import User from "../../models/User"; +import AppError from "../../errors/AppError"; +import ShowUserService from "../UserServices/ShowUserService"; +import authConfig from "../../config/auth"; +import { + createAccessToken, + createRefreshToken +} from "../../helpers/CreateTokens"; + +interface RefreshTokenPayload { + id: string; + tokenVersion: number; + companyId: number; +} + +interface Response { + user: User; + newToken: string; + refreshToken: string; +} + +export const RefreshTokenService = async ( + res: Res, + token: string +): Promise => { + try { + const decoded = verify(token, authConfig.refreshSecret); + const { id, tokenVersion, companyId } = decoded as RefreshTokenPayload; + + const user = await ShowUserService(id); + + if (user.tokenVersion !== tokenVersion) { + res.clearCookie("jrt"); + throw new AppError("ERR_SESSION_EXPIRED", 401); + } + + const newToken = createAccessToken(user); + const refreshToken = createRefreshToken(user); + + return { user, newToken, refreshToken }; + } catch (err) { + res.clearCookie("jrt"); + throw new AppError("ERR_SESSION_EXPIRED", 401); + } +}; diff --git a/backend/src/services/BaileysChatServices/CreateOrUpdateBaileysChatService.ts b/backend/src/services/BaileysChatServices/CreateOrUpdateBaileysChatService.ts new file mode 100644 index 0000000..37e5818 --- /dev/null +++ b/backend/src/services/BaileysChatServices/CreateOrUpdateBaileysChatService.ts @@ -0,0 +1,39 @@ +import { Chat } from "@whiskeysockets/baileys"; +import BaileysChats from "../../models/BaileysChats"; + +export const CreateOrUpdateBaileysChatService = async ( + whatsappId: number, + chat: Partial, +): Promise => { + const { id, conversationTimestamp, unreadCount } = chat; + const baileysChat = await BaileysChats.findOne({ + where: { + whatsappId, + jid: id, + } + }); + + if (baileysChat) { + const baileysChats = await baileysChat.update({ + conversationTimestamp, + unreadCount: unreadCount ? baileysChat.unreadCount + unreadCount : 0 + }); + + return baileysChats; + } + // timestamp now + + const timestamp = new Date().getTime(); + + // convert timestamp to number + const conversationTimestampNumber = Number(timestamp); + + const baileysChats = await BaileysChats.create({ + whatsappId, + jid: id, + conversationTimestamp: conversationTimestamp || conversationTimestampNumber, + unreadCount: unreadCount || 1, + }); + + return baileysChats; +}; diff --git a/backend/src/services/BaileysChatServices/DeleteBaileysChatServices.ts b/backend/src/services/BaileysChatServices/DeleteBaileysChatServices.ts new file mode 100644 index 0000000..57313ff --- /dev/null +++ b/backend/src/services/BaileysChatServices/DeleteBaileysChatServices.ts @@ -0,0 +1,13 @@ +import { ShowBaileysChatService } from "./ShowBaileysChatService"; + +export const DeleteBaileysChatServices = async ( + whatsappId: number, + jid: string, +): Promise => { + const showBaileysChatService = await ShowBaileysChatService( + whatsappId, + jid, + ); + + showBaileysChatService.destroy(); +}; diff --git a/backend/src/services/BaileysChatServices/ShowBaileysChatService.ts b/backend/src/services/BaileysChatServices/ShowBaileysChatService.ts new file mode 100644 index 0000000..a93ca2d --- /dev/null +++ b/backend/src/services/BaileysChatServices/ShowBaileysChatService.ts @@ -0,0 +1,19 @@ +import AppError from "../../errors/AppError"; +import BaileysChats from "../../models/BaileysChats"; + +export const ShowBaileysChatService = async ( + whatsappId: number, + jid: string, +): Promise => { + const baileysChat = await BaileysChats.findOne({ + where: { + whatsappId, + jid, + } + }); + + if (baileysChat) { + return baileysChat; + } + +}; diff --git a/backend/src/services/BaileysChatServices/UpdateBaileysChatServices.ts b/backend/src/services/BaileysChatServices/UpdateBaileysChatServices.ts new file mode 100644 index 0000000..dd4a304 --- /dev/null +++ b/backend/src/services/BaileysChatServices/UpdateBaileysChatServices.ts @@ -0,0 +1,30 @@ +import AppError from "../../errors/AppError"; +import BaileysChats from "../../models/BaileysChats"; + +interface Data { + id?: string; + conversationTimestamp?: number; + unreadCount?: number; +} + +export const UpdateBaileysChatServices = async ( + whatsappId: number, + jid: string, + data: Data +): Promise => { + const baileysChat = await BaileysChats.findOne({ + where: { + whatsappId, + jid + } + }); + + if (baileysChat) { + await baileysChat.update({ + conversationTimestamp: data.conversationTimestamp, + unreadCount: data.unreadCount + }); + + return baileysChat; + } +}; diff --git a/backend/src/services/BaileysServices/CreateOrUpdateBaileysService.ts b/backend/src/services/BaileysServices/CreateOrUpdateBaileysService.ts new file mode 100644 index 0000000..5a91d45 --- /dev/null +++ b/backend/src/services/BaileysServices/CreateOrUpdateBaileysService.ts @@ -0,0 +1,57 @@ +import { Chat, Contact } from "@whiskeysockets/baileys"; +import Baileys from "../../models/Baileys"; +import { isArray } from "lodash"; + +interface Request { + whatsappId: number; + contacts?: Contact[]; + chats?: Chat[]; +} + +const createOrUpdateBaileysService = async ({ + whatsappId, + contacts, + chats +}: Request): Promise => { + const baileysExists = await Baileys.findOne({ + where: { whatsappId } + }); + + if (baileysExists) { + const getChats = baileysExists.chats + ? JSON.parse(JSON.stringify(baileysExists.chats)) + : []; + const getContacts = baileysExists.contacts + ? JSON.parse(JSON.stringify(baileysExists.contacts)) + : []; + + if (chats && isArray(getChats)) { + getChats.push(...chats); + getChats.sort(); + getChats.filter((v, i, a) => a.indexOf(v) === i); + } + + if (contacts && isArray(getContacts)) { + getContacts.push(...contacts); + getContacts.sort(); + getContacts.filter((v, i, a) => a.indexOf(v) === i); + } + + const newBaileys = await baileysExists.update({ + chats: JSON.stringify(getChats), + contacts: JSON.stringify(getContacts) + }); + + return newBaileys; + } + + const baileys = await Baileys.create({ + whatsappId, + contacts: JSON.stringify(contacts), + chats: JSON.stringify(chats) + }); + + return baileys; +}; + +export default createOrUpdateBaileysService; diff --git a/backend/src/services/BaileysServices/DeleteBaileysService.ts b/backend/src/services/BaileysServices/DeleteBaileysService.ts new file mode 100644 index 0000000..12396ba --- /dev/null +++ b/backend/src/services/BaileysServices/DeleteBaileysService.ts @@ -0,0 +1,15 @@ +import Baileys from "../../models/Baileys"; + +const DeleteBaileysService = async (id: string | number): Promise => { + const baileysData = await Baileys.findOne({ + where: { + whatsappId: id + } + }); + + if (baileysData) { + await baileysData.destroy(); + } +}; + +export default DeleteBaileysService; diff --git a/backend/src/services/BaileysServices/ShowBaileysService.ts b/backend/src/services/BaileysServices/ShowBaileysService.ts new file mode 100644 index 0000000..5c3d5fb --- /dev/null +++ b/backend/src/services/BaileysServices/ShowBaileysService.ts @@ -0,0 +1,18 @@ +import Baileys from "../../models/Baileys"; +import AppError from "../../errors/AppError"; + +const ShowBaileysService = async (id: string | number): Promise => { + const baileysData = await Baileys.findOne({ + where: { + whatsappId: id + } + }); + + if (!baileysData) { + throw new AppError("ERR_NO_BAILEYS_DATA_FOUND", 404); + } + + return baileysData; +}; + +export default ShowBaileysService; diff --git a/backend/src/services/CampaignService/CancelService.ts b/backend/src/services/CampaignService/CancelService.ts new file mode 100644 index 0000000..2c83668 --- /dev/null +++ b/backend/src/services/CampaignService/CancelService.ts @@ -0,0 +1,26 @@ +import { Op } from "sequelize"; +import Campaign from "../../models/Campaign"; +import CampaignShipping from "../../models/CampaignShipping"; +import { campaignQueue } from "../../queues"; + +export async function CancelService(id: number) { + const campaign = await Campaign.findByPk(id); + await campaign.update({ status: "CANCELADA" }); + + const recordsToCancel = await CampaignShipping.findAll({ + where: { + campaignId: campaign.id, + jobId: { [Op.not]: null }, + deliveredAt: null + } + }); + + const promises = []; + + for (let record of recordsToCancel) { + const job = await campaignQueue.getJob(+record.jobId); + promises.push(job.remove()); + } + + await Promise.all(promises); +} diff --git a/backend/src/services/CampaignService/CreateService.ts b/backend/src/services/CampaignService/CreateService.ts new file mode 100644 index 0000000..a50db6d --- /dev/null +++ b/backend/src/services/CampaignService/CreateService.ts @@ -0,0 +1,58 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Campaign from "../../models/Campaign"; +import ContactList from "../../models/ContactList"; +import Whatsapp from "../../models/Whatsapp"; + +interface Data { + name: string; + status: string; + confirmation: boolean; + scheduledAt: string; + companyId: number; + contactListId: number; + message1?: string; + message2?: string; + message3?: string; + message4?: string; + message5?: string; + confirmationMessage1?: string; + confirmationMessage2?: string; + confirmationMessage3?: string; + confirmationMessage4?: string; + confirmationMessage5?: string; + fileListId: number; +} + +const CreateService = async (data: Data): Promise => { + const { name } = data; + + const ticketnoteSchema = Yup.object().shape({ + name: Yup.string() + .min(3, "ERR_CAMPAIGN_INVALID_NAME") + .required("ERR_CAMPAIGN_REQUIRED") + }); + + try { + await ticketnoteSchema.validate({ name }); + } catch (err: any) { + throw new AppError(err.message); + } + + if (data.scheduledAt != null && data.scheduledAt != "") { + data.status = "PROGRAMADA"; + } + + const record = await Campaign.create(data); + + await record.reload({ + include: [ + { model: ContactList }, + { model: Whatsapp, attributes: ["id", "name"] } + ] + }); + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/CampaignService/DeleteService.ts b/backend/src/services/CampaignService/DeleteService.ts new file mode 100644 index 0000000..4038555 --- /dev/null +++ b/backend/src/services/CampaignService/DeleteService.ts @@ -0,0 +1,20 @@ +import Campaign from "../../models/Campaign"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await Campaign.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_CAMPAIGN_FOUND", 404); + } + + if (record.status === "EM_ANDAMENTO") { + throw new AppError("Não é permitido excluir campanha em andamento", 400); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/CampaignService/FindAllService.ts b/backend/src/services/CampaignService/FindAllService.ts new file mode 100644 index 0000000..cff8b37 --- /dev/null +++ b/backend/src/services/CampaignService/FindAllService.ts @@ -0,0 +1,10 @@ +import Campaign from "../../models/Campaign"; + +const FindAllService = async (): Promise => { + const records: Campaign[] = await Campaign.findAll({ + order: [["name", "ASC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/CampaignService/FindService.ts b/backend/src/services/CampaignService/FindService.ts new file mode 100644 index 0000000..128e4c1 --- /dev/null +++ b/backend/src/services/CampaignService/FindService.ts @@ -0,0 +1,20 @@ +import Campaign from "../../models/Campaign"; +import Company from "../../models/Company"; + +type Params = { + companyId: string; +}; + +const FindService = async ({ companyId }: Params): Promise => { + const notes: Campaign[] = await Campaign.findAll({ + where: { + companyId + }, + include: [{ model: Company, as: "company", attributes: ["id", "name"] }], + order: [["name", "ASC"]] + }); + + return notes; +}; + +export default FindService; diff --git a/backend/src/services/CampaignService/ListService.ts b/backend/src/services/CampaignService/ListService.ts new file mode 100644 index 0000000..234504e --- /dev/null +++ b/backend/src/services/CampaignService/ListService.ts @@ -0,0 +1,66 @@ +import { Op, fn, col, where } from "sequelize"; +import Campaign from "../../models/Campaign"; +import { isEmpty } from "lodash"; +import ContactList from "../../models/ContactList"; +import Whatsapp from "../../models/Whatsapp"; + +interface Request { + companyId: number | string; + searchParam?: string; + pageNumber?: string; +} + +interface Response { + records: Campaign[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam = "", + pageNumber = "1", + companyId +}: Request): Promise => { + let whereCondition: any = { + companyId + }; + + if (!isEmpty(searchParam)) { + whereCondition = { + ...whereCondition, + [Op.or]: [ + { + name: where( + fn("LOWER", col("Campaign.name")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + } + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await Campaign.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["name", "ASC"]], + include: [ + { model: ContactList }, + { model: Whatsapp, attributes: ["id", "name"] } + ] + }); + + const hasMore = count > offset + records.length; + + return { + records, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/CampaignService/RestartService.ts b/backend/src/services/CampaignService/RestartService.ts new file mode 100644 index 0000000..3ec214d --- /dev/null +++ b/backend/src/services/CampaignService/RestartService.ts @@ -0,0 +1,12 @@ +import Campaign from "../../models/Campaign"; +import { campaignQueue } from "../../queues"; + +export async function RestartService(id: number) { + const campaign = await Campaign.findByPk(id); + await campaign.update({ status: "EM_ANDAMENTO" }); + + await campaignQueue.add("ProcessCampaign", { + id: campaign.id, + delay: 3000 + }); +} diff --git a/backend/src/services/CampaignService/ShowService.ts b/backend/src/services/CampaignService/ShowService.ts new file mode 100644 index 0000000..16974a5 --- /dev/null +++ b/backend/src/services/CampaignService/ShowService.ts @@ -0,0 +1,24 @@ +import Campaign from "../../models/Campaign"; +import AppError from "../../errors/AppError"; +import CampaignShipping from "../../models/CampaignShipping"; +import ContactList from "../../models/ContactList"; +import ContactListItem from "../../models/ContactListItem"; +import Whatsapp from "../../models/Whatsapp"; + +const ShowService = async (id: string | number): Promise => { + const record = await Campaign.findByPk(id, { + include: [ + { model: CampaignShipping }, + { model: ContactList, include: [{ model: ContactListItem }] }, + { model: Whatsapp, attributes: ["id", "name"] } + ] + }); + + if (!record) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/CampaignService/UpdateService.ts b/backend/src/services/CampaignService/UpdateService.ts new file mode 100644 index 0000000..fa33ed4 --- /dev/null +++ b/backend/src/services/CampaignService/UpdateService.ts @@ -0,0 +1,63 @@ +import AppError from "../../errors/AppError"; +import Campaign from "../../models/Campaign"; +import ContactList from "../../models/ContactList"; +import Whatsapp from "../../models/Whatsapp"; + +interface Data { + id: number | string; + name: string; + status: string; + confirmation: boolean; + scheduledAt: string; + companyId: number; + contactListId: number; + message1?: string; + message2?: string; + message3?: string; + message4?: string; + message5?: string; + confirmationMessage1?: string; + confirmationMessage2?: string; + confirmationMessage3?: string; + confirmationMessage4?: string; + confirmationMessage5?: string; + fileListId: number; +} + +const UpdateService = async (data: Data): Promise => { + const { id } = data; + + const record = await Campaign.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_CAMPAIGN_FOUND", 404); + } + + if (["INATIVA", "PROGRAMADA", "CANCELADA"].indexOf(data.status) === -1) { + throw new AppError( + "Só é permitido alterar campanha Inativa e Programada", + 400 + ); + } + + if ( + data.scheduledAt != null && + data.scheduledAt != "" && + data.status === "INATIVA" + ) { + data.status = "PROGRAMADA"; + } + + await record.update(data); + + await record.reload({ + include: [ + { model: ContactList }, + { model: Whatsapp, attributes: ["id", "name"] } + ] + }); + + return record; +}; + +export default UpdateService; diff --git a/backend/src/services/CampaignSettingServices/CreateService.ts b/backend/src/services/CampaignSettingServices/CreateService.ts new file mode 100644 index 0000000..7afeee4 --- /dev/null +++ b/backend/src/services/CampaignSettingServices/CreateService.ts @@ -0,0 +1,36 @@ +import CampaignSetting from "../../models/CampaignSetting"; +import { isArray, isObject } from "lodash"; + +interface Data { + settings: any; +} + +const CreateService = async ( + data: Data, + companyId: number +): Promise => { + const settings = []; + for (let settingKey of Object.keys(data.settings)) { + const value = + isArray(data.settings[settingKey]) || isObject(data.settings[settingKey]) + ? JSON.stringify(data.settings[settingKey]) + : data.settings[settingKey]; + const [record, created] = await CampaignSetting.findOrCreate({ + where: { + key: settingKey, + companyId + }, + defaults: { key: settingKey, value, companyId } + }); + + if (!created) { + await record.update({ value }); + } + + settings.push(record); + } + + return settings; +}; + +export default CreateService; diff --git a/backend/src/services/CampaignSettingServices/ListService.ts b/backend/src/services/CampaignSettingServices/ListService.ts new file mode 100644 index 0000000..009df3a --- /dev/null +++ b/backend/src/services/CampaignSettingServices/ListService.ts @@ -0,0 +1,34 @@ +import { Op, fn, col, where } from "sequelize"; +import Campaign from "../../models/Campaign"; +import { isEmpty } from "lodash"; +import ContactList from "../../models/ContactList"; +import Whatsapp from "../../models/Whatsapp"; +import CampaignSetting from "../../models/CampaignSetting"; + +interface Request { + companyId: number | string; + searchParam?: string; + pageNumber?: string; +} + +interface Response { + records: Campaign[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + companyId +}: Request): Promise => { + let whereCondition: any = { + companyId + }; + + const records = await CampaignSetting.findAll({ + where: whereCondition + }); + + return records; +}; + +export default ListService; diff --git a/backend/src/services/ChatService/CreateMessageService.ts b/backend/src/services/ChatService/CreateMessageService.ts new file mode 100644 index 0000000..946e615 --- /dev/null +++ b/backend/src/services/ChatService/CreateMessageService.ts @@ -0,0 +1,52 @@ +import { Op } from "sequelize"; +import Chat from "../../models/Chat"; +import ChatMessage from "../../models/ChatMessage"; +import ChatUser from "../../models/ChatUser"; +import User from "../../models/User"; + +export interface ChatMessageData { + senderId: number; + chatId: number; + message: string; +} + +export default async function CreateMessageService({ + senderId, + chatId, + message +}: ChatMessageData) { + const newMessage = await ChatMessage.create({ + senderId, + chatId, + message + }); + + await newMessage.reload({ + include: [ + { model: User, as: "sender", attributes: ["id", "name"] }, + { + model: Chat, + as: "chat", + include: [{ model: ChatUser, as: "users" }] + } + ] + }); + + const sender = await User.findByPk(senderId); + + await newMessage.chat.update({ lastMessage: `${sender.name}: ${message}` }); + + const chatUsers = await ChatUser.findAll({ + where: { chatId } + }); + + for (let chatUser of chatUsers) { + if (chatUser.userId === senderId) { + await chatUser.update({ unreads: 0 }); + } else { + await chatUser.update({ unreads: chatUser.unreads + 1 }); + } + } + + return newMessage; +} diff --git a/backend/src/services/ChatService/CreateService.ts b/backend/src/services/ChatService/CreateService.ts new file mode 100644 index 0000000..6b1ff9e --- /dev/null +++ b/backend/src/services/ChatService/CreateService.ts @@ -0,0 +1,38 @@ +import Chat from "../../models/Chat"; +import ChatUser from "../../models/ChatUser"; +import User from "../../models/User"; + +interface Data { + ownerId: number; + companyId: number; + users: any[]; + title: string; +} + +const CreateService = async (data: Data): Promise => { + const { ownerId, companyId, users, title } = data; + + const record = await Chat.create({ + ownerId, + companyId, + title + }); + + if (Array.isArray(users) && users.length > 0) { + await ChatUser.create({ chatId: record.id, userId: ownerId }); + for (let user of users) { + await ChatUser.create({ chatId: record.id, userId: user.id }); + } + } + + await record.reload({ + include: [ + { model: ChatUser, as: "users", include: [{ model: User, as: "user" }] }, + { model: User, as: "owner" } + ] + }); + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/ChatService/DeleteService.ts b/backend/src/services/ChatService/DeleteService.ts new file mode 100644 index 0000000..e560f24 --- /dev/null +++ b/backend/src/services/ChatService/DeleteService.ts @@ -0,0 +1,16 @@ +import Chat from "../../models/Chat"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await Chat.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_CHAT_FOUND", 404); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/ChatService/FindAllService.ts b/backend/src/services/ChatService/FindAllService.ts new file mode 100644 index 0000000..afbc09a --- /dev/null +++ b/backend/src/services/ChatService/FindAllService.ts @@ -0,0 +1,10 @@ +import Chat from "../../models/Chat"; + +const FindAllService = async (): Promise => { + const records: Chat[] = await Chat.findAll({ + order: [["createdAt", "DESC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/ChatService/FindMessages.ts b/backend/src/services/ChatService/FindMessages.ts new file mode 100644 index 0000000..f2dd5f4 --- /dev/null +++ b/backend/src/services/ChatService/FindMessages.ts @@ -0,0 +1,58 @@ +import AppError from "../../errors/AppError"; +import ChatMessage from "../../models/ChatMessage"; +import ChatUser from "../../models/ChatUser"; +import User from "../../models/User"; + +import { sortBy } from "lodash"; + +interface Request { + chatId: string; + ownerId: number; + pageNumber?: string; +} + +interface Response { + records: ChatMessage[]; + count: number; + hasMore: boolean; +} + +const FindMessages = async ({ + chatId, + ownerId, + pageNumber = "1" +}: Request): Promise => { + const userInChat = await ChatUser.count({ + where: { chatId, userId: ownerId } + }); + + if (userInChat === 0) { + throw new AppError("UNAUTHORIZED", 400); + } + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await ChatMessage.findAndCountAll({ + where: { + chatId + }, + include: [{ model: User, as: "sender", attributes: ["id", "name"] }], + limit, + offset, + + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + records.length; + + const sorted = sortBy(records, ["id", "ASC"]); + + return { + records: sorted, + count, + hasMore + }; +}; + +export default FindMessages; diff --git a/backend/src/services/ChatService/FindService.ts b/backend/src/services/ChatService/FindService.ts new file mode 100644 index 0000000..c849990 --- /dev/null +++ b/backend/src/services/ChatService/FindService.ts @@ -0,0 +1,26 @@ +import Chat from "../../models/Chat"; +import Company from "../../models/Company"; +import User from "../../models/User"; + +type Params = { + companyId: number; + ownerId?: number; +}; + +const FindService = async ({ ownerId, companyId }: Params): Promise => { + const chats: Chat[] = await Chat.findAll({ + where: { + ownerId, + companyId + }, + include: [ + { model: Company, as: "company", attributes: ["id", "name"] }, + { model: User, as: "owner", attributes: ["id", "name"] } + ], + order: [["createdAt", "DESC"]] + }); + + return chats; +}; + +export default FindService; diff --git a/backend/src/services/ChatService/ListService.ts b/backend/src/services/ChatService/ListService.ts new file mode 100644 index 0000000..38246d0 --- /dev/null +++ b/backend/src/services/ChatService/ListService.ts @@ -0,0 +1,54 @@ +import { Op } from "sequelize"; +import Chat from "../../models/Chat"; +import ChatUser from "../../models/ChatUser"; +import User from "../../models/User"; + +interface Request { + ownerId: number; + pageNumber?: string; +} + +interface Response { + records: Chat[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + ownerId, + pageNumber = "1" +}: Request): Promise => { + const chatUsers = await ChatUser.findAll({ + where: { userId: ownerId } + }); + + const chatIds = chatUsers.map(chat => chat.chatId); + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await Chat.findAndCountAll({ + where: { + id: { + [Op.in]: chatIds + } + }, + include: [ + { model: User, as: "owner" }, + { model: ChatUser, as: "users", include: [{ model: User, as: "user" }] } + ], + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + records.length; + + return { + records, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/ChatService/ShowFromUuidService.ts b/backend/src/services/ChatService/ShowFromUuidService.ts new file mode 100644 index 0000000..dd5f950 --- /dev/null +++ b/backend/src/services/ChatService/ShowFromUuidService.ts @@ -0,0 +1,14 @@ +import Chat from "../../models/Chat"; +import AppError from "../../errors/AppError"; + +const ShowFromUuidService = async (uuid: string): Promise => { + const record = await Chat.findOne({ where: { uuid } }); + + if (!record) { + throw new AppError("ERR_NO_CHAT_FOUND", 404); + } + + return record; +}; + +export default ShowFromUuidService; diff --git a/backend/src/services/ChatService/ShowService.ts b/backend/src/services/ChatService/ShowService.ts new file mode 100644 index 0000000..55be2d2 --- /dev/null +++ b/backend/src/services/ChatService/ShowService.ts @@ -0,0 +1,14 @@ +import Chat from "../../models/Chat"; +import AppError from "../../errors/AppError"; + +const ShowService = async (id: string | number): Promise => { + const record = await Chat.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_CHAT_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/ChatService/UpdateService.ts b/backend/src/services/ChatService/UpdateService.ts new file mode 100644 index 0000000..8ebdad8 --- /dev/null +++ b/backend/src/services/ChatService/UpdateService.ts @@ -0,0 +1,38 @@ +import Chat from "../../models/Chat"; +import ChatUser from "../../models/ChatUser"; +import User from "../../models/User"; + +interface ChatData { + id: number; + title?: string; + users?: any[]; +} + +export default async function UpdateService(data: ChatData) { + const { users } = data; + const record = await Chat.findByPk(data.id, { + include: [{ model: ChatUser, as: "users" }] + }); + const { ownerId } = record; + + await record.update({ title: data.title }); + + if (Array.isArray(users)) { + await ChatUser.destroy({ where: { chatId: record.id } }); + await ChatUser.create({ chatId: record.id, userId: ownerId }); + for (let user of users) { + if (user.id !== ownerId) { + await ChatUser.create({ chatId: record.id, userId: user.id }); + } + } + } + + await record.reload({ + include: [ + { model: ChatUser, as: "users", include: [{ model: User, as: "user" }] }, + { model: User, as: "owner" } + ] + }); + + return record; +} diff --git a/backend/src/services/CompanyService/CreateCompanyService.ts b/backend/src/services/CompanyService/CreateCompanyService.ts new file mode 100644 index 0000000..c50ff50 --- /dev/null +++ b/backend/src/services/CompanyService/CreateCompanyService.ts @@ -0,0 +1,305 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Company from "../../models/Company"; +import User from "../../models/User"; +import Setting from "../../models/Setting"; + +interface CompanyData { + name: string; + phone?: string; + email?: string; + password?: string; + status?: boolean; + planId?: number; + campaignsEnabled?: boolean; + dueDate?: string; + recurrence?: string; +} + +const CreateCompanyService = async ( + companyData: CompanyData +): Promise => { + const { + name, + phone, + email, + status, + planId, + password, + campaignsEnabled, + dueDate, + recurrence + } = companyData; + + const companySchema = Yup.object().shape({ + name: Yup.string() + .min(2, "ERR_COMPANY_INVALID_NAME") + .required("ERR_COMPANY_INVALID_NAME") + .test( + "Check-unique-name", + "ERR_COMPANY_NAME_ALREADY_EXISTS", + async value => { + if (value) { + const companyWithSameName = await Company.findOne({ + where: { name: value } + }); + + return !companyWithSameName; + } + return false; + } + ) + }); + + try { + await companySchema.validate({ name }); + } catch (err: any) { + throw new AppError(err.message); + } + + const company = await Company.create({ + name, + phone, + email, + status, + planId, + dueDate, + recurrence + }); + + const user = await User.create({ + name: company.name, + email: company.email, + password: companyData.password, + profile: "admin", + companyId: company.id + }); + + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "asaas" + }, + defaults: { + companyId: company.id, + key: "asaas", + value: "" + }, + }); + + //tokenixc + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "tokenixc" + }, + defaults: { + companyId: company.id, + key: "tokenixc", + value: "" + }, + }); + + //ipixc + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "ipixc" + }, + defaults: { + companyId: company.id, + key: "ipixc", + value: "" + }, + }); + + //ipmkauth + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "ipmkauth" + }, + defaults: { + companyId: company.id, + key: "ipmkauth", + value: "" + }, + }); + + //clientsecretmkauth + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "clientsecretmkauth" + }, + defaults: { + companyId: company.id, + key: "clientsecretmkauth", + value: "" + }, + }); + + //clientidmkauth + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "clientidmkauth" + }, + defaults: { + companyId: company.id, + key: "clientidmkauth", + value: "" + }, + }); + + //CheckMsgIsGroup + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "CheckMsgIsGroup" + }, + defaults: { + companyId: company.id, + key: "enabled", + value: "" + }, + }); + + //CheckMsgIsGroup + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "" + }, + defaults: { + companyId: company.id, + key: "call", + value: "disabled" + }, + }); + + //scheduleType + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "scheduleType" + }, + defaults: { + companyId: company.id, + key: "scheduleType", + value: "disabled" + }, + }); + + + // Enviar mensagem ao aceitar ticket + await Setting.findOrCreate({ + where:{ + companyId: company.id, + key: "sendGreetingAccepted", + }, + defaults: { + companyId: company.id, + key: "sendGreetingAccepted", + value: "disabled" + }, + }); + + // Enviar mensagem de transferencia + await Setting.findOrCreate({ + where:{ + companyId: company.id, + key: "sendMsgTransfTicket", + }, + defaults: { + companyId: company.id, + key: "sendMsgTransfTicket", + value: "disabled" + }, + }); + + //userRating + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "userRating" + }, + defaults: { + companyId: company.id, + key: "userRating", + value: "disabled" + }, + }); + + //userRating + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "chatBotType" + }, + defaults: { + companyId: company.id, + key: "chatBotType", + value: "text" + }, + + }); + + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "tokensgp" + }, + defaults: { + companyId: company.id, + key: "tokensgp", + value: "" + }, + }); + + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "ipsgp" + }, + defaults: { + companyId: company.id, + key: "ipsgp", + value: "" + }, + }); + + await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "appsgp" + }, + defaults: { + companyId: company.id, + key: "appsgp", + value: "" + }, + }); + + if (companyData.campaignsEnabled !== undefined) { + const [setting, created] = await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "campaignsEnabled" + }, + defaults: { + companyId: company.id, + key: "campaignsEnabled", + value: `${campaignsEnabled}` + }, + + }); + if (!created) { + await setting.update({ value: `${campaignsEnabled}` }); + } + } + + return company; +}; + +export default CreateCompanyService; \ No newline at end of file diff --git a/backend/src/services/CompanyService/DeleteCompanyService.ts b/backend/src/services/CompanyService/DeleteCompanyService.ts new file mode 100644 index 0000000..c4c9651 --- /dev/null +++ b/backend/src/services/CompanyService/DeleteCompanyService.ts @@ -0,0 +1,16 @@ +import Company from "../../models/Company"; +import AppError from "../../errors/AppError"; + +const DeleteCompanyService = async (id: string): Promise => { + const company = await Company.findOne({ + where: { id } + }); + + if (!company) { + throw new AppError("ERR_NO_COMPANY_FOUND", 404); + } + + await company.destroy(); +}; + +export default DeleteCompanyService; diff --git a/backend/src/services/CompanyService/FindAllCompaniesService.ts b/backend/src/services/CompanyService/FindAllCompaniesService.ts new file mode 100644 index 0000000..e593345 --- /dev/null +++ b/backend/src/services/CompanyService/FindAllCompaniesService.ts @@ -0,0 +1,16 @@ +import Company from "../../models/Company"; +import Plan from "../../models/Plan"; +import Setting from "../../models/Setting"; + +const FindAllCompanyService = async (): Promise => { + const companies = await Company.findAll({ + order: [["name", "ASC"]], + include: [ + { model: Plan, as: "plan", attributes: ["id", "name", "value"] }, + { model: Setting, as: "settings" } + ] + }); + return companies; +}; + +export default FindAllCompanyService; diff --git a/backend/src/services/CompanyService/ListCompaniesPlanService.ts b/backend/src/services/CompanyService/ListCompaniesPlanService.ts new file mode 100644 index 0000000..cf76d87 --- /dev/null +++ b/backend/src/services/CompanyService/ListCompaniesPlanService.ts @@ -0,0 +1,32 @@ +import Company from "../../models/Company"; +import Plan from "../../models/Plan"; + +const ListCompaniesPlanService = async (): Promise => { + const companies = await Company.findAll({ + attributes: ["id", "name", "email", "status", "dueDate", "createdAt", "phone"], + order: [["name", "ASC"]], + include: [ + { + model: Plan, as: "plan", + attributes: [ + "id", + "name", + "users", + "connections", + "queues", + "value", + "useCampaigns", + "useSchedules", + "useInternalChat", + "useExternalApi", + "useKanban", + "useOpenAi", + "useIntegrations" + ] + }, + ] + }); + return companies; +}; + +export default ListCompaniesPlanService; diff --git a/backend/src/services/CompanyService/ListCompaniesService.ts b/backend/src/services/CompanyService/ListCompaniesService.ts new file mode 100644 index 0000000..69b813f --- /dev/null +++ b/backend/src/services/CompanyService/ListCompaniesService.ts @@ -0,0 +1,50 @@ +import { Sequelize, Op } from "sequelize"; +import Company from "../../models/Company"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + companies: Company[]; + count: number; + hasMore: boolean; +} + +const ListCompaniesService = async ({ + searchParam = "", + pageNumber = "1", +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + + const { count, rows: companies } = await Company.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["name", "ASC"]] + }); + + const hasMore = count > offset + companies.length; + + return { + companies, + count, + hasMore, + }; +}; + +export default ListCompaniesService; diff --git a/backend/src/services/CompanyService/ShowCompanyService.ts b/backend/src/services/CompanyService/ShowCompanyService.ts new file mode 100644 index 0000000..bede133 --- /dev/null +++ b/backend/src/services/CompanyService/ShowCompanyService.ts @@ -0,0 +1,14 @@ +import Company from "../../models/Company"; +import AppError from "../../errors/AppError"; + +const ShowCompanyService = async (id: string | number): Promise => { + const company = await Company.findByPk(id); + + if (!company) { + throw new AppError("ERR_NO_COMPANY_FOUND", 404); + } + + return company; +}; + +export default ShowCompanyService; diff --git a/backend/src/services/CompanyService/ShowPlanCompanyService.ts b/backend/src/services/CompanyService/ShowPlanCompanyService.ts new file mode 100644 index 0000000..6b01aa1 --- /dev/null +++ b/backend/src/services/CompanyService/ShowPlanCompanyService.ts @@ -0,0 +1,34 @@ +import Company from "../../models/Company"; +import Plan from "../../models/Plan"; + +const ShowPlanCompanyService = async (id: string | number): Promise => { + const companies = await Company.findOne({ + where: { id }, + attributes: ["id", "name", "email", "status", "dueDate", "createdAt", "phone"], + order: [["name", "ASC"]], + include: [ + { + model: Plan, as: "plan", + attributes: [ + "id", + "name", + "users", + "connections", + "queues", + "value", + "useCampaigns", + "useSchedules", + "useInternalChat", + "useExternalApi", + "useKanban", + "useOpenAi", + "useIntegrations" + ] + }, + ] + }); + + return companies; +}; + +export default ShowPlanCompanyService; diff --git a/backend/src/services/CompanyService/UpdateCompanyService.ts b/backend/src/services/CompanyService/UpdateCompanyService.ts new file mode 100644 index 0000000..92351d8 --- /dev/null +++ b/backend/src/services/CompanyService/UpdateCompanyService.ts @@ -0,0 +1,66 @@ +import AppError from "../../errors/AppError"; +import Company from "../../models/Company"; +import Setting from "../../models/Setting"; + +interface CompanyData { + name: string; + id?: number | string; + phone?: string; + email?: string; + status?: boolean; + planId?: number; + campaignsEnabled?: boolean; + dueDate?: string; + recurrence?: string; +} + +const UpdateCompanyService = async ( + companyData: CompanyData +): Promise => { + const company = await Company.findByPk(companyData.id); + const { + name, + phone, + email, + status, + planId, + campaignsEnabled, + dueDate, + recurrence + } = companyData; + + if (!company) { + throw new AppError("ERR_NO_COMPANY_FOUND", 404); + } + + await company.update({ + name, + phone, + email, + status, + planId, + dueDate, + recurrence + }); + + if (companyData.campaignsEnabled !== undefined) { + const [setting, created] = await Setting.findOrCreate({ + where: { + companyId: company.id, + key: "campaignsEnabled" + }, + defaults: { + companyId: company.id, + key: "campaignsEnabled", + value: `${campaignsEnabled}` + } + }); + if (!created) { + await setting.update({ value: `${campaignsEnabled}` }); + } + } + + return company; +}; + +export default UpdateCompanyService; diff --git a/backend/src/services/CompanyService/UpdateSchedulesService.ts b/backend/src/services/CompanyService/UpdateSchedulesService.ts new file mode 100644 index 0000000..afaef18 --- /dev/null +++ b/backend/src/services/CompanyService/UpdateSchedulesService.ts @@ -0,0 +1,26 @@ +import AppError from "../../errors/AppError"; +import Company from "../../models/Company"; + +type ScheduleData = { + id: number | string; + schedules: []; +}; + +const UpdateSchedulesService = async ({ + id, + schedules +}: ScheduleData): Promise => { + const company = await Company.findByPk(id); + + if (!company) { + throw new AppError("ERR_NO_COMPANY_FOUND", 404); + } + + await company.update({ + schedules + }); + + return company; +}; + +export default UpdateSchedulesService; diff --git a/backend/src/services/CompanyService/VerifyCurrentSchedule.ts b/backend/src/services/CompanyService/VerifyCurrentSchedule.ts new file mode 100644 index 0000000..4b0bd6f --- /dev/null +++ b/backend/src/services/CompanyService/VerifyCurrentSchedule.ts @@ -0,0 +1,45 @@ +import { QueryTypes } from "sequelize"; +import sequelize from "../../database"; + +type Result = { + id: number; + currentSchedule: []; + startTime: string; + endTime: string; + inActivity: boolean; +}; + +const VerifyCurrentSchedule = async (id: string | number): Promise => { + const sql = ` + select + s.id, + s.currentWeekday, + s.currentSchedule, + (s.currentSchedule->>'startTime')::time "startTime", + (s.currentSchedule->>'endTime')::time "endTime", + ( + now()::time >= (s.currentSchedule->>'startTime')::time and + now()::time <= (s.currentSchedule->>'endTime')::time + ) "inActivity" + from ( + SELECT + c.id, + to_char(current_date, 'day') currentWeekday, + (array_to_json(array_agg(s))->>0)::jsonb currentSchedule + FROM "Companies" c, jsonb_array_elements(c.schedules) s + WHERE s->>'weekdayEn' like trim(to_char(current_date, 'day')) and c.id = :id + GROUP BY 1, 2 + ) s + where s.currentSchedule->>'startTime' not like '' and s.currentSchedule->>'endTime' not like ''; + `; + + const result: Result = await sequelize.query(sql, { + replacements: { id }, + type: QueryTypes.SELECT, + plain: true + }); + + return result; +}; + +export default VerifyCurrentSchedule; diff --git a/backend/src/services/ContactListItemService/CreateService.ts b/backend/src/services/ContactListItemService/CreateService.ts new file mode 100644 index 0000000..8b69715 --- /dev/null +++ b/backend/src/services/ContactListItemService/CreateService.ts @@ -0,0 +1,52 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import ContactListItem from "../../models/ContactListItem"; +import { logger } from "../../utils/logger"; +import CheckContactNumber from "../WbotServices/CheckNumber"; + +interface Data { + name: string; + number: string; + contactListId: number; + companyId: number; + email?: string; +} + +const CreateService = async (data: Data): Promise => { + const { name } = data; + + const contactListItemSchema = Yup.object().shape({ + name: Yup.string() + .min(3, "ERR_CONTACTLISTITEM_INVALID_NAME") + .required("ERR_CONTACTLISTITEM_REQUIRED") + }); + + try { + await contactListItemSchema.validate({ name }); + } catch (err: any) { + throw new AppError(err.message); + } + + const [record] = await ContactListItem.findOrCreate({ + where: { + number: data.number, + companyId: data.companyId, + contactListId: data.contactListId + }, + defaults: data + }); + + try { + const response = await CheckContactNumber(record.number, record.companyId); + record.isWhatsappValid = response.exists; + const number = response.jid.replace(/\D/g, ""); + record.number = number; + await record.save(); + } catch (e) { + logger.error(`Número de contato inválido: ${record.number}`); + } + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/ContactListItemService/DeleteService.ts b/backend/src/services/ContactListItemService/DeleteService.ts new file mode 100644 index 0000000..2824a96 --- /dev/null +++ b/backend/src/services/ContactListItemService/DeleteService.ts @@ -0,0 +1,16 @@ +import ContactListItem from "../../models/ContactListItem"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await ContactListItem.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_CONTACTLISTITEM_FOUND", 404); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/ContactListItemService/FindAllService.ts b/backend/src/services/ContactListItemService/FindAllService.ts new file mode 100644 index 0000000..2e4f158 --- /dev/null +++ b/backend/src/services/ContactListItemService/FindAllService.ts @@ -0,0 +1,10 @@ +import ContactListItem from "../../models/ContactListItem"; + +const FindAllService = async (): Promise => { + const records: ContactListItem[] = await ContactListItem.findAll({ + order: [["name", "ASC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/ContactListItemService/FindService.ts b/backend/src/services/ContactListItemService/FindService.ts new file mode 100644 index 0000000..e574f38 --- /dev/null +++ b/backend/src/services/ContactListItemService/FindService.ts @@ -0,0 +1,33 @@ +import ContactListItem from "../../models/ContactListItem"; +import Company from "../../models/Company"; + +type Params = { + companyId: number; + contactListId: number; +}; + +const FindService = async ({ + companyId, + contactListId +}: Params): Promise => { + let where: any = { + companyId + }; + + if (contactListId) { + where = { + ...where, + contactListId + }; + } + + const notes: ContactListItem[] = await ContactListItem.findAll({ + where, + include: [{ model: Company, as: "company", attributes: ["id", "name"] }], + order: [["name", "ASC"]] + }); + + return notes; +}; + +export default FindService; diff --git a/backend/src/services/ContactListItemService/ListService.ts b/backend/src/services/ContactListItemService/ListService.ts new file mode 100644 index 0000000..ab1f53f --- /dev/null +++ b/backend/src/services/ContactListItemService/ListService.ts @@ -0,0 +1,57 @@ +import { Sequelize, Op } from "sequelize"; +import ContactListItem from "../../models/ContactListItem"; + +interface Request { + searchParam?: string; + pageNumber?: string; + companyId: number | string; + contactListId: number | string; +} + +interface Response { + contacts: ContactListItem[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam = "", + pageNumber = "1", + companyId, + contactListId +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + }, + { number: { [Op.like]: `%${searchParam.toLowerCase().trim()}%` } } + ], + companyId, + contactListId + }; + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: contacts } = await ContactListItem.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["name", "ASC"]] + }); + + const hasMore = count > offset + contacts.length; + + return { + contacts, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/ContactListItemService/ShowService.ts b/backend/src/services/ContactListItemService/ShowService.ts new file mode 100644 index 0000000..1b09384 --- /dev/null +++ b/backend/src/services/ContactListItemService/ShowService.ts @@ -0,0 +1,14 @@ +import ContactListItem from "../../models/ContactListItem"; +import AppError from "../../errors/AppError"; + +const ShowService = async (id: string | number): Promise => { + const record = await ContactListItem.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_CONTACTLISTITEM_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/ContactListItemService/UpdateService.ts b/backend/src/services/ContactListItemService/UpdateService.ts new file mode 100644 index 0000000..b254eb8 --- /dev/null +++ b/backend/src/services/ContactListItemService/UpdateService.ts @@ -0,0 +1,41 @@ +import AppError from "../../errors/AppError"; +import ContactListItem from "../../models/ContactListItem"; +import { logger } from "../../utils/logger"; +import CheckContactNumber from "../WbotServices/CheckNumber"; + +interface Data { + id: number | string; + name: string; + number: string; + email?: string; +} + +const UpdateService = async (data: Data): Promise => { + const { id, name, number, email } = data; + + const record = await ContactListItem.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_CONTACTLISTITEM_FOUND", 404); + } + + await record.update({ + name, + number, + email + }); + + try { + const response = await CheckContactNumber(record.number, record.companyId); + record.isWhatsappValid = response.exists; + const number = response.jid.replace(/\D/g, ""); + record.number = number; + await record.save(); + } catch (e) { + logger.error(`Número de contato inválido: ${record.number}`); + } + + return record; +}; + +export default UpdateService; diff --git a/backend/src/services/ContactListService/CreateService.ts b/backend/src/services/ContactListService/CreateService.ts new file mode 100644 index 0000000..fcf7d03 --- /dev/null +++ b/backend/src/services/ContactListService/CreateService.ts @@ -0,0 +1,30 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import ContactList from "../../models/ContactList"; + +interface Data { + name: string; + companyId: number | string; +} + +const CreateService = async (data: Data): Promise => { + const { name, companyId } = data; + + const ticketnoteSchema = Yup.object().shape({ + name: Yup.string() + .min(3, "ERR_CONTACTLIST_INVALID_NAME") + .required("ERR_CONTACTLIST_REQUIRED") + }); + + try { + await ticketnoteSchema.validate({ name }); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await ContactList.create(data); + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/ContactListService/DeleteService.ts b/backend/src/services/ContactListService/DeleteService.ts new file mode 100644 index 0000000..3550ff3 --- /dev/null +++ b/backend/src/services/ContactListService/DeleteService.ts @@ -0,0 +1,16 @@ +import ContactList from "../../models/ContactList"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await ContactList.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_CONTACTLIST_FOUND", 404); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/ContactListService/FindAllService.ts b/backend/src/services/ContactListService/FindAllService.ts new file mode 100644 index 0000000..44efebe --- /dev/null +++ b/backend/src/services/ContactListService/FindAllService.ts @@ -0,0 +1,10 @@ +import ContactList from "../../models/ContactList"; + +const FindAllService = async (): Promise => { + const records: ContactList[] = await ContactList.findAll({ + order: [["name", "ASC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/ContactListService/FindService.ts b/backend/src/services/ContactListService/FindService.ts new file mode 100644 index 0000000..daf7ffd --- /dev/null +++ b/backend/src/services/ContactListService/FindService.ts @@ -0,0 +1,20 @@ +import ContactList from "../../models/ContactList"; +import Company from "../../models/Company"; + +type Params = { + companyId: string; +}; + +const FindService = async ({ companyId }: Params): Promise => { + const notes: ContactList[] = await ContactList.findAll({ + where: { + companyId + }, + include: [{ model: Company, as: "company", attributes: ["id", "name"] }], + order: [["name", "ASC"]] + }); + + return notes; +}; + +export default FindService; diff --git a/backend/src/services/ContactListService/ImportContacts.ts b/backend/src/services/ContactListService/ImportContacts.ts new file mode 100644 index 0000000..a5d2c9b --- /dev/null +++ b/backend/src/services/ContactListService/ImportContacts.ts @@ -0,0 +1,79 @@ +import { head } from "lodash"; +import XLSX from "xlsx"; +import { has } from "lodash"; +import ContactListItem from "../../models/ContactListItem"; +import CheckContactNumber from "../WbotServices/CheckNumber"; +import { logger } from "../../utils/logger"; +// import CheckContactNumber from "../WbotServices/CheckNumber"; + +export async function ImportContacts( + contactListId: number, + companyId: number, + file: Express.Multer.File | undefined +) { + const workbook = XLSX.readFile(file?.path as string); + const worksheet = head(Object.values(workbook.Sheets)) as any; + const rows: any[] = XLSX.utils.sheet_to_json(worksheet, { header: 0 }); + const contacts = rows.map(row => { + let name = ""; + let number = ""; + let email = ""; + + if (has(row, "nome") || has(row, "Nome")) { + name = row["nome"] || row["Nome"]; + } + + if ( + has(row, "numero") || + has(row, "número") || + has(row, "Numero") || + has(row, "Número") + ) { + number = row["numero"] || row["número"] || row["Numero"] || row["Número"]; + number = `${number}`.replace(/\D/g, ""); + } + + if ( + has(row, "email") || + has(row, "e-mail") || + has(row, "Email") || + has(row, "E-mail") + ) { + email = row["email"] || row["e-mail"] || row["Email"] || row["E-mail"]; + } + + return { name, number, email, contactListId, companyId }; + }); + + const contactList: ContactListItem[] = []; + + for (const contact of contacts) { + const [newContact, created] = await ContactListItem.findOrCreate({ + where: { + number: `${contact.number}`, + contactListId: contact.contactListId, + companyId: contact.companyId + }, + defaults: contact + }); + if (created) { + contactList.push(newContact); + } + } + + if (contactList) { + for (let newContact of contactList) { + try { + const response = await CheckContactNumber(newContact.number, companyId); + newContact.isWhatsappValid = response.exists; + const number = response.jid.replace(/\D/g, ""); + newContact.number = number; + await newContact.save(); + } catch (e) { + logger.error(`Número de contato inválido: ${newContact.number}`); + } + } + } + + return contactList; +} diff --git a/backend/src/services/ContactListService/ListService.ts b/backend/src/services/ContactListService/ListService.ts new file mode 100644 index 0000000..f77af09 --- /dev/null +++ b/backend/src/services/ContactListService/ListService.ts @@ -0,0 +1,76 @@ +import { Op, fn, col, where } from "sequelize"; +import ContactList from "../../models/ContactList"; +import ContactListItem from "../../models/ContactListItem"; +import { isEmpty } from "lodash"; + +interface Request { + companyId: number | string; + searchParam?: string; + pageNumber?: string; +} + +interface Response { + records: ContactList[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam = "", + pageNumber = "1", + companyId +}: Request): Promise => { + let whereCondition: any = { + companyId + }; + + if (!isEmpty(searchParam)) { + whereCondition = { + ...whereCondition, + [Op.or]: [ + { + name: where( + fn("LOWER", col("ContactList.name")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + } + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await ContactList.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["name", "ASC"]], + subQuery: false, + include: [ + { + model: ContactListItem, + as: "contacts", + attributes: [], + required: false + } + ], + attributes: [ + "id", + "name", + [fn("count", col("contacts.id")), "contactsCount"] + ], + group: ["ContactList.id"] + }); + + const hasMore = count > offset + records.length; + + return { + records, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/ContactListService/ShowService.ts b/backend/src/services/ContactListService/ShowService.ts new file mode 100644 index 0000000..13b11e3 --- /dev/null +++ b/backend/src/services/ContactListService/ShowService.ts @@ -0,0 +1,14 @@ +import ContactList from "../../models/ContactList"; +import AppError from "../../errors/AppError"; + +const ShowService = async (id: string | number): Promise => { + const record = await ContactList.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/ContactListService/UpdateService.ts b/backend/src/services/ContactListService/UpdateService.ts new file mode 100644 index 0000000..6c58753 --- /dev/null +++ b/backend/src/services/ContactListService/UpdateService.ts @@ -0,0 +1,25 @@ +import AppError from "../../errors/AppError"; +import ContactList from "../../models/ContactList"; + +interface Data { + id: number | string; + name: string; +} + +const UpdateService = async (data: Data): Promise => { + const { id, name } = data; + + const record = await ContactList.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_CONTACTLIST_FOUND", 404); + } + + await record.update({ + name + }); + + return record; +}; + +export default UpdateService; diff --git a/backend/src/services/ContactServices/CreateContactService.ts b/backend/src/services/ContactServices/CreateContactService.ts new file mode 100644 index 0000000..fbc4295 --- /dev/null +++ b/backend/src/services/ContactServices/CreateContactService.ts @@ -0,0 +1,50 @@ +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import ContactCustomField from "../../models/ContactCustomField"; + +interface ExtraInfo extends ContactCustomField { + name: string; + value: string; +} + +interface Request { + name: string; + number: string; + email?: string; + profilePicUrl?: string; + companyId: number; + extraInfo?: ExtraInfo[]; +} + +const CreateContactService = async ({ + name, + number, + email = "", + companyId, + extraInfo = [] +}: Request): Promise => { + const numberExists = await Contact.findOne({ + where: { number, companyId } + }); + + if (numberExists) { + throw new AppError("ERR_DUPLICATED_CONTACT"); + } + + const contact = await Contact.create( + { + name, + number, + email, + extraInfo, + companyId + }, + { + include: ["extraInfo"] + } + ); + + return contact; +}; + +export default CreateContactService; diff --git a/backend/src/services/ContactServices/CreateOrUpdateContactService.ts b/backend/src/services/ContactServices/CreateOrUpdateContactService.ts new file mode 100644 index 0000000..c25edd0 --- /dev/null +++ b/backend/src/services/ContactServices/CreateOrUpdateContactService.ts @@ -0,0 +1,76 @@ +import { getIO } from "../../libs/socket"; +import Contact from "../../models/Contact"; +import ContactCustomField from "../../models/ContactCustomField"; +import { isNil } from "lodash"; +interface ExtraInfo extends ContactCustomField { + name: string; + value: string; +} + +interface Request { + name: string; + number: string; + isGroup: boolean; + email?: string; + profilePicUrl?: string; + companyId: number; + extraInfo?: ExtraInfo[]; + whatsappId?: number; +} + +const CreateOrUpdateContactService = async ({ + name, + number: rawNumber, + profilePicUrl, + isGroup, + email = "", + companyId, + extraInfo = [], + whatsappId +}: Request): Promise => { + const number = isGroup ? rawNumber : rawNumber.replace(/[^0-9]/g, ""); + + const io = getIO(); + let contact: Contact | null; + + contact = await Contact.findOne({ + where: { + number, + companyId + } + }); + + if (contact) { + contact.update({ profilePicUrl }); + console.log(contact.whatsappId) + if (isNil(contact.whatsappId === null)) { + contact.update({ + whatsappId + }); + } + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, { + action: "update", + contact + }); + } else { + contact = await Contact.create({ + name, + number, + profilePicUrl, + email, + isGroup, + extraInfo, + companyId, + whatsappId + }); + + io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, { + action: "create", + contact + }); + } + + return contact; +}; + +export default CreateOrUpdateContactService; diff --git a/backend/src/services/ContactServices/DeleteContactService.ts b/backend/src/services/ContactServices/DeleteContactService.ts new file mode 100644 index 0000000..caaf86a --- /dev/null +++ b/backend/src/services/ContactServices/DeleteContactService.ts @@ -0,0 +1,16 @@ +import Contact from "../../models/Contact"; +import AppError from "../../errors/AppError"; + +const DeleteContactService = async (id: string): Promise => { + const contact = await Contact.findOne({ + where: { id } + }); + + if (!contact) { + throw new AppError("ERR_NO_CONTACT_FOUND", 404); + } + + await contact.destroy(); +}; + +export default DeleteContactService; diff --git a/backend/src/services/ContactServices/GetContactService.ts b/backend/src/services/ContactServices/GetContactService.ts new file mode 100644 index 0000000..7466c54 --- /dev/null +++ b/backend/src/services/ContactServices/GetContactService.ts @@ -0,0 +1,43 @@ +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import ContactCustomField from "../../models/ContactCustomField"; +import CreateContactService from "./CreateContactService"; + +interface ExtraInfo extends ContactCustomField { + name: string; + value: string; +} + +interface Request { + name: string; + number: string; + companyId: number; + email?: string; + profilePicUrl?: string; + extraInfo?: ExtraInfo[]; +} + +const GetContactService = async ({ + name, + number, + companyId +}: Request): Promise => { + const numberExists = await Contact.findOne({ + where: { number, companyId } + }); + + if (!numberExists) { + const contact = await CreateContactService({ + name, + number, + companyId + }); + + if (contact == null) throw new AppError("CONTACT_NOT_FIND"); + else return contact; + } + + return numberExists; +}; + +export default GetContactService; diff --git a/backend/src/services/ContactServices/ListContactsService.ts b/backend/src/services/ContactServices/ListContactsService.ts new file mode 100644 index 0000000..0443c7b --- /dev/null +++ b/backend/src/services/ContactServices/ListContactsService.ts @@ -0,0 +1,55 @@ +import { Sequelize, Op } from "sequelize"; +import Contact from "../../models/Contact"; + +interface Request { + searchParam?: string; + pageNumber?: string; + companyId: number; +} + +interface Response { + contacts: Contact[]; + count: number; + hasMore: boolean; +} + +const ListContactsService = async ({ + searchParam = "", + pageNumber = "1", + companyId +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + }, + { number: { [Op.like]: `%${searchParam.toLowerCase().trim()}%` } } + ], + companyId: { + [Op.eq]: companyId + } + }; + const limit = 30; + const offset = limit * (+pageNumber - 1); + + const { count, rows: contacts } = await Contact.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["name", "ASC"]] + }); + + const hasMore = count > offset + contacts.length; + + return { + contacts, + count, + hasMore + }; +}; + +export default ListContactsService; diff --git a/backend/src/services/ContactServices/ShowContactService.ts b/backend/src/services/ContactServices/ShowContactService.ts new file mode 100644 index 0000000..4c23ef4 --- /dev/null +++ b/backend/src/services/ContactServices/ShowContactService.ts @@ -0,0 +1,21 @@ +import Contact from "../../models/Contact"; +import AppError from "../../errors/AppError"; + +const ShowContactService = async ( + id: string | number, + companyId: number +): Promise => { + const contact = await Contact.findByPk(id, { include: ["extraInfo", "whatsapp"] }); + + if (contact?.companyId !== companyId) { + throw new AppError("Não é possível excluir registro de outra empresa"); + } + + if (!contact) { + throw new AppError("ERR_NO_CONTACT_FOUND", 404); + } + + return contact; +}; + +export default ShowContactService; diff --git a/backend/src/services/ContactServices/SimpleListService.ts b/backend/src/services/ContactServices/SimpleListService.ts new file mode 100644 index 0000000..12608ed --- /dev/null +++ b/backend/src/services/ContactServices/SimpleListService.ts @@ -0,0 +1,39 @@ +import Contact from "../../models/Contact"; +import AppError from "../../errors/AppError"; +import { FindOptions, Op } from "sequelize"; + +export interface SearchContactParams { + companyId: string | number; + name?: string; +} + +const SimpleListService = async ({ name, companyId }: SearchContactParams): Promise => { + let options: FindOptions = { + order: [ + ['name', 'ASC'] + ] + } + + if (name) { + options.where = { + name: { + [Op.like]: `%${name}%` + } + } + } + + options.where = { + ...options.where, + companyId + } + + const contacts = await Contact.findAll(options); + + if (!contacts) { + throw new AppError("ERR_NO_CONTACT_FOUND", 404); + } + + return contacts; +}; + +export default SimpleListService; diff --git a/backend/src/services/ContactServices/UpdateContactService.ts b/backend/src/services/ContactServices/UpdateContactService.ts new file mode 100644 index 0000000..638c195 --- /dev/null +++ b/backend/src/services/ContactServices/UpdateContactService.ts @@ -0,0 +1,76 @@ +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import ContactCustomField from "../../models/ContactCustomField"; + +interface ExtraInfo { + id?: number; + name: string; + value: string; +} +interface ContactData { + email?: string; + number?: string; + name?: string; + extraInfo?: ExtraInfo[]; +} + +interface Request { + contactData: ContactData; + contactId: string; + companyId: number; +} + +const UpdateContactService = async ({ + contactData, + contactId, + companyId +}: Request): Promise => { + const { email, name, number, extraInfo } = contactData; + + const contact = await Contact.findOne({ + where: { id: contactId }, + attributes: ["id", "name", "number", "email", "companyId", "profilePicUrl"], + include: ["extraInfo"] + }); + + if (contact?.companyId !== companyId) { + throw new AppError("Não é possível alterar registros de outra empresa"); + } + + if (!contact) { + throw new AppError("ERR_NO_CONTACT_FOUND", 404); + } + + if (extraInfo) { + await Promise.all( + extraInfo.map(async (info: any) => { + await ContactCustomField.upsert({ ...info, contactId: contact.id }); + }) + ); + + await Promise.all( + contact.extraInfo.map(async oldInfo => { + const stillExists = extraInfo.findIndex(info => info.id === oldInfo.id); + + if (stillExists === -1) { + await ContactCustomField.destroy({ where: { id: oldInfo.id } }); + } + }) + ); + } + + await contact.update({ + name, + number, + email + }); + + await contact.reload({ + attributes: ["id", "name", "number", "email", "profilePicUrl"], + include: ["extraInfo"] + }); + + return contact; +}; + +export default UpdateContactService; diff --git a/backend/src/services/FileServices/CreateService.ts b/backend/src/services/FileServices/CreateService.ts new file mode 100644 index 0000000..2e7356e --- /dev/null +++ b/backend/src/services/FileServices/CreateService.ts @@ -0,0 +1,68 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Files from "../../models/Files"; +import FilesOptions from "../../models/FilesOptions"; +import ShowService from "./ShowService"; + +interface Request { + name: string; + companyId: number; + message: string; + options?: FilesOptions[]; +} + +const CreateService = async ({ + name, + message, + companyId, + options +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string() + .required() + .min(3) + .test( + "Check-unique-name", + "ERR_RATING_NAME_ALREADY_EXISTS", + async value => { + if (value) { + const tagWithSameName = await Files.findOne({ + where: { name: value, companyId } + }); + + return !tagWithSameName; + } + return false; + } + ) + }); + + try { + await schema.validate({ name }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + throw new AppError(err.message); + } + let fileList = await Files.create({ + name, + message, + companyId + }); + + if(options && options.length > 0) { + await Promise.all( + options.map(async info => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await FilesOptions.upsert({ ...info, fileId: fileList.id }); + }) + ); + } + + fileList = await ShowService(fileList.id, companyId) + + return fileList; +}; + +export default CreateService; \ No newline at end of file diff --git a/backend/src/services/FileServices/DeleteAllService.ts b/backend/src/services/FileServices/DeleteAllService.ts new file mode 100644 index 0000000..46736b4 --- /dev/null +++ b/backend/src/services/FileServices/DeleteAllService.ts @@ -0,0 +1,16 @@ +import Files from "../../models/Files"; +import AppError from "../../errors/AppError"; + +const DeleteAllService = async (companyId: number): Promise => { + await Files.findAll({ + where: { companyId } + }); + + if (!Files) { + throw new AppError("ERR_NO_RATING_FOUND", 404); + } + + await Files.destroy({ where: {} }); +}; + +export default DeleteAllService; diff --git a/backend/src/services/FileServices/DeleteService.ts b/backend/src/services/FileServices/DeleteService.ts new file mode 100644 index 0000000..55078f3 --- /dev/null +++ b/backend/src/services/FileServices/DeleteService.ts @@ -0,0 +1,16 @@ +import Files from "../../models/Files"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string | number, companyId: number): Promise => { + const file = await Files.findOne({ + where: { id, companyId } + }); + + if (!file) { + throw new AppError("ERR_NO_RATING_FOUND", 404); + } + + await file.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/FileServices/ListService.ts b/backend/src/services/FileServices/ListService.ts new file mode 100644 index 0000000..062e49b --- /dev/null +++ b/backend/src/services/FileServices/ListService.ts @@ -0,0 +1,46 @@ +import { Op } from "sequelize"; +import Files from "../../models/Files"; + +interface Request { + companyId: number; + searchParam?: string; + pageNumber?: string | number; +} + +interface Response { + files: Files[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam, + pageNumber = "1", + companyId +}: Request): Promise => { + let whereCondition = {}; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + if (searchParam) { + whereCondition = { + [Op.or]: [{ name: { [Op.like]: `%${searchParam}%` } }] + }; + } + const { count, rows: files } = await Files.findAndCountAll({ + where: {companyId, ...whereCondition}, + limit, + offset, + order: [["name", "ASC"]] + }); + + const hasMore = count > offset + files.length; + + return { + files, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/FileServices/ShowService.ts b/backend/src/services/FileServices/ShowService.ts new file mode 100644 index 0000000..6684c0b --- /dev/null +++ b/backend/src/services/FileServices/ShowService.ts @@ -0,0 +1,25 @@ +import Files from "../../models/Files"; +import AppError from "../../errors/AppError"; +import FilesOptions from "../../models/FilesOptions"; + +const ShowFileService = async (id: string | number, companyId: number): Promise => { + const fileList = await Files.findOne({ + where: { id, companyId }, + include: [ + "options", + { + model: FilesOptions, + as: "options", + order: [["id","ASC"]] + } + ] + }); + + if (!fileList) { + throw new AppError("ERR_NO_FILE_FOUND", 404); + } + + return fileList; +}; + +export default ShowFileService; diff --git a/backend/src/services/FileServices/SimpleListService.ts b/backend/src/services/FileServices/SimpleListService.ts new file mode 100644 index 0000000..fb027f7 --- /dev/null +++ b/backend/src/services/FileServices/SimpleListService.ts @@ -0,0 +1,30 @@ +import { Op } from "sequelize"; +import Rating from "../../models/Files"; + +interface Request { + companyId: number + searchParam?: string; +} + +const ListService = async ({ searchParam, companyId }: Request): Promise => { + let whereCondition = {}; + + if (searchParam) { + whereCondition = { + [Op.or]: [{ name: { [Op.like]: `%${searchParam}%` } }] + }; + } + + const ratings = await Rating.findAll({ + where: {companyId, ...whereCondition}, + order: [["name", "ASC"]], + attributes: { + exclude: ["createdAt", "updatedAt"] + }, + group: ["Rating.id"] + }); + + return ratings; +}; + +export default ListService; diff --git a/backend/src/services/FileServices/UpdateService.ts b/backend/src/services/FileServices/UpdateService.ts new file mode 100644 index 0000000..7946cd7 --- /dev/null +++ b/backend/src/services/FileServices/UpdateService.ts @@ -0,0 +1,82 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Files from "../../models/Files"; +import FilesOptions from "../../models/FilesOptions"; +import ShowService from "./ShowService"; + +interface Options { + id?: number; + name: string; + path: string; +} + +interface FileData { + id?: number; + name: string; + message: string; + options?: Options[]; +} + +interface Request { + fileData: FileData; + id: string | number; + companyId: number; +} + +const UpdateService = async ({ + fileData, + id, + companyId +}: Request): Promise => { + const file = await ShowService(id, companyId); + + const schema = Yup.object().shape({ + name: Yup.string().min(3) + }); + + const { name, message, options } = fileData; + + try { + await schema.validate({ name }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + throw new AppError(err.message); + } + + if (options) { + await Promise.all( + options.map(async info => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await FilesOptions.upsert({ ...info, fileId: file.id }); + }) + ); + + await Promise.all( + file.options.map(async oldInfo => { + const stillExists = options.findIndex(info => info.id === oldInfo.id); + + if (stillExists === -1) { + await FilesOptions.destroy({ where: { id: oldInfo.id } }); + } + }) + ); + } + + + + + await file.update({ + name, + message + }); + + await file.reload({ + attributes: ["id", "name", "message","companyId"], + include: ["options"] + }); + return file; +}; + +export default UpdateService; diff --git a/backend/src/services/ForgotPassWordServices/SendMail.ts b/backend/src/services/ForgotPassWordServices/SendMail.ts new file mode 100644 index 0000000..cef87d0 --- /dev/null +++ b/backend/src/services/ForgotPassWordServices/SendMail.ts @@ -0,0 +1,248 @@ +import nodemailer from "nodemailer"; +import sequelize from "sequelize"; +import database from "../../database"; +import Setting from "../../models/Setting"; +import { config } from "dotenv"; +config(); +interface UserData { + companyId: number; +} +const SendMail = async (email: string, tokenSenha: string) => { + const { hasResult, data } = await filterEmail(email); + if (!hasResult) { + return { status: 404, message: "Email não encontrado" }; + } + const userData = data[0][0] as UserData; + if (!userData || userData.companyId === undefined) { + return { status: 404, message: "Dados do usuário não encontrados" }; + } + const companyId = userData.companyId; + const urlSmtp = process.env.MAIL_HOST; + const userSmtp = process.env.MAIL_USER; + const passwordSmpt = process.env.MAIL_PASS; + const fromEmail = process.env.MAIL_FROM; + const transporter = nodemailer.createTransport({ + host: urlSmtp, + port: Number(process.env.MAIL_PORT), + secure: true, + auth: { user: userSmtp, pass: passwordSmpt } + }); + if (hasResult === true) { + const { hasResults, datas } = await insertToken(email, tokenSenha); + async function sendEmail() { + try { + const mailOptions = { + from: fromEmail, + to: email, + subject: "Redefinição de Senha - S-WhiteLabel", + html: ` + + + + + + + + Novo modelo + + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + + + + +

Bem-vindo à Scripts WhiteLabel

Você solicitou recuperação de senha do Whaticket!

+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + +

Código de Verificação:

${tokenSenha}

+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +

Está com dúvidas?

Entre em contato agora mesmo conosco.

+
+ +` + }; + const info = await transporter.sendMail(mailOptions); + console.log("E-mail enviado: " + info.response); + } catch (error) { + console.log(error); + } + } + sendEmail(); + } +}; +const filterEmail = async (email: string) => { + const sql = `SELECT * FROM "Users" WHERE email ='${email}'`; + const result = await database.query(sql, { + type: sequelize.QueryTypes.SELECT + }); + return { hasResult: result.length > 0, data: [result] }; +}; +const insertToken = async (email: string, tokenSenha: string) => { + const sqls = `UPDATE "Users" SET "resetPassword"= '${tokenSenha}' WHERE email ='${email}'`; + const results = await database.query(sqls, { + type: sequelize.QueryTypes.UPDATE + }); + return { hasResults: results.length > 0, datas: results }; +}; +export default SendMail; diff --git a/backend/src/services/HelpServices/CreateService.ts b/backend/src/services/HelpServices/CreateService.ts new file mode 100644 index 0000000..a444df4 --- /dev/null +++ b/backend/src/services/HelpServices/CreateService.ts @@ -0,0 +1,33 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Help from "../../models/Help"; + +interface Data { + title: string; + description?: string; + video?: string; + link?: string; +} + +const CreateService = async (data: Data): Promise => { + const { title, description } = data; + + const helpSchema = Yup.object().shape({ + title: Yup.string() + .min(3, "ERR_HELP_INVALID_NAME") + .required("ERR_HELP_REQUIRED"), + description: Yup.string().min(3, "ERR_HELP_INVALID_NAME") + }); + + try { + await helpSchema.validate({ title, description }); + } catch (err) { + throw new AppError(err.message); + } + + const record = await Help.create(data); + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/HelpServices/DeleteService.ts b/backend/src/services/HelpServices/DeleteService.ts new file mode 100644 index 0000000..e922ea5 --- /dev/null +++ b/backend/src/services/HelpServices/DeleteService.ts @@ -0,0 +1,16 @@ +import Help from "../../models/Help"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await Help.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_HELP_FOUND", 404); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/HelpServices/FindAllService.ts b/backend/src/services/HelpServices/FindAllService.ts new file mode 100644 index 0000000..ea6262d --- /dev/null +++ b/backend/src/services/HelpServices/FindAllService.ts @@ -0,0 +1,10 @@ +import Help from "../../models/Help"; + +const FindAllService = async (): Promise => { + const records: Help[] = await Help.findAll({ + order: [["title", "ASC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/HelpServices/FindService.ts b/backend/src/services/HelpServices/FindService.ts new file mode 100644 index 0000000..b3d3401 --- /dev/null +++ b/backend/src/services/HelpServices/FindService.ts @@ -0,0 +1,11 @@ +import Help from "../../models/Help"; + +const FindService = async (): Promise => { + const notes: Help[] = await Help.findAll({ + order: [["title", "ASC"]] + }); + + return notes; +}; + +export default FindService; diff --git a/backend/src/services/HelpServices/ListService.ts b/backend/src/services/HelpServices/ListService.ts new file mode 100644 index 0000000..5ab1dd5 --- /dev/null +++ b/backend/src/services/HelpServices/ListService.ts @@ -0,0 +1,49 @@ +import { Sequelize, Op } from "sequelize"; +import Help from "../../models/Help"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + records: Help[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + title: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("title")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await Help.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["title", "ASC"]] + }); + + const hasMore = count > offset + records.length; + + return { + records, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/HelpServices/ShowService.ts b/backend/src/services/HelpServices/ShowService.ts new file mode 100644 index 0000000..8e38638 --- /dev/null +++ b/backend/src/services/HelpServices/ShowService.ts @@ -0,0 +1,14 @@ +import Help from "../../models/Help"; +import AppError from "../../errors/AppError"; + +const ShowService = async (id: string | number): Promise => { + const record = await Help.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_HELP_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/HelpServices/UpdateService.ts b/backend/src/services/HelpServices/UpdateService.ts new file mode 100644 index 0000000..d4bc4b2 --- /dev/null +++ b/backend/src/services/HelpServices/UpdateService.ts @@ -0,0 +1,26 @@ +import AppError from "../../errors/AppError"; +import Help from "../../models/Help"; + +interface Data { + id: number | string; + title: string; + description?: string; + video?: string; + link?: string; +} + +const UpdateService = async (data: Data): Promise => { + const { id } = data; + + const record = await Help.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_HELP_FOUND", 404); + } + + await record.update(data); + + return record; +}; + +export default UpdateService; diff --git a/backend/src/services/InvoicesService/FindAllInvoiceService.ts b/backend/src/services/InvoicesService/FindAllInvoiceService.ts new file mode 100644 index 0000000..47d73cb --- /dev/null +++ b/backend/src/services/InvoicesService/FindAllInvoiceService.ts @@ -0,0 +1,17 @@ +import Invoices from "../../models/Invoices"; + +interface Request { + companyId: number; +} + +const FindAllPlanService = async (companyId: number): Promise => { + const invoice = await Invoices.findAll({ + where: { + companyId + }, + order: [["id", "ASC"]] + }); + return invoice; +}; + +export default FindAllPlanService; diff --git a/backend/src/services/InvoicesService/ListInvoicesServices.ts b/backend/src/services/InvoicesService/ListInvoicesServices.ts new file mode 100644 index 0000000..5b9faff --- /dev/null +++ b/backend/src/services/InvoicesService/ListInvoicesServices.ts @@ -0,0 +1,49 @@ +import { Sequelize, Op } from "sequelize"; +import Invoices from "../../models/Invoices"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + invoices: Invoices[]; + count: number; + hasMore: boolean; +} + +const ListInvoicesServices = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("detail")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: invoices } = await Invoices.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["id", "ASC"]] + }); + + const hasMore = count > offset + invoices.length; + + return { + invoices, + count, + hasMore + }; +}; + +export default ListInvoicesServices; diff --git a/backend/src/services/InvoicesService/ShowInvoiceService.ts b/backend/src/services/InvoicesService/ShowInvoiceService.ts new file mode 100644 index 0000000..a0409c7 --- /dev/null +++ b/backend/src/services/InvoicesService/ShowInvoiceService.ts @@ -0,0 +1,14 @@ +import Invoice from "../../models/Invoices"; +import AppError from "../../errors/AppError"; + +const ShowInvoceService = async (Invoiceid: string | number): Promise => { + const invoice = await Invoice.findByPk(Invoiceid); + + if (!invoice) { + throw new AppError("ERR_NO_PLAN_FOUND", 404); + } + + return invoice; +}; + +export default ShowInvoceService; diff --git a/backend/src/services/InvoicesService/UpdateInvoiceService.ts b/backend/src/services/InvoicesService/UpdateInvoiceService.ts new file mode 100644 index 0000000..6ac45e8 --- /dev/null +++ b/backend/src/services/InvoicesService/UpdateInvoiceService.ts @@ -0,0 +1,25 @@ +import AppError from "../../errors/AppError"; +import Invoice from "../../models/Invoices"; + +interface InvoiceData { + status: string; + id?: number | string; +} + +const UpdateInvoiceService = async (InvoiceData: InvoiceData): Promise => { + const { id, status } = InvoiceData; + + const invoice = await Invoice.findByPk(id); + + if (!invoice) { + throw new AppError("ERR_NO_PLAN_FOUND", 404); + } + + await invoice.update({ + status, + }); + + return invoice; +}; + +export default UpdateInvoiceService; diff --git a/backend/src/services/MessageServices/CreateMessageService.ts b/backend/src/services/MessageServices/CreateMessageService.ts new file mode 100644 index 0000000..43236fc --- /dev/null +++ b/backend/src/services/MessageServices/CreateMessageService.ts @@ -0,0 +1,77 @@ +import { getIO } from "../../libs/socket"; +import Message from "../../models/Message"; +import Ticket from "../../models/Ticket"; +import Whatsapp from "../../models/Whatsapp"; + +interface MessageData { + id: string; + ticketId: number; + body: string; + contactId?: number; + fromMe?: boolean; + read?: boolean; + mediaType?: string; + mediaUrl?: string; + ack?: number; + queueId?: number; +} +interface Request { + messageData: MessageData; + companyId: number; +} + +const CreateMessageService = async ({ + messageData, + companyId +}: Request): Promise => { + await Message.upsert({ ...messageData, companyId }); + + const message = await Message.findByPk(messageData.id, { + include: [ + "contact", + { + model: Ticket, + as: "ticket", + include: [ + "contact", + "queue", + { + model: Whatsapp, + as: "whatsapp", + attributes: ["name"] + } + ] + }, + { + model: Message, + as: "quotedMsg", + include: ["contact"] + } + ] + }); + + if (message.ticket.queueId !== null && message.queueId === null) { + await message.update({ queueId: message.ticket.queueId }); + } + + if (!message) { + throw new Error("ERR_CREATING_MESSAGE"); + } + + const io = getIO(); + io.to(message.ticketId.toString()) + .to(`company-${companyId}-${message.ticket.status}`) + .to(`company-${companyId}-notification`) + .to(`queue-${message.ticket.queueId}-${message.ticket.status}`) + .to(`queue-${message.ticket.queueId}-notification`) + .emit(`company-${companyId}-appMessage`, { + action: "create", + message, + ticket: message.ticket, + contact: message.ticket.contact + }); + + return message; +}; + +export default CreateMessageService; diff --git a/backend/src/services/MessageServices/GetMessagesService.ts b/backend/src/services/MessageServices/GetMessagesService.ts new file mode 100644 index 0000000..b33c014 --- /dev/null +++ b/backend/src/services/MessageServices/GetMessagesService.ts @@ -0,0 +1,20 @@ +import AppError from "../../errors/AppError"; +import Message from "../../models/Message"; + +interface Request { + id: string; +} + +const GetMessageService = async ({ id }: Request): Promise => { + const messageExists = await Message.findOne({ + where: { id } + }); + + if (!messageExists) { + throw new AppError("MESSAGE_NOT_FIND"); + } + + return messageExists; +}; + +export default GetMessageService; diff --git a/backend/src/services/MessageServices/ListMessagesService.ts b/backend/src/services/MessageServices/ListMessagesService.ts new file mode 100644 index 0000000..8cec516 --- /dev/null +++ b/backend/src/services/MessageServices/ListMessagesService.ts @@ -0,0 +1,84 @@ +import { FindOptions } from "sequelize/types"; +import { Op } from "sequelize"; +import AppError from "../../errors/AppError"; +import Message from "../../models/Message"; +import Ticket from "../../models/Ticket"; +import ShowTicketService from "../TicketServices/ShowTicketService"; +import Queue from "../../models/Queue"; + +interface Request { + ticketId: string; + companyId: number; + pageNumber?: string; + queues?: number[]; +} + +interface Response { + messages: Message[]; + ticket: Ticket; + count: number; + hasMore: boolean; +} + +const ListMessagesService = async ({ + pageNumber = "1", + ticketId, + companyId, + queues = [] +}: Request): Promise => { + const ticket = await ShowTicketService(ticketId, companyId); + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + // await setMessagesAsRead(ticket); + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const options: FindOptions = { + where: { + ticketId, + companyId + } + }; + + if (queues.length > 0) { + options.where["queueId"] = { + [Op.or]: { + [Op.in]: queues, + [Op.eq]: null + } + }; + } + + const { count, rows: messages } = await Message.findAndCountAll({ + ...options, + limit, + include: [ + "contact", + { + model: Message, + as: "quotedMsg", + include: ["contact"] + }, + { + model: Queue, + as: "queue" + } + ], + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + messages.length; + + return { + messages: messages.reverse(), + ticket, + count, + hasMore + }; +}; + +export default ListMessagesService; diff --git a/backend/src/services/PlanService/CreatePlanService.ts b/backend/src/services/PlanService/CreatePlanService.ts new file mode 100644 index 0000000..704b339 --- /dev/null +++ b/backend/src/services/PlanService/CreatePlanService.ts @@ -0,0 +1,54 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Plan from "../../models/Plan"; + +interface PlanData { + name: string; + users: number; + connections: number; + queues: number; + value: number; + useCampaigns?: boolean; + useSchedules?: boolean; + useInternalChat?: boolean; + useExternalApi?: boolean; + useKanban?: boolean; + useOpenAi?: boolean; + useIntegrations?: boolean; +} + +const CreatePlanService = async (planData: PlanData): Promise => { + const { name } = planData; + + const planSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "ERR_PLAN_INVALID_NAME") + .required("ERR_PLAN_INVALID_NAME") + .test( + "Check-unique-name", + "ERR_PLAN_NAME_ALREADY_EXISTS", + async value => { + if (value) { + const planWithSameName = await Plan.findOne({ + where: { name: value } + }); + + return !planWithSameName; + } + return false; + } + ) + }); + + try { + await planSchema.validate({ name }); + } catch (err) { + throw new AppError(err.message); + } + + const plan = await Plan.create(planData); + + return plan; +}; + +export default CreatePlanService; diff --git a/backend/src/services/PlanService/DeletePlanService.ts b/backend/src/services/PlanService/DeletePlanService.ts new file mode 100644 index 0000000..47764b9 --- /dev/null +++ b/backend/src/services/PlanService/DeletePlanService.ts @@ -0,0 +1,16 @@ +import Plan from "../../models/Plan"; +import AppError from "../../errors/AppError"; + +const DeletePlanService = async (id: string): Promise => { + const plan = await Plan.findOne({ + where: { id } + }); + + if (!plan) { + throw new AppError("ERR_NO_PLAN_FOUND", 404); + } + + await plan.destroy(); +}; + +export default DeletePlanService; diff --git a/backend/src/services/PlanService/FindAllPlanService.ts b/backend/src/services/PlanService/FindAllPlanService.ts new file mode 100644 index 0000000..1aa20e4 --- /dev/null +++ b/backend/src/services/PlanService/FindAllPlanService.ts @@ -0,0 +1,10 @@ +import Plan from "../../models/Plan"; + +const FindAllPlanService = async (): Promise => { + const plan = await Plan.findAll({ + order: [["name", "ASC"]] + }); + return plan; +}; + +export default FindAllPlanService; diff --git a/backend/src/services/PlanService/ListPlansService.ts b/backend/src/services/PlanService/ListPlansService.ts new file mode 100644 index 0000000..e244c91 --- /dev/null +++ b/backend/src/services/PlanService/ListPlansService.ts @@ -0,0 +1,49 @@ +import { Sequelize, Op } from "sequelize"; +import Plan from "../../models/Plan"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + plans: Plan[]; + count: number; + hasMore: boolean; +} + +const ListPlansService = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: plans } = await Plan.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["name", "ASC"]] + }); + + const hasMore = count > offset + plans.length; + + return { + plans, + count, + hasMore + }; +}; + +export default ListPlansService; diff --git a/backend/src/services/PlanService/ShowPlanService.ts b/backend/src/services/PlanService/ShowPlanService.ts new file mode 100644 index 0000000..96f9388 --- /dev/null +++ b/backend/src/services/PlanService/ShowPlanService.ts @@ -0,0 +1,14 @@ +import Plan from "../../models/Plan"; +import AppError from "../../errors/AppError"; + +const ShowPlanService = async (id: string | number): Promise => { + const plan = await Plan.findByPk(id); + + if (!plan) { + throw new AppError("ERR_NO_PLAN_FOUND", 404); + } + + return plan; +}; + +export default ShowPlanService; diff --git a/backend/src/services/PlanService/UpdatePlanService.ts b/backend/src/services/PlanService/UpdatePlanService.ts new file mode 100644 index 0000000..7cfeb35 --- /dev/null +++ b/backend/src/services/PlanService/UpdatePlanService.ts @@ -0,0 +1,34 @@ +import AppError from "../../errors/AppError"; +import Plan from "../../models/Plan"; + +interface PlanData { + name: string; + id?: number | string; + users?: number; + connections?: number; + queues?: number; + value?: number; + useCampaigns?: boolean; + useSchedules?: boolean; + useInternalChat?: boolean; + useExternalApi?: boolean; + useKanban?: boolean; + useOpenAi?: boolean; + useIntegrations?: boolean; +} + +const UpdatePlanService = async (planData: PlanData): Promise => { + const { id, name, users, connections, queues, value } = planData; + + const plan = await Plan.findByPk(id); + + if (!plan) { + throw new AppError("ERR_NO_PLAN_FOUND", 404); + } + + await plan.update(planData); + + return plan; +}; + +export default UpdatePlanService; diff --git a/backend/src/services/PromptServices/CreatePromptService.ts b/backend/src/services/PromptServices/CreatePromptService.ts new file mode 100644 index 0000000..ccf8029 --- /dev/null +++ b/backend/src/services/PromptServices/CreatePromptService.ts @@ -0,0 +1,47 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Prompt from "../../models/Prompt"; +import ShowPromptService from "./ShowPromptService"; + +interface PromptData { + name: string; + apiKey: string; + prompt: string; + maxTokens?: number; + temperature?: number; + promptTokens?: number; + completionTokens?: number; + totalTokens?: number; + queueId?: number; + maxMessages?: number; + companyId: string | number; + voice?: string; + voiceKey?: string; + voiceRegion?: string; +} + +const CreatePromptService = async (promptData: PromptData): Promise => { + const { name, apiKey, prompt, queueId,maxMessages,companyId } = promptData; + + const promptSchema = Yup.object().shape({ + name: Yup.string().required("ERR_PROMPT_NAME_INVALID"), + prompt: Yup.string().required("ERR_PROMPT_INTELLIGENCE_INVALID"), + apiKey: Yup.string().required("ERR_PROMPT_APIKEY_INVALID"), + queueId: Yup.number().required("ERR_PROMPT_QUEUEID_INVALID"), + maxMessages: Yup.number().required("ERR_PROMPT_MAX_MESSAGES_INVALID"), + companyId: Yup.number().required("ERR_PROMPT_companyId_INVALID") + }); + + try { + await promptSchema.validate({ name, apiKey, prompt, queueId,maxMessages,companyId }); + } catch (err) { + throw new AppError(`${JSON.stringify(err, undefined, 2)}`); + } + + let promptTable = await Prompt.create(promptData); + promptTable = await ShowPromptService({ promptId: promptTable.id, companyId }); + + return promptTable; +}; + +export default CreatePromptService; diff --git a/backend/src/services/PromptServices/DeletePromptService.ts b/backend/src/services/PromptServices/DeletePromptService.ts new file mode 100644 index 0000000..9fc04d6 --- /dev/null +++ b/backend/src/services/PromptServices/DeletePromptService.ts @@ -0,0 +1,9 @@ +import ShowPromptService from "./ShowPromptService"; + +const DeletePromptService = async (promptId: number | string, companyId: number | string): Promise => { + const prompt = await ShowPromptService({ promptId, companyId }); + + await prompt.destroy(); +}; + +export default DeletePromptService; diff --git a/backend/src/services/PromptServices/ListPromptsService.ts b/backend/src/services/PromptServices/ListPromptsService.ts new file mode 100644 index 0000000..8b0540c --- /dev/null +++ b/backend/src/services/PromptServices/ListPromptsService.ts @@ -0,0 +1,56 @@ +import { Op } from "sequelize"; +import Prompt from "../../models/Prompt"; +import Queue from "../../models/Queue"; + +interface Request { + searchParam?: string; + pageNumber?: string | number; + companyId: string | number; +} + +interface Response { + prompts: Prompt[]; + count: number; + hasMore: boolean; +} + +const ListPromptsService = async ({ + searchParam = "", + pageNumber = "1", + companyId +}: Request): Promise => { + let whereCondition = {}; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + if (searchParam) { + whereCondition = { + [Op.or]: [ + { name: { [Op.like]: `%${searchParam}%` } } + ] + } + } + + const { count, rows: prompts } = await Prompt.findAndCountAll({ + where: { ...whereCondition, companyId }, + include: [ + { + model: Queue, + as: "queue", + attributes: ["id", "name"] + } + ], + limit, + offset, + order: [["name", "ASC"]], + }); + const hasMore = count > offset + prompts.length; + + return { + prompts, + count, + hasMore + }; +}; + +export default ListPromptsService; diff --git a/backend/src/services/PromptServices/ShowPromptService.ts b/backend/src/services/PromptServices/ShowPromptService.ts new file mode 100644 index 0000000..13424d1 --- /dev/null +++ b/backend/src/services/PromptServices/ShowPromptService.ts @@ -0,0 +1,30 @@ +import AppError from "../../errors/AppError"; +import Prompt from "../../models/Prompt"; +import Queue from "../../models/Queue"; + +interface Data { + promptId: string | number; + companyId: string | number; +} +const ShowPromptService = async ({ promptId, companyId }: Data): Promise => { + + const prompt = await Prompt.findOne({ + where: { + id: promptId, + companyId + }, + include: [ + { + model: Queue, + as: "queue" + } + ] + }); + + if (!prompt) { + throw new AppError("ERR_NO_PROMPT_FOUND", 404); + } + + return prompt; +}; +export default ShowPromptService; diff --git a/backend/src/services/PromptServices/UpdatePromptService.ts b/backend/src/services/PromptServices/UpdatePromptService.ts new file mode 100644 index 0000000..ed70999 --- /dev/null +++ b/backend/src/services/PromptServices/UpdatePromptService.ts @@ -0,0 +1,58 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Prompt from "../../models/Prompt"; +import ShowPromptService from "./ShowPromptService"; + +interface PromptData { + id?: number; + name: string; + apiKey: string; + prompt: string; + maxTokens?: number; + temperature?: number; + promptTokens?: number; + completionTokens?: number; + totalTokens?: number; + queueId?: number; + maxMessages?: number; + companyId: string | number; + voice?: string; + voiceKey?: string; + voiceRegion?: string; +} + +interface Request { + promptData: PromptData; + promptId: string | number; + companyId: string | number; +} + +const UpdatePromptService = async ({ + promptId, + promptData, + companyId +}: Request): Promise => { + const promptTable = await ShowPromptService({ promptId: promptId, companyId }); + + const promptSchema = Yup.object().shape({ + name: Yup.string().required("ERR_PROMPT_NAME_INVALID"), + prompt: Yup.string().required("ERR_PROMPT_PROMPT_INVALID"), + apiKey: Yup.string().required("ERR_PROMPT_APIKEY_INVALID"), + queueId: Yup.number().required("ERR_PROMPT_QUEUEID_INVALID"), + maxMessages: Yup.number().required("ERR_PROMPT_MAX_MESSAGES_INVALID") + }); + + const { name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages, voice, voiceKey, voiceRegion } = promptData; + + try { + await promptSchema.validate({ name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages }); + } catch (err) { + throw new AppError(`${JSON.stringify(err, undefined, 2)}`); + } + + await promptTable.update({ name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages, voice, voiceKey, voiceRegion }); + await promptTable.reload(); + return promptTable; +}; + +export default UpdatePromptService; diff --git a/backend/src/services/QueueIntegrationServices/CreateQueueIntegrationService.ts b/backend/src/services/QueueIntegrationServices/CreateQueueIntegrationService.ts new file mode 100644 index 0000000..c0f4529 --- /dev/null +++ b/backend/src/services/QueueIntegrationServices/CreateQueueIntegrationService.ts @@ -0,0 +1,86 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import QueueIntegrations from "../../models/QueueIntegrations"; + + +interface Request { + type: string; + name: string; + projectName: string; + jsonContent: string; + language: string; + urlN8N?: string; + companyId: number; + typebotSlug?: string; + typebotExpires?: number; + typebotKeywordFinish?: string; + typebotUnknownMessage?: string; + typebotDelayMessage?: number; + typebotKeywordRestart?: string; + typebotRestartMessage?: string; +} + +const CreateQueueIntegrationService = async ({ + type, + name, + projectName, + jsonContent, + language, + urlN8N, + companyId, + typebotExpires, + typebotKeywordFinish, + typebotSlug, + typebotUnknownMessage, + typebotDelayMessage, + typebotKeywordRestart, + typebotRestartMessage +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string() + .required() + .min(2) + .test( + "Check-name", + "This integration name is already used.", + async value => { + if (!value) return false; + const nameExists = await QueueIntegrations.findOne({ + where: { name: value, companyId } + }); + return !nameExists; + } + ) + }); + + try { + await schema.validate({ type, name, projectName, jsonContent, language, urlN8N, companyId }); + } catch (err) { + throw new AppError(err.message); + } + + + const queueIntegration = await QueueIntegrations.create( + { + type, + name, + projectName, + jsonContent, + language, + urlN8N, + companyId, + typebotExpires, + typebotKeywordFinish, + typebotSlug, + typebotUnknownMessage, + typebotDelayMessage, + typebotKeywordRestart, + typebotRestartMessage + } + ); + + return queueIntegration; +}; + +export default CreateQueueIntegrationService; \ No newline at end of file diff --git a/backend/src/services/QueueIntegrationServices/DeleteQueueIntegrationService.ts b/backend/src/services/QueueIntegrationServices/DeleteQueueIntegrationService.ts new file mode 100644 index 0000000..5f9d3ef --- /dev/null +++ b/backend/src/services/QueueIntegrationServices/DeleteQueueIntegrationService.ts @@ -0,0 +1,16 @@ +import QueueIntegrations from "../../models/QueueIntegrations"; +import AppError from "../../errors/AppError"; + +const DeleteQueueIntegrationService = async (id: string): Promise => { + const dialogflow = await QueueIntegrations.findOne({ + where: { id } + }); + + if (!dialogflow) { + throw new AppError("ERR_NO_DIALOG_FOUND", 404); + } + + await dialogflow.destroy(); +}; + +export default DeleteQueueIntegrationService; \ No newline at end of file diff --git a/backend/src/services/QueueIntegrationServices/ListQueueIntegrationService.ts b/backend/src/services/QueueIntegrationServices/ListQueueIntegrationService.ts new file mode 100644 index 0000000..18247a6 --- /dev/null +++ b/backend/src/services/QueueIntegrationServices/ListQueueIntegrationService.ts @@ -0,0 +1,57 @@ +import { Sequelize, Op, Filterable } from "sequelize"; +import QueueIntegrations from "../../models/QueueIntegrations"; + +interface Request { + searchParam?: string; + pageNumber?: string | number; + companyId: number; +} + +interface Response { + queueIntegrations: QueueIntegrations[]; + count: number; + hasMore: boolean; +} + +const ListQueueIntegrationService = async ({ + searchParam = "", + pageNumber = "1", + companyId +}: Request): Promise => { + let whereCondition: Filterable["where"] = { + [Op.or]: [ + { + "$QueueIntegrations.name$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("QueueIntegrations.name")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + } + ] + }; + + whereCondition = { + ...whereCondition, + companyId + }; + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: queueIntegrations } = await QueueIntegrations.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]], + }); + + const hasMore = count > offset + queueIntegrations.length; + + return { + queueIntegrations, + count, + hasMore + }; +}; + +export default ListQueueIntegrationService; \ No newline at end of file diff --git a/backend/src/services/QueueIntegrationServices/ShowQueueIntegrationService.ts b/backend/src/services/QueueIntegrationServices/ShowQueueIntegrationService.ts new file mode 100644 index 0000000..11d37b1 --- /dev/null +++ b/backend/src/services/QueueIntegrationServices/ShowQueueIntegrationService.ts @@ -0,0 +1,19 @@ +import QueueIntegrations from "../../models/QueueIntegrations"; +import AppError from "../../errors/AppError"; + + +const ShowQueueIntegrationService = async (id: string | number, companyId: number): Promise => { + const integration = await QueueIntegrations.findByPk(id); + + // if (Number(integration?.companyId) !== Number(companyId)) { + // throw new AppError("Não é possível excluir registro de outra empresa"); + // } + + if (!integration) { + throw new AppError("ERR_NO_DIALOG_FOUND", 404); + } + + return integration; +}; + +export default ShowQueueIntegrationService; \ No newline at end of file diff --git a/backend/src/services/QueueIntegrationServices/UpdateQueueIntegrationService.ts b/backend/src/services/QueueIntegrationServices/UpdateQueueIntegrationService.ts new file mode 100644 index 0000000..b973fc8 --- /dev/null +++ b/backend/src/services/QueueIntegrationServices/UpdateQueueIntegrationService.ts @@ -0,0 +1,83 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import QueueIntegrations from "../../models/QueueIntegrations"; +import ShowIntegrationService from "./ShowQueueIntegrationService"; + +interface IntegrationData { + type?: string; + name?: string; + projectName?: string; + jsonContent?: string; + language?: string; + urlN8N?: string; + typebotSlug?: string; + typebotExpires?: number; + typebotKeywordFinish?: string; + typebotUnknownMessage?: string; + typebotDelayMessage?: number; + typebotKeywordRestart?: string; + typebotRestartMessage?: string; +} + +interface Request { + integrationData: IntegrationData; + integrationId: string; + companyId: number; +} + +const UpdateQueueIntegrationService = async ({ + integrationData, + integrationId, + companyId +}: Request): Promise => { + const schema = Yup.object().shape({ + type: Yup.string().min(2), + name: Yup.string().min(2) + }); + + const { + type, + name, + projectName, + jsonContent, + language, + urlN8N, + typebotExpires, + typebotKeywordFinish, + typebotSlug, + typebotUnknownMessage, + typebotDelayMessage, + typebotKeywordRestart, + typebotRestartMessage + } = integrationData; + + try { + await schema.validate({ type, name, projectName, jsonContent, language, urlN8N }); + } catch (err) { + throw new AppError(err.message); + } + + const integration = await ShowIntegrationService(integrationId, companyId); + + await integration.update({ + type, + name, + projectName, + jsonContent, + language, + urlN8N, + companyId, + typebotExpires, + typebotKeywordFinish, + typebotSlug, + typebotUnknownMessage, + typebotDelayMessage, + typebotKeywordRestart, + typebotRestartMessage + }); + + return integration; +}; + +export default UpdateQueueIntegrationService; \ No newline at end of file diff --git a/backend/src/services/QueueOptionService/CreateService.ts b/backend/src/services/QueueOptionService/CreateService.ts new file mode 100644 index 0000000..6027841 --- /dev/null +++ b/backend/src/services/QueueOptionService/CreateService.ts @@ -0,0 +1,16 @@ +import QueueOption from "../../models/QueueOption"; + +interface QueueOptionData { + queueId: string; + title: string; + option: string; + message?: string; + parentId?: string; +} + +const CreateService = async (queueOptionData: QueueOptionData): Promise => { + const queueOption = await QueueOption.create(queueOptionData); + return queueOption; +}; + +export default CreateService; diff --git a/backend/src/services/QueueOptionService/DeleteService.ts b/backend/src/services/QueueOptionService/DeleteService.ts new file mode 100644 index 0000000..07a506e --- /dev/null +++ b/backend/src/services/QueueOptionService/DeleteService.ts @@ -0,0 +1,9 @@ +import ShowService from "./ShowService"; + +const DeleteService = async (queueOptionId: number | string): Promise => { + const queueOption = await ShowService(queueOptionId); + + await queueOption.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/QueueOptionService/ListService.ts b/backend/src/services/QueueOptionService/ListService.ts new file mode 100644 index 0000000..a2cc9b1 --- /dev/null +++ b/backend/src/services/QueueOptionService/ListService.ts @@ -0,0 +1,38 @@ +import { WhereOptions } from "sequelize/types"; +import QueueOption from "../../models/QueueOption"; + +type QueueOptionFilter = { + queueId: string | number; + queueOptionId: string | number; + parentId: string | number | boolean; +}; + +const ListService = async ({ queueId, queueOptionId, parentId }: QueueOptionFilter): Promise => { + + const whereOptions: WhereOptions = {}; + + if (queueId) { + whereOptions.queueId = queueId; + } + + if (queueOptionId) { + whereOptions.id = queueOptionId; + } + + if (parentId == -1) { + whereOptions.parentId = null; + } + + if (parentId > 0) { + whereOptions.parentId = parentId; + } + + const queueOptions = await QueueOption.findAll({ + where: whereOptions, + order: [["id", "ASC"]] + }); + + return queueOptions; +}; + +export default ListService; diff --git a/backend/src/services/QueueOptionService/ShowService.ts b/backend/src/services/QueueOptionService/ShowService.ts new file mode 100644 index 0000000..bbb24a0 --- /dev/null +++ b/backend/src/services/QueueOptionService/ShowService.ts @@ -0,0 +1,26 @@ +import AppError from "../../errors/AppError"; +import QueueOption from "../../models/QueueOption"; + +const ShowService = async (queueOptionId: number | string): Promise => { + const queue = await QueueOption.findOne({ + where: { + id: queueOptionId + }, + include: [ + { + model: QueueOption, + as: 'parent', + where: { parentId: queueOptionId }, + required: false + }, + ] + }); + + if (!queue) { + throw new AppError("ERR_QUEUE_NOT_FOUND"); + } + + return queue; +}; + +export default ShowService; diff --git a/backend/src/services/QueueOptionService/UpdateService.ts b/backend/src/services/QueueOptionService/UpdateService.ts new file mode 100644 index 0000000..f288f80 --- /dev/null +++ b/backend/src/services/QueueOptionService/UpdateService.ts @@ -0,0 +1,24 @@ +import QueueOption from "../../models/QueueOption"; +import ShowService from "./ShowService"; + +interface QueueData { + queueId?: string; + title?: string; + option?: string; + message?: string; + parentId?: string; +} + +const UpdateService = async ( + queueOptionId: number | string, + queueOptionData: QueueData +): Promise => { + + const queueOption = await ShowService(queueOptionId); + + await queueOption.update(queueOptionData); + + return queueOption; +}; + +export default UpdateService; diff --git a/backend/src/services/QueueService/CreateQueueService.ts b/backend/src/services/QueueService/CreateQueueService.ts new file mode 100644 index 0000000..b428c83 --- /dev/null +++ b/backend/src/services/QueueService/CreateQueueService.ts @@ -0,0 +1,94 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; +import Company from "../../models/Company"; +import Plan from "../../models/Plan"; + +interface QueueData { + name: string; + color: string; + companyId: number; + greetingMessage?: string; + outOfHoursMessage?: string; + schedules?: any[]; + orderQueue?: number; + integrationId?: number; + promptId?: number; +} + +const CreateQueueService = async (queueData: QueueData): Promise => { + const { color, name, companyId } = queueData; + + const company = await Company.findOne({ + where: { + id: companyId + }, + include: [{ model: Plan, as: "plan" }] + }); + + if (company !== null) { + const queuesCount = await Queue.count({ + where: { + companyId + } + }); + + if (queuesCount >= company.plan.queues) { + throw new AppError(`Número máximo de filas já alcançado: ${queuesCount}`); + } + } + + const queueSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "ERR_QUEUE_INVALID_NAME") + .required("ERR_QUEUE_INVALID_NAME") + .test( + "Check-unique-name", + "ERR_QUEUE_NAME_ALREADY_EXISTS", + async value => { + if (value) { + const queueWithSameName = await Queue.findOne({ + where: { name: value, companyId } + }); + + return !queueWithSameName; + } + return false; + } + ), + color: Yup.string() + .required("ERR_QUEUE_INVALID_COLOR") + .test("Check-color", "ERR_QUEUE_INVALID_COLOR", async value => { + if (value) { + const colorTestRegex = /^#[0-9a-f]{3,6}$/i; + return colorTestRegex.test(value); + } + return false; + }) + .test( + "Check-color-exists", + "ERR_QUEUE_COLOR_ALREADY_EXISTS", + async value => { + if (value) { + const queueWithSameColor = await Queue.findOne({ + where: { color: value, companyId } + }); + return !queueWithSameColor; + } + return false; + } + ) + }); + + try { + await queueSchema.validate({ color, name }); + } catch (err: any) { + throw new AppError(err.message); + } + + const queue = await Queue.create(queueData); + + return queue; +}; + +export default CreateQueueService; diff --git a/backend/src/services/QueueService/DeleteQueueService.ts b/backend/src/services/QueueService/DeleteQueueService.ts new file mode 100644 index 0000000..3a04c3e --- /dev/null +++ b/backend/src/services/QueueService/DeleteQueueService.ts @@ -0,0 +1,12 @@ +import ShowQueueService from "./ShowQueueService"; + +const DeleteQueueService = async ( + queueId: number | string, + companyId: number +): Promise => { + const queue = await ShowQueueService(queueId, companyId); + + await queue.destroy(); +}; + +export default DeleteQueueService; diff --git a/backend/src/services/QueueService/ListQueuesService.ts b/backend/src/services/QueueService/ListQueuesService.ts new file mode 100644 index 0000000..b092209 --- /dev/null +++ b/backend/src/services/QueueService/ListQueuesService.ts @@ -0,0 +1,18 @@ +import Queue from "../../models/Queue"; + +interface Request { + companyId: number; +} + +const ListQueuesService = async ({ companyId }: Request): Promise => { + const queues = await Queue.findAll({ + where: { + companyId + }, + order: [["orderQueue", "ASC"]] + }); + + return queues; +}; + +export default ListQueuesService; diff --git a/backend/src/services/QueueService/ShowQueueService.ts b/backend/src/services/QueueService/ShowQueueService.ts new file mode 100644 index 0000000..3c3329d --- /dev/null +++ b/backend/src/services/QueueService/ShowQueueService.ts @@ -0,0 +1,21 @@ +import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; + +const ShowQueueService = async ( + queueId: number | string, + companyId: number +): Promise => { + const queue = await Queue.findByPk(queueId); + + if (queue?.companyId !== companyId) { + throw new AppError("Não é possível consultar registros de outra empresa"); + } + + if (!queue) { + throw new AppError("ERR_QUEUE_NOT_FOUND"); + } + + return queue; +}; + +export default ShowQueueService; diff --git a/backend/src/services/QueueService/UpdateQueueService.ts b/backend/src/services/QueueService/UpdateQueueService.ts new file mode 100644 index 0000000..50028e8 --- /dev/null +++ b/backend/src/services/QueueService/UpdateQueueService.ts @@ -0,0 +1,83 @@ +import { Op } from "sequelize"; +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; +import ShowQueueService from "./ShowQueueService"; + +interface QueueData { + name?: string; + color?: string; + greetingMessage?: string; + outOfHoursMessage?: string; + schedules?: any[]; + orderQueue?: number; + integrationId?: number; + promptId?: number; +} + +const UpdateQueueService = async ( + queueId: number | string, + queueData: QueueData, + companyId: number +): Promise => { + const { color, name } = queueData; + + const queueSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "ERR_QUEUE_INVALID_NAME") + .test( + "Check-unique-name", + "ERR_QUEUE_NAME_ALREADY_EXISTS", + async value => { + if (value) { + const queueWithSameName = await Queue.findOne({ + where: { name: value, id: { [Op.ne]: queueId }, companyId } + }); + + return !queueWithSameName; + } + return true; + } + ), + color: Yup.string() + .required("ERR_QUEUE_INVALID_COLOR") + .test("Check-color", "ERR_QUEUE_INVALID_COLOR", async value => { + if (value) { + const colorTestRegex = /^#[0-9a-f]{3,6}$/i; + return colorTestRegex.test(value); + } + return true; + }) + .test( + "Check-color-exists", + "ERR_QUEUE_COLOR_ALREADY_EXISTS", + async value => { + if (value) { + const queueWithSameColor = await Queue.findOne({ + where: { color: value, id: { [Op.ne]: queueId }, companyId } + }); + return !queueWithSameColor; + } + return true; + } + ) + }); + + try { + await queueSchema.validate({ color, name }); + } catch (err: any) { + throw new AppError(err.message); + } + + const queue = await ShowQueueService(queueId, companyId); + + if (queue.companyId !== companyId) { + throw new AppError("Não é permitido alterar registros de outra empresa"); + } + + await queue.update(queueData); + + return queue; +}; + +export default UpdateQueueService; diff --git a/backend/src/services/QuickMessageService/CreateService.ts b/backend/src/services/QuickMessageService/CreateService.ts new file mode 100644 index 0000000..94eb198 --- /dev/null +++ b/backend/src/services/QuickMessageService/CreateService.ts @@ -0,0 +1,35 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import QuickMessage from "../../models/QuickMessage"; + +interface Data { + shortcode: string; + message: string; + companyId: number | string; + userId: number | string; +} + +const CreateService = async (data: Data): Promise => { + const { shortcode, message } = data; + + const ticketnoteSchema = Yup.object().shape({ + shortcode: Yup.string() + .min(3, "ERR_QUICKMESSAGE_INVALID_NAME") + .required("ERR_QUICKMESSAGE_REQUIRED"), + message: Yup.string() + .min(3, "ERR_QUICKMESSAGE_INVALID_NAME") + .required("ERR_QUICKMESSAGE_REQUIRED") + }); + + try { + await ticketnoteSchema.validate({ shortcode, message }); + } catch (err: any) { + throw new AppError(err.message); + } + + const record = await QuickMessage.create(data); + + return record; +}; + +export default CreateService; diff --git a/backend/src/services/QuickMessageService/DeleteService.ts b/backend/src/services/QuickMessageService/DeleteService.ts new file mode 100644 index 0000000..e2d09bf --- /dev/null +++ b/backend/src/services/QuickMessageService/DeleteService.ts @@ -0,0 +1,16 @@ +import QuickMessage from "../../models/QuickMessage"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string): Promise => { + const record = await QuickMessage.findOne({ + where: { id } + }); + + if (!record) { + throw new AppError("ERR_NO_QUICKMESSAGE_FOUND", 404); + } + + await record.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/QuickMessageService/FindAllService.ts b/backend/src/services/QuickMessageService/FindAllService.ts new file mode 100644 index 0000000..e461f72 --- /dev/null +++ b/backend/src/services/QuickMessageService/FindAllService.ts @@ -0,0 +1,10 @@ +import QuickMessage from "../../models/QuickMessage"; + +const FindAllService = async (): Promise => { + const records: QuickMessage[] = await QuickMessage.findAll({ + order: [["shortcode", "ASC"]] + }); + return records; +}; + +export default FindAllService; diff --git a/backend/src/services/QuickMessageService/FindService.ts b/backend/src/services/QuickMessageService/FindService.ts new file mode 100644 index 0000000..4866ac1 --- /dev/null +++ b/backend/src/services/QuickMessageService/FindService.ts @@ -0,0 +1,23 @@ +import { Op } from "sequelize"; +import QuickMessage from "../../models/QuickMessage"; +import Company from "../../models/Company"; + +type Params = { + companyId: string; + userId: string; +}; + +const FindService = async ({ companyId, userId }: Params): Promise => { + const notes: QuickMessage[] = await QuickMessage.findAll({ + where: { + companyId, + userId, + }, + include: [{ model: Company, as: "company", attributes: ["id", "name"] }], + order: [["shortcode", "ASC"]] + }); + + return notes; +}; + +export default FindService; diff --git a/backend/src/services/QuickMessageService/ListService.ts b/backend/src/services/QuickMessageService/ListService.ts new file mode 100644 index 0000000..89fb973 --- /dev/null +++ b/backend/src/services/QuickMessageService/ListService.ts @@ -0,0 +1,67 @@ +import { Sequelize, Op, Filterable } from "sequelize"; +import QuickMessage from "../../models/QuickMessage"; + +interface Request { + searchParam?: string; + pageNumber?: string; + companyId: number | string; + userId?: number | string; +} + +interface Response { + records: QuickMessage[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam = "", + pageNumber = "1", + companyId, + userId +}: Request): Promise => { + const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim(); + + let whereCondition: Filterable["where"] = { + // [Op.or]: [ + // { + shortcode: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("shortcode")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + // }, + // { + // message: Sequelize.where( + // Sequelize.fn("LOWER", Sequelize.col("message")), + // "LIKE", + // `%${sanitizedSearchParam}%` + // ) + // } + // ] + }; + whereCondition = { + ...whereCondition, + companyId, + userId: userId + } + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: records } = await QuickMessage.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["shortcode", "ASC"]] + }); + + const hasMore = count > offset + records.length; + + return { + records, + count, + hasMore + }; +}; + +export default ListService; \ No newline at end of file diff --git a/backend/src/services/QuickMessageService/ShowService.ts b/backend/src/services/QuickMessageService/ShowService.ts new file mode 100644 index 0000000..a12282c --- /dev/null +++ b/backend/src/services/QuickMessageService/ShowService.ts @@ -0,0 +1,14 @@ +import QuickMessage from "../../models/QuickMessage"; +import AppError from "../../errors/AppError"; + +const ShowService = async (id: string | number): Promise => { + const record = await QuickMessage.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + return record; +}; + +export default ShowService; diff --git a/backend/src/services/QuickMessageService/UpdateService.ts b/backend/src/services/QuickMessageService/UpdateService.ts new file mode 100644 index 0000000..e8a4e8f --- /dev/null +++ b/backend/src/services/QuickMessageService/UpdateService.ts @@ -0,0 +1,29 @@ +import AppError from "../../errors/AppError"; +import QuickMessage from "../../models/QuickMessage"; + +interface Data { + shortcode: string; + message: string; + userId: number | string; + id?: number | string; +} + +const UpdateService = async (data: Data): Promise => { + const { id, shortcode, message, userId } = data; + + const record = await QuickMessage.findByPk(id); + + if (!record) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + await record.update({ + shortcode, + message, + userId + }); + + return record; +}; + +export default UpdateService; diff --git a/backend/src/services/ReportService/DashbardDataService.ts b/backend/src/services/ReportService/DashbardDataService.ts new file mode 100644 index 0000000..378ee00 --- /dev/null +++ b/backend/src/services/ReportService/DashbardDataService.ts @@ -0,0 +1,144 @@ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable camelcase */ +import { QueryTypes } from "sequelize"; +import * as _ from "lodash"; +import sequelize from "../../database"; + +export interface DashboardData { + counters: any; + attendants: []; +} + +export interface Params { + days?: number; + date_from?: string; + date_to?: string; +} + +export default async function DashboardDataService( + companyId: string | number, + params: Params +): Promise { + const query = ` + with + traking as ( + select + c.name "companyName", + u.name "userName", + u.online "userOnline", + w.name "whatsappName", + ct.name "contactName", + ct.number "contactNumber", + (tt."finishedAt" is not null) "finished", + (tt."userId" is null and tt."finishedAt" is null) "pending", + coalesce(( + (date_part('day', age(coalesce(tt."ratingAt", tt."finishedAt") , tt."startedAt")) * 24 * 60) + + (date_part('hour', age(coalesce(tt."ratingAt", tt."finishedAt"), tt."startedAt")) * 60) + + (date_part('minutes', age(coalesce(tt."ratingAt", tt."finishedAt"), tt."startedAt"))) + ), 0) "supportTime", + coalesce(( + (date_part('day', age(tt."startedAt", tt."queuedAt")) * 24 * 60) + + (date_part('hour', age(tt."startedAt", tt."queuedAt")) * 60) + + (date_part('minutes', age(tt."startedAt", tt."queuedAt"))) + ), 0) "waitTime", + t.status, + tt.*, + ct."id" "contactId" + from "TicketTraking" tt + left join "Companies" c on c.id = tt."companyId" + left join "Users" u on u.id = tt."userId" + left join "Whatsapps" w on w.id = tt."whatsappId" + left join "Tickets" t on t.id = tt."ticketId" + left join "Contacts" ct on ct.id = t."contactId" + -- filterPeriod + ), + counters as ( + select + (select avg("supportTime") from traking where "supportTime" > 0) "avgSupportTime", + (select avg("waitTime") from traking where "waitTime" > 0) "avgWaitTime", + ( + select count(distinct "id") + from "Tickets" + where status like 'open' and "companyId" = ? + ) "supportHappening", + ( + select count(distinct "id") + from "Tickets" + where status like 'pending' and "companyId" = ? + ) "supportPending", + (select count(id) from traking where finished) "supportFinished", + ( + select count(leads.id) from ( + select + ct1.id, + count(tt1.id) total + from traking tt1 + left join "Tickets" t1 on t1.id = tt1."ticketId" + left join "Contacts" ct1 on ct1.id = t1."contactId" + group by 1 + having count(tt1.id) = 1 + ) leads + ) "leads" + ), + attedants as ( + select + u.id, + u.name, + coalesce(att."avgSupportTime", 0) "avgSupportTime", + att.tickets, + att.rating, + att.online + from "Users" u + left join ( + select + u1.id, + u1."name", + u1."online", + avg(t."supportTime") "avgSupportTime", + count(t."id") tickets, + coalesce(avg(ur.rate), 0) rating + from "Users" u1 + left join traking t on t."userId" = u1.id + left join "UserRatings" ur on ur."userId" = t."userId" and ur."createdAt"::date = t."finishedAt"::date + group by 1, 2 + ) att on att.id = u.id + where u."companyId" = ? + order by att.name + ) + select + (select coalesce(jsonb_build_object('counters', c.*)->>'counters', '{}')::jsonb from counters c) counters, + (select coalesce(json_agg(a.*), '[]')::jsonb from attedants a) attendants; + `; + + let where = 'where tt."companyId" = ?'; + const replacements: any[] = [companyId]; + + if (_.has(params, "days")) { + where += ` and tt."queuedAt" >= (now() - '? days'::interval)`; + replacements.push(parseInt(`${params.days}`.replace(/\D/g, ""), 10)); + } + + if (_.has(params, "date_from")) { + where += ` and tt."queuedAt" >= ?`; + replacements.push(`${params.date_from} 00:00:00`); + } + + if (_.has(params, "date_to")) { + where += ` and tt."finishedAt" <= ?`; + replacements.push(`${params.date_to} 23:59:59`); + } + + replacements.push(companyId); + replacements.push(companyId); + replacements.push(companyId); + + const finalQuery = query.replace("-- filterPeriod", where); + + const responseData: DashboardData = await sequelize.query(finalQuery, { + replacements, + type: QueryTypes.SELECT, + plain: true + }); + + return responseData; +} diff --git a/backend/src/services/ReportService/TicketsAttendance.ts b/backend/src/services/ReportService/TicketsAttendance.ts new file mode 100644 index 0000000..ecafcaa --- /dev/null +++ b/backend/src/services/ReportService/TicketsAttendance.ts @@ -0,0 +1,60 @@ +import sequelize from "../../database/index"; +import { QueryTypes } from "sequelize"; + +interface Return { + data: {}; +} + +interface Request { + initialDate: string; + finalDate: string; + companyId: number; +} + +interface DataReturn { + quantidade: number; + data?: number; + nome?: string; +} + +interface dataUser { + name: string; +} + +export const TicketsAttendance = async ({ initialDate, finalDate, companyId }: Request): Promise => { + + const sqlUsers = `select u.name from "Users" u where u."companyId" = ${companyId}` + + const users: dataUser[] = await sequelize.query(sqlUsers, { type: QueryTypes.SELECT }); + + const sql = ` + select + COUNT(*) AS quantidade, + u.name AS nome + from + "TicketTraking" tt + left join "Users" u on u.id = tt."userId" + where + tt."companyId" = ${companyId} + and "ticketId" is not null + and tt."userId" is not null + and tt."finishedAt" >= '${initialDate} 00:00:00' + and tt."finishedAt" <= '${finalDate} 23:59:59' + group by + nome + ORDER BY + nome asc` + + const data: DataReturn[] = await sequelize.query(sql, { type: QueryTypes.SELECT }); + + users.map(user => { + let indexCreated = data.findIndex((item) => item.nome === user.name); + + if (indexCreated === -1) { + data.push({ quantidade: 0, nome: user.name }) + } + + }) + + return { data }; +} diff --git a/backend/src/services/ReportService/TicketsDayService.ts b/backend/src/services/ReportService/TicketsDayService.ts new file mode 100644 index 0000000..d501e87 --- /dev/null +++ b/backend/src/services/ReportService/TicketsDayService.ts @@ -0,0 +1,70 @@ +import sequelize from "../../database/index"; +import { QueryTypes } from "sequelize"; + +interface Return { + data: {}; + count: number; +} + +interface Request { + initialDate: string; + finalDate: string; + companyId: number; +} + +interface DataReturn { + total: number; + data?: number; + horario?: string; +} + +export const TicketsDayService = async ({ initialDate, finalDate, companyId }: Request): Promise => { + + let sql = ''; + let count = 0; + + if (initialDate && initialDate.trim() === finalDate && finalDate.trim()) { + sql = ` + SELECT + COUNT(*) AS total, + extract(hour from tick."createdAt") AS horario + --to_char(DATE(tick."createdAt"), 'dd-mm-YYYY') as horario + FROM + "TicketTraking" tick + WHERE + tick."companyId" = ${companyId} + and DATE(tick."createdAt") >= '${initialDate} 00:00:00' + AND DATE(tick."createdAt") <= '${finalDate} 23:59:59' + GROUP BY + extract(hour from tick."createdAt") + --to_char(DATE(tick."createdAt"), 'dd-mm-YYYY') + ORDER BY + horario asc; + ` + } else { + sql = ` + SELECT + COUNT(*) AS total, + to_char(DATE(tick."createdAt"), 'dd/mm/YYYY') as data + FROM + "TicketTraking" tick + WHERE + tick."companyId" = ${companyId} + and DATE(tick."createdAt") >= '${initialDate}' + AND DATE(tick."createdAt") <= '${finalDate}' + GROUP BY + to_char(DATE(tick."createdAt"), 'dd/mm/YYYY') + ORDER BY + data asc; + ` + } + + const data: DataReturn[] = await sequelize.query(sql, { type: QueryTypes.SELECT }); + + data.forEach((register) => { + count += Number(register.total); + }) + + return { data, count }; + +} diff --git a/backend/src/services/ResetPasswordService/ResetPassword.ts b/backend/src/services/ResetPasswordService/ResetPassword.ts new file mode 100644 index 0000000..071db69 --- /dev/null +++ b/backend/src/services/ResetPasswordService/ResetPassword.ts @@ -0,0 +1,51 @@ +import sequelize from "sequelize"; +import database from "../../database"; +import { hash } from "bcryptjs"; +const ResetPassword = async ( + email: string, + token: string, + password: string +) => { + const { hasResult, data } = await filterUser(email, token); + if (!hasResult) { + return { status: 404, message: "Email não encontrado" }; + } + if (hasResult === true) { + try { + const convertPassword: string = await hash(password, 8); + const { hasResults, datas } = await insertHasPassword( + email, + token, + convertPassword + ); + if (datas.length === 0) { + return { status: 404, message: "Token não encontrado" }; + } + } catch (err) { + console.log(err); + } + } +}; +export default ResetPassword; +const filterUser = async (email: string, token: string) => { + const sql = `SELECT * FROM "Users" WHERE email = '${email}' AND "resetPassword" != ''`; + const result = await database.query(sql, { + type: sequelize.QueryTypes.SELECT + }); + return { hasResult: result.length > 0, data: result }; +}; +const insertHasPassword = async ( + email: string, + token: string, + convertPassword: string +) => { + const sqlValida = `SELECT * FROM "Users" WHERE email = '${email}' AND "resetPassword" = '${token}'`; + const resultado = await database.query(sqlValida, { + type: sequelize.QueryTypes.SELECT + }); + const sqls = `UPDATE "Users" SET "passwordHash"= '${convertPassword}' , "resetPassword" = '' WHERE email= '${email}' AND "resetPassword" = '${token}'`; + const results = await database.query(sqls, { + type: sequelize.QueryTypes.UPDATE + }); + return { hasResults: results.length > 0, datas: resultado }; +}; diff --git a/backend/src/services/ResetPasswordService/ResetPassword.ts.bak b/backend/src/services/ResetPasswordService/ResetPassword.ts.bak new file mode 100644 index 0000000..411f45e --- /dev/null +++ b/backend/src/services/ResetPasswordService/ResetPassword.ts.bak @@ -0,0 +1,48 @@ +import sequelize from "sequelize"; +import database from "../../database"; +import { hash } from "bcryptjs"; + +const ResetPassword = async (email: string ,token: string, password: string) => { + + const {hasResult , data} = await filterUser(email, token); + + if (!hasResult) { + return { status: 404, message: "Email não encontrado" }; + } + + if(hasResult === true){ + try{ + const convertPassword: string= await hash(password,8) + + const {hasResults , datas} = await insertHasPassword(email, token ,convertPassword); + + if (datas.length === 0){ + return { status: 404, message: "Token não encontrado" }; + } + + }catch(err){ + console.log(err) + } + } + +} +export default ResetPassword; + +const filterUser = async (email : string , token: string)=>{ + const sql = `SELECT * FROM "Users" WHERE email = '${email}' AND "resetPassword" != ''`; + const result = await database.query(sql, { type: sequelize.QueryTypes.SELECT }); + return { hasResult: result.length > 0, data: result }; +} +const insertHasPassword = async (email : string , token: string, convertPassword: string)=>{ + + + const sqlValida = `SELECT * FROM "Users" WHERE email = '${email}' AND "resetPassword" = '${token}'`; + const resultado = await database.query(sqlValida, { type: sequelize.QueryTypes.SELECT }); + + + const sqls = `UPDATE "Users" SET "passwordHash"= '${convertPassword}' , "resetPassword" = '' WHERE email= '${email}' AND "resetPassword" = '${token}'`; + const results = await database.query(sqls, { type: sequelize.QueryTypes.UPDATE }); + + + return { hasResults: results.length > 0, datas: resultado}; +} \ No newline at end of file diff --git a/backend/src/services/ScheduleServices/CreateService.ts b/backend/src/services/ScheduleServices/CreateService.ts new file mode 100644 index 0000000..0454976 --- /dev/null +++ b/backend/src/services/ScheduleServices/CreateService.ts @@ -0,0 +1,48 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Schedule from "../../models/Schedule"; + +interface Request { + body: string; + sendAt: string; + contactId: number | string; + companyId: number | string; + userId?: number | string; +} + +const CreateService = async ({ + body, + sendAt, + contactId, + companyId, + userId +}: Request): Promise => { + const schema = Yup.object().shape({ + body: Yup.string().required().min(5), + sendAt: Yup.string().required() + }); + + try { + await schema.validate({ body, sendAt }); + } catch (err: any) { + throw new AppError(err.message); + } + + const schedule = await Schedule.create( + { + body, + sendAt, + contactId, + companyId, + userId, + status: 'PENDENTE' + } + ); + + await schedule.reload(); + + return schedule; +}; + +export default CreateService; diff --git a/backend/src/services/ScheduleServices/DeleteService.ts b/backend/src/services/ScheduleServices/DeleteService.ts new file mode 100644 index 0000000..bdda0c2 --- /dev/null +++ b/backend/src/services/ScheduleServices/DeleteService.ts @@ -0,0 +1,16 @@ +import Schedule from "../../models/Schedule"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string | number, companyId: number): Promise => { + const schedule = await Schedule.findOne({ + where: { id, companyId } + }); + + if (!schedule) { + throw new AppError("ERR_NO_SCHEDULE_FOUND", 404); + } + + await schedule.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/ScheduleServices/ListService.ts b/backend/src/services/ScheduleServices/ListService.ts new file mode 100644 index 0000000..14b193b --- /dev/null +++ b/backend/src/services/ScheduleServices/ListService.ts @@ -0,0 +1,93 @@ +import { Op, Sequelize } from "sequelize"; +import Contact from "../../models/Contact"; +import Schedule from "../../models/Schedule"; +import User from "../../models/User"; + +interface Request { + searchParam?: string; + contactId?: number | string; + userId?: number | string; + companyId?: number; + pageNumber?: string | number; +} + +interface Response { + schedules: Schedule[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + searchParam, + contactId = "", + userId = "", + pageNumber = "1", + companyId +}: Request): Promise => { + let whereCondition = {}; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + if (searchParam) { + whereCondition = { + [Op.or]: [ + { + "$Schedule.body$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("Schedule.body")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + { + "$Contact.name$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("contact.name")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + ], + } + } + + if (contactId !== "") { + whereCondition = { + ...whereCondition, + contactId + } + } + + if (userId !== "") { + whereCondition = { + ...whereCondition, + userId + } + } + + whereCondition = { + ...whereCondition, + companyId: { + [Op.eq]: companyId + } + } + + const { count, rows: schedules } = await Schedule.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]], + include: [ + { model: Contact, as: "contact", attributes: ["id", "name"] }, + { model: User, as: "user", attributes: ["id", "name"] }, + ] + }); + + const hasMore = count > offset + schedules.length; + + return { + schedules, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/ScheduleServices/ShowService.ts b/backend/src/services/ScheduleServices/ShowService.ts new file mode 100644 index 0000000..6237703 --- /dev/null +++ b/backend/src/services/ScheduleServices/ShowService.ts @@ -0,0 +1,25 @@ +import Schedule from "../../models/Schedule"; +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import User from "../../models/User"; + +const ScheduleService = async (id: string | number, companyId: number): Promise => { + const schedule = await Schedule.findByPk(id, { + include: [ + { model: Contact, as: "contact", attributes: ["id", "name"] }, + { model: User, as: "user", attributes: ["id", "name"] }, + ] + }); + + if (schedule?.companyId !== companyId) { + throw new AppError("Não é possível excluir registro de outra empresa"); + } + + if (!schedule) { + throw new AppError("ERR_NO_SCHEDULE_FOUND", 404); + } + + return schedule; +}; + +export default ScheduleService; diff --git a/backend/src/services/ScheduleServices/UpdateService.ts b/backend/src/services/ScheduleServices/UpdateService.ts new file mode 100644 index 0000000..649ca5c --- /dev/null +++ b/backend/src/services/ScheduleServices/UpdateService.ts @@ -0,0 +1,67 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Schedule from "../../models/Schedule"; +import ShowService from "./ShowService"; + +interface ScheduleData { + id?: number; + body?: string; + sendAt?: string; + sentAt?: string; + contactId?: number; + companyId?: number; + ticketId?: number; + userId?: number; +} + +interface Request { + scheduleData: ScheduleData; + id: string | number; + companyId: number; +} + +const UpdateUserService = async ({ + scheduleData, + id, + companyId +}: Request): Promise => { + const schedule = await ShowService(id, companyId); + + if (schedule?.companyId !== companyId) { + throw new AppError("Não é possível alterar registros de outra empresa"); + } + + const schema = Yup.object().shape({ + body: Yup.string().min(5) + }); + + const { + body, + sendAt, + sentAt, + contactId, + ticketId, + userId, + } = scheduleData; + + try { + await schema.validate({ body }); + } catch (err: any) { + throw new AppError(err.message); + } + + await schedule.update({ + body, + sendAt, + sentAt, + contactId, + ticketId, + userId, + }); + + await schedule.reload(); + return schedule; +}; + +export default UpdateUserService; diff --git a/backend/src/services/SettingServices/ListSettingsService.ts b/backend/src/services/SettingServices/ListSettingsService.ts new file mode 100644 index 0000000..19853e3 --- /dev/null +++ b/backend/src/services/SettingServices/ListSettingsService.ts @@ -0,0 +1,19 @@ +import Setting from "../../models/Setting"; + +interface Request { + companyId: number; +} + +const ListSettingsService = async ({ + companyId +}: Request): Promise => { + const settings = await Setting.findAll({ + where: { + companyId + } + }); + + return settings; +}; + +export default ListSettingsService; diff --git a/backend/src/services/SettingServices/ListSettingsServiceOne.ts b/backend/src/services/SettingServices/ListSettingsServiceOne.ts new file mode 100644 index 0000000..4d64221 --- /dev/null +++ b/backend/src/services/SettingServices/ListSettingsServiceOne.ts @@ -0,0 +1,22 @@ +import Setting from "../../models/Setting"; + +interface Request { + companyId: number; + key?: string; +} + +const ListSettingsServiceOne = async ({ + companyId, + key +}: Request): Promise => { + const setting = await Setting.findOne({ + where: { + companyId, + ...(key && { key }) + } + }); + + return setting; +}; + +export default ListSettingsServiceOne; \ No newline at end of file diff --git a/backend/src/services/SettingServices/UpdateSettingService.ts b/backend/src/services/SettingServices/UpdateSettingService.ts new file mode 100644 index 0000000..af05567 --- /dev/null +++ b/backend/src/services/SettingServices/UpdateSettingService.ts @@ -0,0 +1,40 @@ +import AppError from "../../errors/AppError"; +import Setting from "../../models/Setting"; + +interface Request { + key: string; + value: string; + companyId: number; +} + +const UpdateSettingService = async ({ + key, + value, + companyId +}: Request): Promise => { + const [setting] = await Setting.findOrCreate({ + where: { + key, + companyId + }, + defaults: { + key, + value, + companyId + } + }); + + if (setting != null && setting?.companyId !== companyId) { + throw new AppError("Não é possível consultar registros de outra empresa"); + } + + if (!setting) { + throw new AppError("ERR_NO_SETTING_FOUND", 404); + } + + await setting.update({ value }); + + return setting; +}; + +export default UpdateSettingService; diff --git a/backend/src/services/TagServices/CreateService.ts b/backend/src/services/TagServices/CreateService.ts new file mode 100644 index 0000000..45527a4 --- /dev/null +++ b/backend/src/services/TagServices/CreateService.ts @@ -0,0 +1,39 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Tag from "../../models/Tag"; + +interface Request { + name: string; + color: string; + kanban: number; + companyId: number; +} + +const CreateService = async ({ + name, + color = "#A4CCCC", + kanban = 0, + companyId +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string().required().min(3) + }); + + try { + await schema.validate({ name }); + } catch (err: any) { + throw new AppError(err.message); + } + + const [tag] = await Tag.findOrCreate({ + where: { name, color, companyId, kanban }, + defaults: { name, color, companyId, kanban } + }); + + await tag.reload(); + + return tag; +}; + +export default CreateService; diff --git a/backend/src/services/TagServices/DeleteService.ts b/backend/src/services/TagServices/DeleteService.ts new file mode 100644 index 0000000..9d81b7b --- /dev/null +++ b/backend/src/services/TagServices/DeleteService.ts @@ -0,0 +1,16 @@ +import Tag from "../../models/Tag"; +import AppError from "../../errors/AppError"; + +const DeleteService = async (id: string | number): Promise => { + const tag = await Tag.findOne({ + where: { id } + }); + + if (!tag) { + throw new AppError("ERR_NO_TAG_FOUND", 404); + } + + await tag.destroy(); +}; + +export default DeleteService; diff --git a/backend/src/services/TagServices/KanbanListService.ts b/backend/src/services/TagServices/KanbanListService.ts new file mode 100644 index 0000000..f999a41 --- /dev/null +++ b/backend/src/services/TagServices/KanbanListService.ts @@ -0,0 +1,24 @@ +import { Op } from "sequelize"; +import Tag from "../../models/Tag"; +import Ticket from "../../models/Ticket"; +import TicketTag from "../../models/TicketTag"; + +interface Request { + companyId: number; +} + +const KanbanListService = async ({ + companyId +}: Request): Promise => { + const tags = await Tag.findAll({ + where: { + kanban: 1, + companyId: companyId, + }, + order: [["id", "ASC"]], + raw: true, + }); + return tags; +}; + +export default KanbanListService; diff --git a/backend/src/services/TagServices/ListService.ts b/backend/src/services/TagServices/ListService.ts new file mode 100644 index 0000000..b07787d --- /dev/null +++ b/backend/src/services/TagServices/ListService.ts @@ -0,0 +1,66 @@ +import { Op, literal, fn, col } from "sequelize"; +import Tag from "../../models/Tag"; +import Ticket from "../../models/Ticket"; +import TicketTag from "../../models/TicketTag"; + +interface Request { + companyId: number; + searchParam?: string; + pageNumber?: string | number; +} + +interface Response { + tags: Tag[]; + count: number; + hasMore: boolean; +} + +const ListService = async ({ + companyId, + searchParam, + pageNumber = "1" +}: Request): Promise => { + let whereCondition = {}; + const limit = 5000; + const offset = limit * (+pageNumber - 1); + + if (searchParam) { + whereCondition = { + [Op.or]: [ + { name: { [Op.like]: `%${searchParam}%` } }, + { color: { [Op.like]: `%${searchParam}%` } } + ] + }; + } + + const { count, rows: tags } = await Tag.findAndCountAll({ + where: { ...whereCondition, companyId }, + limit, + offset, + order: [["name", "ASC"]], + subQuery: false, + include: [{ + model: TicketTag, + as: 'ticketTags', + attributes: [], + required: false + }], + attributes: [ + 'id', + 'name', + 'color', + [fn('count', col('ticketTags.tagId')), 'ticketsCount'] + ], + group: ['Tag.id'] + }); + + const hasMore = count > offset + tags.length; + + return { + tags, + count, + hasMore + }; +}; + +export default ListService; diff --git a/backend/src/services/TagServices/ShowService.ts b/backend/src/services/TagServices/ShowService.ts new file mode 100644 index 0000000..fec4ade --- /dev/null +++ b/backend/src/services/TagServices/ShowService.ts @@ -0,0 +1,14 @@ +import Tag from "../../models/Tag"; +import AppError from "../../errors/AppError"; + +const TagService = async (id: string | number): Promise => { + const tag = await Tag.findByPk(id); + + if (!tag) { + throw new AppError("ERR_NO_TAG_FOUND", 404); + } + + return tag; +}; + +export default TagService; diff --git a/backend/src/services/TagServices/SimpleListService.ts b/backend/src/services/TagServices/SimpleListService.ts new file mode 100644 index 0000000..451cb93 --- /dev/null +++ b/backend/src/services/TagServices/SimpleListService.ts @@ -0,0 +1,34 @@ +import { Op, Sequelize } from "sequelize"; +import Tag from "../../models/Tag"; +import Ticket from "../../models/Ticket"; +import TicketTag from "../../models/TicketTag"; + +interface Request { + companyId: number; + searchParam?: string; +} + +const ListService = async ({ + companyId, + searchParam +}: Request): Promise => { + let whereCondition = {}; + + if (searchParam) { + whereCondition = { + [Op.or]: [ + { name: { [Op.like]: `%${searchParam}%` } }, + { color: { [Op.like]: `%${searchParam}%` } } + ] + }; + } + + const tags = await Tag.findAll({ + where: { ...whereCondition, companyId }, + order: [["name", "ASC"]] + }); + + return tags; +}; + +export default ListService; diff --git a/backend/src/services/TagServices/SyncTagsService.ts b/backend/src/services/TagServices/SyncTagsService.ts new file mode 100644 index 0000000..18ef7c0 --- /dev/null +++ b/backend/src/services/TagServices/SyncTagsService.ts @@ -0,0 +1,26 @@ +import Tag from "../../models/Tag"; +import Ticket from "../../models/Ticket"; +import TicketTag from "../../models/TicketTag"; + +interface Request { + tags: Tag[]; + ticketId: number; +} + +const SyncTags = async ({ + tags, + ticketId +}: Request): Promise => { + const ticket = await Ticket.findByPk(ticketId, { include: [Tag] }); + + const tagList = tags.map(t => ({ tagId: t.id, ticketId })); + + await TicketTag.destroy({ where: { ticketId } }); + await TicketTag.bulkCreate(tagList); + + ticket?.reload(); + + return ticket; +}; + +export default SyncTags; diff --git a/backend/src/services/TagServices/UpdateService.ts b/backend/src/services/TagServices/UpdateService.ts new file mode 100644 index 0000000..06889ad --- /dev/null +++ b/backend/src/services/TagServices/UpdateService.ts @@ -0,0 +1,47 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Tag from "../../models/Tag"; +import ShowService from "./ShowService"; + +interface TagData { + id?: number; + name?: string; + color?: string; + kanban?: number; +} + +interface Request { + tagData: TagData; + id: string | number; +} + +const UpdateUserService = async ({ + tagData, + id +}: Request): Promise => { + const tag = await ShowService(id); + + const schema = Yup.object().shape({ + name: Yup.string().min(3) + }); + + const { name, color, kanban } = tagData; + + try { + await schema.validate({ name }); + } catch (err: any) { + throw new AppError(err.message); + } + + await tag.update({ + name, + color, + kanban + }); + + await tag.reload(); + return tag; +}; + +export default UpdateUserService; diff --git a/backend/src/services/TicketNoteService/CreateTicketNoteService.ts b/backend/src/services/TicketNoteService/CreateTicketNoteService.ts new file mode 100644 index 0000000..08b6518 --- /dev/null +++ b/backend/src/services/TicketNoteService/CreateTicketNoteService.ts @@ -0,0 +1,34 @@ +import * as Yup from "yup"; +import AppError from "../../errors/AppError"; +import TicketNote from "../../models/TicketNote"; + +interface TicketNoteData { + note: string; + userId: number | string; + contactId: number | string; + ticketId: number | string; +} + +const CreateTicketNoteService = async ( + ticketNoteData: TicketNoteData +): Promise => { + const { note } = ticketNoteData; + + const ticketnoteSchema = Yup.object().shape({ + note: Yup.string() + .min(3, "ERR_TICKETNOTE_INVALID_NAME") + .required("ERR_TICKETNOTE_INVALID_NAME") + }); + + try { + await ticketnoteSchema.validate({ note }); + } catch (err) { + throw new AppError(err.message); + } + + const ticketNote = await TicketNote.create(ticketNoteData); + + return ticketNote; +}; + +export default CreateTicketNoteService; diff --git a/backend/src/services/TicketNoteService/DeleteTicketNoteService.ts b/backend/src/services/TicketNoteService/DeleteTicketNoteService.ts new file mode 100644 index 0000000..5d8f9fe --- /dev/null +++ b/backend/src/services/TicketNoteService/DeleteTicketNoteService.ts @@ -0,0 +1,16 @@ +import TicketNote from "../../models/TicketNote"; +import AppError from "../../errors/AppError"; + +const DeleteTicketNoteService = async (id: string): Promise => { + const ticketnote = await TicketNote.findOne({ + where: { id } + }); + + if (!ticketnote) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + await ticketnote.destroy(); +}; + +export default DeleteTicketNoteService; diff --git a/backend/src/services/TicketNoteService/FindAllTicketNotesService.ts b/backend/src/services/TicketNoteService/FindAllTicketNotesService.ts new file mode 100644 index 0000000..57fc19e --- /dev/null +++ b/backend/src/services/TicketNoteService/FindAllTicketNotesService.ts @@ -0,0 +1,8 @@ +import TicketNote from "../../models/TicketNote"; + +const FindAllTicketNotesService = async (): Promise => { + const ticketNote = await TicketNote.findAll(); + return ticketNote; +}; + +export default FindAllTicketNotesService; diff --git a/backend/src/services/TicketNoteService/FindNotesByContactIdAndTicketId.ts b/backend/src/services/TicketNoteService/FindNotesByContactIdAndTicketId.ts new file mode 100644 index 0000000..bd01d0e --- /dev/null +++ b/backend/src/services/TicketNoteService/FindNotesByContactIdAndTicketId.ts @@ -0,0 +1,31 @@ +import TicketNote from "../../models/TicketNote"; +import User from "../../models/User"; +import Contact from "../../models/Contact"; +import Ticket from "../../models/Ticket"; + +interface Params { + contactId: number | string; + ticketId: number | string; +} + +const FindNotesByContactIdAndTicketId = async ({ + contactId, + ticketId +}: Params): Promise => { + const notes: TicketNote[] = await TicketNote.findAll({ + where: { + contactId, + ticketId + }, + include: [ + { model: User, as: "user", attributes: ["id", "name", "email"] }, + { model: Contact, as: "contact", attributes: ["id", "name"] }, + { model: Ticket, as: "ticket", attributes: ["id", "status", "createdAt"] } + ], + order: [["createdAt", "DESC"]] + }); + + return notes; +}; + +export default FindNotesByContactIdAndTicketId; diff --git a/backend/src/services/TicketNoteService/ListTicketNotesService.ts b/backend/src/services/TicketNoteService/ListTicketNotesService.ts new file mode 100644 index 0000000..900061e --- /dev/null +++ b/backend/src/services/TicketNoteService/ListTicketNotesService.ts @@ -0,0 +1,49 @@ +import { Sequelize, Op } from "sequelize"; +import TicketNote from "../../models/TicketNote"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + ticketNotes: TicketNote[]; + count: number; + hasMore: boolean; +} + +const ListTicketNotesService = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + note: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("note")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + } + ] + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: ticketNotes } = await TicketNote.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + ticketNotes.length; + + return { + ticketNotes, + count, + hasMore + }; +}; + +export default ListTicketNotesService; diff --git a/backend/src/services/TicketNoteService/ShowTicketNoteService.ts b/backend/src/services/TicketNoteService/ShowTicketNoteService.ts new file mode 100644 index 0000000..ead2ae6 --- /dev/null +++ b/backend/src/services/TicketNoteService/ShowTicketNoteService.ts @@ -0,0 +1,16 @@ +import TicketNote from "../../models/TicketNote"; +import AppError from "../../errors/AppError"; + +const ShowTicketNoteService = async ( + id: string | number +): Promise => { + const ticketNote = await TicketNote.findByPk(id); + + if (!ticketNote) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + return ticketNote; +}; + +export default ShowTicketNoteService; diff --git a/backend/src/services/TicketNoteService/UpdateTicketNoteService.ts b/backend/src/services/TicketNoteService/UpdateTicketNoteService.ts new file mode 100644 index 0000000..a656260 --- /dev/null +++ b/backend/src/services/TicketNoteService/UpdateTicketNoteService.ts @@ -0,0 +1,27 @@ +import AppError from "../../errors/AppError"; +import TicketNote from "../../models/TicketNote"; + +interface TicketNoteData { + note: string; + id?: number | string; +} + +const UpdateTicketNoteService = async ( + ticketNoteData: TicketNoteData +): Promise => { + const { id, note } = ticketNoteData; + + const ticketNote = await TicketNote.findByPk(id); + + if (!ticketNote) { + throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404); + } + + await ticketNote.update({ + note + }); + + return ticketNote; +}; + +export default UpdateTicketNoteService; diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts new file mode 100644 index 0000000..528e912 --- /dev/null +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -0,0 +1,82 @@ +import AppError from "../../errors/AppError"; +import CheckContactOpenTickets from "../../helpers/CheckContactOpenTickets"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import Ticket from "../../models/Ticket"; +import ShowContactService from "../ContactServices/ShowContactService"; +import { getIO } from "../../libs/socket"; +import GetDefaultWhatsAppByUser from "../../helpers/GetDefaultWhatsAppByUser"; +import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; + +interface Request { + contactId: number; + status: string; + userId: number; + companyId: number; + queueId?: number; + whatsappId?: string; +} + +const CreateTicketService = async ({ + contactId, + status, + userId, + queueId, + companyId, + whatsappId +}: Request): Promise => { + let whatsapp; + + if (whatsappId !== undefined && whatsappId !== null && whatsappId !== "") { + whatsapp = await ShowWhatsAppService(whatsappId, companyId) + } + + let defaultWhatsapp = await GetDefaultWhatsAppByUser(userId); + + if (whatsapp) { + defaultWhatsapp = whatsapp; + } + if (!defaultWhatsapp) + defaultWhatsapp = await GetDefaultWhatsApp(companyId); + + await CheckContactOpenTickets(contactId, whatsappId); + + const { isGroup } = await ShowContactService(contactId, companyId); + + const [{ id }] = await Ticket.findOrCreate({ + where: { + contactId, + companyId, + whatsappId + }, + defaults: { + contactId, + companyId, + whatsappId: defaultWhatsapp.id, + status, + isGroup, + userId + } + }); + + await Ticket.update( + { companyId, queueId, userId, whatsappId: defaultWhatsapp.id, status: "open" }, + { where: { id } } + ); + + const ticket = await Ticket.findByPk(id, { include: ["contact", "queue"] }); + + if (!ticket) { + throw new AppError("ERR_CREATING_TICKET"); + } + + const io = getIO(); + + io.to(ticket.id.toString()).emit("ticket", { + action: "update", + ticket + }); + + return ticket; +}; + +export default CreateTicketService; diff --git a/backend/src/services/TicketServices/DeleteTicketService.ts b/backend/src/services/TicketServices/DeleteTicketService.ts new file mode 100644 index 0000000..279a913 --- /dev/null +++ b/backend/src/services/TicketServices/DeleteTicketService.ts @@ -0,0 +1,18 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; + +const DeleteTicketService = async (id: string): Promise => { + const ticket = await Ticket.findOne({ + where: { id } + }); + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + await ticket.destroy(); + + return ticket; +}; + +export default DeleteTicketService; diff --git a/backend/src/services/TicketServices/FindOrCreateATicketTrakingService.ts b/backend/src/services/TicketServices/FindOrCreateATicketTrakingService.ts new file mode 100644 index 0000000..80d0df3 --- /dev/null +++ b/backend/src/services/TicketServices/FindOrCreateATicketTrakingService.ts @@ -0,0 +1,40 @@ +import { Op } from "sequelize"; +import TicketTraking from "../../models/TicketTraking"; + +interface Params { + ticketId: string | number; + companyId: string | number; + whatsappId?: string | number; + userId?: string | number; +} + +const FindOrCreateATicketTrakingService = async ({ + ticketId, + companyId, + whatsappId, + userId +}: Params): Promise => { + const ticketTraking = await TicketTraking.findOne({ + where: { + ticketId, + finishedAt: { + [Op.is]: null + } + } + }); + + if (ticketTraking) { + return ticketTraking; + } + + const newRecord = await TicketTraking.create({ + ticketId, + companyId, + whatsappId, + userId + }); + + return newRecord; +}; + +export default FindOrCreateATicketTrakingService; diff --git a/backend/src/services/TicketServices/FindOrCreateTicketService.ts b/backend/src/services/TicketServices/FindOrCreateTicketService.ts new file mode 100644 index 0000000..ca1cf9b --- /dev/null +++ b/backend/src/services/TicketServices/FindOrCreateTicketService.ts @@ -0,0 +1,128 @@ +import { subHours } from "date-fns"; +import { Op } from "sequelize"; +import Contact from "../../models/Contact"; +import Ticket from "../../models/Ticket"; +import ShowTicketService from "./ShowTicketService"; +import FindOrCreateATicketTrakingService from "./FindOrCreateATicketTrakingService"; +import Setting from "../../models/Setting"; +import Whatsapp from "../../models/Whatsapp"; + +interface TicketData { + status?: string; + companyId?: number; + unreadMessages?: number; +} + +const FindOrCreateTicketService = async ( + contact: Contact, + whatsappId: number, + unreadMessages: number, + companyId: number, + groupContact?: Contact +): Promise => { + let ticket = await Ticket.findOne({ + where: { + status: { + [Op.or]: ["open", "pending", "closed"] + }, + contactId: groupContact ? groupContact.id : contact.id, + companyId, + whatsappId + }, + order: [["id", "DESC"]] + }); + + if (ticket) { + await ticket.update({ unreadMessages, whatsappId }); + } + + if (ticket?.status === "closed") { + await ticket.update({ queueId: null, userId: null }); + } + + if (!ticket && groupContact) { + ticket = await Ticket.findOne({ + where: { + contactId: groupContact.id + }, + order: [["updatedAt", "DESC"]] + }); + + if (ticket) { + await ticket.update({ + status: "pending", + userId: null, + unreadMessages, + queueId: null, + companyId + }); + await FindOrCreateATicketTrakingService({ + ticketId: ticket.id, + companyId, + whatsappId: ticket.whatsappId, + userId: ticket.userId + }); + } + const msgIsGroupBlock = await Setting.findOne({ + where: { key: "timeCreateNewTicket" } + }); + + const value = msgIsGroupBlock ? parseInt(msgIsGroupBlock.value, 10) : 7200; + } + + if (!ticket && !groupContact) { + ticket = await Ticket.findOne({ + where: { + updatedAt: { + [Op.between]: [+subHours(new Date(), 2), +new Date()] + }, + contactId: contact.id + }, + order: [["updatedAt", "DESC"]] + }); + + if (ticket) { + await ticket.update({ + status: "pending", + userId: null, + unreadMessages, + queueId: null, + companyId + }); + await FindOrCreateATicketTrakingService({ + ticketId: ticket.id, + companyId, + whatsappId: ticket.whatsappId, + userId: ticket.userId + }); + } + } + + const whatsapp = await Whatsapp.findOne({ + where: { id: whatsappId } + }); + + if (!ticket) { + ticket = await Ticket.create({ + contactId: groupContact ? groupContact.id : contact.id, + status: "pending", + isGroup: !!groupContact, + unreadMessages, + whatsappId, + whatsapp, + companyId + }); + await FindOrCreateATicketTrakingService({ + ticketId: ticket.id, + companyId, + whatsappId, + userId: ticket.userId + }); + } + + ticket = await ShowTicketService(ticket.id, companyId); + + return ticket; +}; + +export default FindOrCreateTicketService; diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts new file mode 100644 index 0000000..8fff0c4 --- /dev/null +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -0,0 +1,237 @@ +import { Op, fn, where, col, Filterable, Includeable } from "sequelize"; +import { startOfDay, endOfDay, parseISO } from "date-fns"; + +import Ticket from "../../models/Ticket"; +import Contact from "../../models/Contact"; +import Message from "../../models/Message"; +import Queue from "../../models/Queue"; +import User from "../../models/User"; +import ShowUserService from "../UserServices/ShowUserService"; +import Tag from "../../models/Tag"; +import TicketTag from "../../models/TicketTag"; +import { intersection } from "lodash"; +import Whatsapp from "../../models/Whatsapp"; + +interface Request { + searchParam?: string; + pageNumber?: string; + status?: string; + date?: string; + updatedAt?: string; + showAll?: string; + userId: string; + withUnreadMessages?: string; + queueIds: number[]; + tags: number[]; + users: number[]; + companyId: number; +} + +interface Response { + tickets: Ticket[]; + count: number; + hasMore: boolean; +} + +const ListTicketsService = async ({ + searchParam = "", + pageNumber = "1", + queueIds, + tags, + users, + status, + date, + updatedAt, + showAll, + userId, + withUnreadMessages, + companyId +}: Request): Promise => { + let whereCondition: Filterable["where"] = { + [Op.or]: [{ userId }, { status: "pending" }], + queueId: { [Op.or]: [queueIds, null] } + }; + let includeCondition: Includeable[]; + + includeCondition = [ + { + model: Contact, + as: "contact", + attributes: ["id", "name", "number", "email", "profilePicUrl"] + }, + { + model: Queue, + as: "queue", + attributes: ["id", "name", "color"] + }, + { + model: User, + as: "user", + attributes: ["id", "name"] + }, + { + model: Tag, + as: "tags", + attributes: ["id", "name", "color"] + }, + { + model: Whatsapp, + as: "whatsapp", + attributes: ["name"] + }, + ]; + + if (showAll === "true") { + whereCondition = { queueId: { [Op.or]: [queueIds, null] } }; + } + + if (status) { + whereCondition = { + ...whereCondition, + status + }; + } + + if (searchParam) { + const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim(); + + includeCondition = [ + ...includeCondition, + { + model: Message, + as: "messages", + attributes: ["id", "body"], + where: { + body: where( + fn("LOWER", col("body")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + }, + required: false, + duplicating: false + } + ]; + + whereCondition = { + ...whereCondition, + [Op.or]: [ + { + "$contact.name$": where( + fn("LOWER", col("contact.name")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + }, + { "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } }, + { + "$message.body$": where( + fn("LOWER", col("body")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + } + ] + }; + } + + if (date) { + whereCondition = { + createdAt: { + [Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))] + } + }; + } + + if (updatedAt) { + whereCondition = { + updatedAt: { + [Op.between]: [ + +startOfDay(parseISO(updatedAt)), + +endOfDay(parseISO(updatedAt)) + ] + } + }; + } + + if (withUnreadMessages === "true") { + const user = await ShowUserService(userId); + const userQueueIds = user.queues.map(queue => queue.id); + + whereCondition = { + [Op.or]: [{ userId }, { status: "pending" }], + queueId: { [Op.or]: [userQueueIds, null] }, + unreadMessages: { [Op.gt]: 0 } + }; + } + + if (Array.isArray(tags) && tags.length > 0) { + const ticketsTagFilter: any[] | null = []; + for (let tag of tags) { + const ticketTags = await TicketTag.findAll({ + where: { tagId: tag } + }); + if (ticketTags) { + ticketsTagFilter.push(ticketTags.map(t => t.ticketId)); + } + } + + const ticketsIntersection: number[] = intersection(...ticketsTagFilter); + + whereCondition = { + ...whereCondition, + id: { + [Op.in]: ticketsIntersection + } + }; + } + + if (Array.isArray(users) && users.length > 0) { + const ticketsUserFilter: any[] | null = []; + for (let user of users) { + const ticketUsers = await Ticket.findAll({ + where: { userId: user } + }); + if (ticketUsers) { + ticketsUserFilter.push(ticketUsers.map(t => t.id)); + } + } + + const ticketsIntersection: number[] = intersection(...ticketsUserFilter); + + whereCondition = { + ...whereCondition, + id: { + [Op.in]: ticketsIntersection + } + }; + } + + const limit = 40; + const offset = limit * (+pageNumber - 1); + + whereCondition = { + ...whereCondition, + companyId + }; + + const { count, rows: tickets } = await Ticket.findAndCountAll({ + where: whereCondition, + include: includeCondition, + distinct: true, + limit, + offset, + order: [["updatedAt", "DESC"]], + subQuery: false + }); + + const hasMore = count > offset + tickets.length; + + return { + tickets, + count, + hasMore + }; +}; + +export default ListTicketsService; \ No newline at end of file diff --git a/backend/src/services/TicketServices/ListTicketsServiceKanban.ts b/backend/src/services/TicketServices/ListTicketsServiceKanban.ts new file mode 100644 index 0000000..b96b51c --- /dev/null +++ b/backend/src/services/TicketServices/ListTicketsServiceKanban.ts @@ -0,0 +1,234 @@ +import { Op, fn, where, col, Filterable, Includeable } from "sequelize"; +import { startOfDay, endOfDay, parseISO } from "date-fns"; + +import Ticket from "../../models/Ticket"; +import Contact from "../../models/Contact"; +import Message from "../../models/Message"; +import Queue from "../../models/Queue"; +import User from "../../models/User"; +import ShowUserService from "../UserServices/ShowUserService"; +import Tag from "../../models/Tag"; +import TicketTag from "../../models/TicketTag"; +import { intersection } from "lodash"; +import Whatsapp from "../../models/Whatsapp"; + +interface Request { + searchParam?: string; + pageNumber?: string; + status?: string; + date?: string; + updatedAt?: string; + showAll?: string; + userId: string; + withUnreadMessages?: string; + queueIds: number[]; + tags: number[]; + users: number[]; + companyId: number; +} + +interface Response { + tickets: Ticket[]; + count: number; + hasMore: boolean; +} + +const ListTicketsServiceKanban = async ({ + searchParam = "", + pageNumber = "1", + queueIds, + tags, + users, + status, + date, + updatedAt, + showAll, + userId, + withUnreadMessages, + companyId +}: Request): Promise => { + let whereCondition: Filterable["where"] = { + [Op.or]: [{ userId }, { status: "pending" }], + queueId: { [Op.or]: [queueIds, null] } + }; + let includeCondition: Includeable[]; + + includeCondition = [ + { + model: Contact, + as: "contact", + attributes: ["id", "name", "number", "email"] + }, + { + model: Queue, + as: "queue", + attributes: ["id", "name", "color"] + }, + { + model: User, + as: "user", + attributes: ["id", "name"] + }, + { + model: Tag, + as: "tags", + attributes: ["id", "name", "color"] + }, + { + model: Whatsapp, + as: "whatsapp", + attributes: ["name"] + }, + ]; + + if (showAll === "true") { + whereCondition = { queueId: { [Op.or]: [queueIds, null] } }; + } + + whereCondition = { + ...whereCondition, + status: { [Op.or]: ["pending", "open"] } + }; + + if (searchParam) { + const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim(); + + includeCondition = [ + ...includeCondition, + { + model: Message, + as: "messages", + attributes: ["id", "body"], + where: { + body: where( + fn("LOWER", col("body")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + }, + required: false, + duplicating: false + } + ]; + + whereCondition = { + ...whereCondition, + [Op.or]: [ + { + "$contact.name$": where( + fn("LOWER", col("contact.name")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + }, + { "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } }, + { + "$message.body$": where( + fn("LOWER", col("body")), + "LIKE", + `%${sanitizedSearchParam}%` + ) + } + ] + }; + } + + if (date) { + whereCondition = { + createdAt: { + [Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))] + } + }; + } + + if (updatedAt) { + whereCondition = { + updatedAt: { + [Op.between]: [ + +startOfDay(parseISO(updatedAt)), + +endOfDay(parseISO(updatedAt)) + ] + } + }; + } + + if (withUnreadMessages === "true") { + const user = await ShowUserService(userId); + const userQueueIds = user.queues.map(queue => queue.id); + + whereCondition = { + [Op.or]: [{ userId }, { status: "pending" }], + queueId: { [Op.or]: [userQueueIds, null] }, + unreadMessages: { [Op.gt]: 0 } + }; + } + + if (Array.isArray(tags) && tags.length > 0) { + const ticketsTagFilter: any[] | null = []; + for (let tag of tags) { + const ticketTags = await TicketTag.findAll({ + where: { tagId: tag } + }); + if (ticketTags) { + ticketsTagFilter.push(ticketTags.map(t => t.ticketId)); + } + } + + const ticketsIntersection: number[] = intersection(...ticketsTagFilter); + + whereCondition = { + ...whereCondition, + id: { + [Op.in]: ticketsIntersection + } + }; + } + + if (Array.isArray(users) && users.length > 0) { + const ticketsUserFilter: any[] | null = []; + for (let user of users) { + const ticketUsers = await Ticket.findAll({ + where: { userId: user } + }); + if (ticketUsers) { + ticketsUserFilter.push(ticketUsers.map(t => t.id)); + } + } + + const ticketsIntersection: number[] = intersection(...ticketsUserFilter); + + whereCondition = { + ...whereCondition, + id: { + [Op.in]: ticketsIntersection + } + }; + } + + const limit = 40; + const offset = limit * (+pageNumber - 1); + + whereCondition = { + ...whereCondition, + companyId + }; + + const { count, rows: tickets } = await Ticket.findAndCountAll({ + where: whereCondition, + include: includeCondition, + distinct: true, + limit, + offset, + order: [["updatedAt", "DESC"]], + subQuery: false + }); + const hasMore = count > offset + tickets.length; + + return { + tickets, + count, + hasMore + }; +}; + +export default ListTicketsServiceKanban; \ No newline at end of file diff --git a/backend/src/services/TicketServices/ShowTicketFromUUIDService.ts b/backend/src/services/TicketServices/ShowTicketFromUUIDService.ts new file mode 100644 index 0000000..61afebf --- /dev/null +++ b/backend/src/services/TicketServices/ShowTicketFromUUIDService.ts @@ -0,0 +1,51 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import User from "../../models/User"; +import Queue from "../../models/Queue"; +import Tag from "../../models/Tag"; +import Whatsapp from "../../models/Whatsapp"; + +const ShowTicketUUIDService = async (uuid: string): Promise => { + const ticket = await Ticket.findOne({ + where: { + uuid + }, + include: [ + { + model: Contact, + as: "contact", + attributes: ["id", "name", "number", "email", "profilePicUrl"], + include: ["extraInfo"] + }, + { + model: User, + as: "user", + attributes: ["id", "name"] + }, + { + model: Queue, + as: "queue", + attributes: ["id", "name", "color"] + }, + { + model: Whatsapp, + as: "whatsapp", + attributes: ["name"] + }, + { + model: Tag, + as: "tags", + attributes: ["id", "name", "color"] + } + ] + }); + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + return ticket; +}; + +export default ShowTicketUUIDService; diff --git a/backend/src/services/TicketServices/ShowTicketService.ts b/backend/src/services/TicketServices/ShowTicketService.ts new file mode 100644 index 0000000..2d9b516 --- /dev/null +++ b/backend/src/services/TicketServices/ShowTicketService.ts @@ -0,0 +1,57 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import User from "../../models/User"; +import Queue from "../../models/Queue"; +import Tag from "../../models/Tag"; +import Whatsapp from "../../models/Whatsapp"; +import Prompt from "../../models/Prompt"; + +const ShowTicketService = async ( + id: string | number, + companyId: number +): Promise => { + const ticket = await Ticket.findByPk(id, { + include: [ + { + model: Contact, + as: "contact", + attributes: ["id", "name", "number", "email", "profilePicUrl"], + include: ["extraInfo"] + }, + { + model: User, + as: "user", + attributes: ["id", "name"] + }, + { + model: Queue, + as: "queue", + attributes: ["id", "name", "color"], + include: ["prompt", "queueIntegrations"] + }, + { + model: Whatsapp, + as: "whatsapp", + attributes: ["name"] + }, + { + model: Tag, + as: "tags", + attributes: ["id", "name", "color"] + } + ] + }); + + if (ticket?.companyId !== companyId) { + throw new AppError("Não é possível consultar registros de outra empresa"); + } + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + return ticket; +}; + +export default ShowTicketService; diff --git a/backend/src/services/TicketServices/UpdateTicketService.ts b/backend/src/services/TicketServices/UpdateTicketService.ts new file mode 100644 index 0000000..4292605 --- /dev/null +++ b/backend/src/services/TicketServices/UpdateTicketService.ts @@ -0,0 +1,297 @@ +import moment from "moment"; +import * as Sentry from "@sentry/node"; +import CheckContactOpenTickets from "../../helpers/CheckContactOpenTickets"; +import SetTicketMessagesAsRead from "../../helpers/SetTicketMessagesAsRead"; +import { getIO } from "../../libs/socket"; +import Ticket from "../../models/Ticket"; +import Setting from "../../models/Setting"; +import Queue from "../../models/Queue"; +import ShowTicketService from "./ShowTicketService"; +import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; +import SendWhatsAppMessage from "../WbotServices/SendWhatsAppMessage"; +import FindOrCreateATicketTrakingService from "./FindOrCreateATicketTrakingService"; +import GetTicketWbot from "../../helpers/GetTicketWbot"; +import { verifyMessage } from "../WbotServices/wbotMessageListener"; +import ListSettingsServiceOne from "../SettingServices/ListSettingsServiceOne"; //NOVO PLW DESIGN// +import ShowUserService from "../UserServices/ShowUserService"; //NOVO PLW DESIGN// +import { isNil } from "lodash"; +import Whatsapp from "../../models/Whatsapp"; +import { Op } from "sequelize"; +import AppError from "../../errors/AppError"; + +interface TicketData { + status?: string; + userId?: number | null; + queueId?: number | null; + chatbot?: boolean; + queueOptionId?: number; + whatsappId?: string; + useIntegration?: boolean; + integrationId?: number | null; + promptId?: number | null; +} + +interface Request { + ticketData: TicketData; + ticketId: string | number; + companyId: number; +} + +interface Response { + ticket: Ticket; + oldStatus: string; + oldUserId: number | undefined; +} + +const UpdateTicketService = async ({ + ticketData, + ticketId, + companyId +}: Request): Promise => { + + try { + const { status } = ticketData; + let { queueId, userId, whatsappId } = ticketData; + let chatbot: boolean | null = ticketData.chatbot || false; + let queueOptionId: number | null = ticketData.queueOptionId || null; + let promptId: number | null = ticketData.promptId || null; + let useIntegration: boolean | null = ticketData.useIntegration || false; + let integrationId: number | null = ticketData.integrationId || null; + + const io = getIO(); + + const key = "userRating"; + const setting = await Setting.findOne({ + where: { + companyId, + key + } + }); + + + + const ticket = await ShowTicketService(ticketId, companyId); + const ticketTraking = await FindOrCreateATicketTrakingService({ + ticketId, + companyId, + whatsappId: ticket.whatsappId + }); + + if (isNil(whatsappId)) { + whatsappId = ticket.whatsappId.toString(); + } + + await SetTicketMessagesAsRead(ticket); + + const oldStatus = ticket.status; + const oldUserId = ticket.user?.id; + const oldQueueId = ticket.queueId; + + if (oldStatus === "closed" || Number(whatsappId) !== ticket.whatsappId) { + // let otherTicket = await Ticket.findOne({ + // where: { + // contactId: ticket.contactId, + // status: { [Op.or]: ["open", "pending", "group"] }, + // whatsappId + // } + // }); + // if (otherTicket) { + // otherTicket = await ShowTicketService(otherTicket.id, companyId) + + // await ticket.update({status: "closed"}) + + // io.to(oldStatus).emit(`company-${companyId}-ticket`, { + // action: "delete", + // ticketId: ticket.id + // }); + + // return { ticket: otherTicket, oldStatus, oldUserId } + // } + await CheckContactOpenTickets(ticket.contact.id, whatsappId); + chatbot = null; + queueOptionId = null; + } + + if (status !== undefined && ["closed"].indexOf(status) > -1) { + const { complationMessage, ratingMessage } = await ShowWhatsAppService( + ticket.whatsappId, + companyId + ); + + if (setting?.value === "enabled") { + if (ticketTraking.ratingAt == null) { + const ratingTxt = ratingMessage || ""; + let bodyRatingMessage = `\u200e${ratingTxt}\n\n`; + bodyRatingMessage += + "Digite de 1 à 3 para qualificar nosso atendimento:\n*1* - _Insatisfeito_\n*2* - _Satisfeito_\n*3* - _Muito Satisfeito_\n\n"; + await SendWhatsAppMessage({ body: bodyRatingMessage, ticket }); + + await ticketTraking.update({ + ratingAt: moment().toDate() + }); + + io.to(`company-${ticket.companyId}-open`) + .to(`queue-${ticket.queueId}-open`) + .to(ticketId.toString()) + .emit(`company-${ticket.companyId}-ticket`, { + action: "delete", + ticketId: ticket.id + }); + + return { ticket, oldStatus, oldUserId }; + } + ticketTraking.ratingAt = moment().toDate(); + ticketTraking.rated = false; + } + + if (!isNil(complationMessage) && complationMessage !== "") { + const body = `\u200e${complationMessage}`; + await SendWhatsAppMessage({ body, ticket }); + } + await ticket.update({ + promptId: null, + integrationId: null, + useIntegration: false, + typebotStatus: false, + typebotSessionId: null + }) + + ticketTraking.finishedAt = moment().toDate(); + ticketTraking.whatsappId = ticket.whatsappId; + ticketTraking.userId = ticket.userId; + + /* queueId = null; + userId = null; */ + } + + if (queueId !== undefined && queueId !== null) { + ticketTraking.queuedAt = moment().toDate(); + } + + const settingsTransfTicket = await ListSettingsServiceOne({ companyId: companyId, key: "sendMsgTransfTicket" }); + + if (settingsTransfTicket?.value === "enabled") { + // Mensagem de transferencia da FILA + if (oldQueueId !== queueId && oldUserId === userId && !isNil(oldQueueId) && !isNil(queueId)) { + + const queue = await Queue.findByPk(queueId); + const wbot = await GetTicketWbot(ticket); + const msgtxt = "*Mensagem automática*:\nVocê foi transferido para o departamento *" + queue?.name + "*\naguarde, já vamos te atender!"; + + const queueChangedMessage = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + text: msgtxt + } + ); + await verifyMessage(queueChangedMessage, ticket, ticket.contact); + } + else + // Mensagem de transferencia do ATENDENTE + if (oldUserId !== userId && oldQueueId === queueId && !isNil(oldUserId) && !isNil(userId)) { + const wbot = await GetTicketWbot(ticket); + const nome = await ShowUserService(ticketData.userId); + const msgtxt = "*Mensagem automática*:\nFoi transferido para o atendente *" + nome.name + "*\naguarde, já vamos te atender!"; + + const queueChangedMessage = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + text: msgtxt + } + ); + await verifyMessage(queueChangedMessage, ticket, ticket.contact); + } + else + // Mensagem de transferencia do ATENDENTE e da FILA + if (oldUserId !== userId && !isNil(oldUserId) && !isNil(userId) && oldQueueId !== queueId && !isNil(oldQueueId) && !isNil(queueId)) { + const wbot = await GetTicketWbot(ticket); + const queue = await Queue.findByPk(queueId); + const nome = await ShowUserService(ticketData.userId); + const msgtxt = "*Mensagem automática*:\nVocê foi transferido para o departamento *" + queue?.name + "* e contará com a presença de *" + nome.name + "*\naguarde, já vamos te atender!"; + + const queueChangedMessage = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + text: msgtxt + } + ); + await verifyMessage(queueChangedMessage, ticket, ticket.contact); + } else + if (oldUserId !== undefined && isNil(userId) && oldQueueId !== queueId && !isNil(queueId)) { + + const queue = await Queue.findByPk(queueId); + const wbot = await GetTicketWbot(ticket); + const msgtxt = "*Mensagem automática*:\nVocê foi transferido para o departamento *" + queue?.name + "*\naguarde, já vamos te atender!"; + + const queueChangedMessage = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + text: msgtxt + } + ); + await verifyMessage(queueChangedMessage, ticket, ticket.contact); + } + } + + await ticket.update({ + status, + queueId, + userId, + whatsappId, + chatbot, + queueOptionId + }); + + await ticket.reload(); + + if (status !== undefined && ["pending"].indexOf(status) > -1) { + ticketTraking.update({ + whatsappId, + queuedAt: moment().toDate(), + startedAt: null, + userId: null + }); + } + + if (status !== undefined && ["open"].indexOf(status) > -1) { + ticketTraking.update({ + startedAt: moment().toDate(), + ratingAt: null, + rated: false, + whatsappId, + userId: ticket.userId + }); + } + + await ticketTraking.save(); + + if (ticket.status !== oldStatus || ticket.user?.id !== oldUserId) { + + io.to(`company-${companyId}-${oldStatus}`) + .to(`queue-${ticket.queueId}-${oldStatus}`) + .to(`user-${oldUserId}`) + .emit(`company-${companyId}-ticket`, { + action: "delete", + ticketId: ticket.id + }); + } + + io.to(`company-${companyId}-${ticket.status}`) + .to(`company-${companyId}-notification`) + .to(`queue-${ticket.queueId}-${ticket.status}`) + .to(`queue-${ticket.queueId}-notification`) + .to(ticketId.toString()) + .to(`user-${ticket?.userId}`) + .to(`user-${oldUserId}`) + .emit(`company-${companyId}-ticket`, { + action: "update", + ticket + }); + + return { ticket, oldStatus, oldUserId }; + } catch (err) { + Sentry.captureException(err); + } +}; + +export default UpdateTicketService; diff --git a/backend/src/services/TypebotServices/typebotListener.ts b/backend/src/services/TypebotServices/typebotListener.ts new file mode 100644 index 0000000..30147f3 --- /dev/null +++ b/backend/src/services/TypebotServices/typebotListener.ts @@ -0,0 +1,413 @@ +import axios from "axios"; +import Ticket from "../../models/Ticket"; +import QueueIntegrations from "../../models/QueueIntegrations"; +import { WASocket, delay, proto } from "@whiskeysockets/baileys"; +import { getBodyMessage } from "../WbotServices/wbotMessageListener"; +import { logger } from "../../utils/logger"; +import { isNil } from "lodash"; +import UpdateTicketService from "../TicketServices/UpdateTicketService"; + + +type Session = WASocket & { + id?: number; +}; + +interface Request { + wbot: Session; + msg: proto.IWebMessageInfo; + ticket: Ticket; + typebot: QueueIntegrations; +} + + +const typebotListener = async ({ + wbot, + msg, + ticket, + typebot +}: Request): Promise => { + + if (msg.key.remoteJid === 'status@broadcast') return; + + const { urlN8N: url, + typebotExpires, + typebotKeywordFinish, + typebotKeywordRestart, + typebotUnknownMessage, + typebotSlug, + typebotDelayMessage, + typebotRestartMessage + } = typebot; + + const number = msg.key.remoteJid.replace(/\D/g, ''); + + let body = getBodyMessage(msg); + + async function createSession(msg, typebot, number) { + try { + const id = Math.floor(Math.random() * 10000000000).toString(); + + const reqData = JSON.stringify({ + "isStreamEnabled": true, + "message": "string", + "resultId": "string", + "isOnlyRegistering": false, + "prefilledVariables": { + "number": number, + "pushName": msg.pushName || "" + }, + }); + + const config = { + method: 'post', + maxBodyLength: Infinity, + url: `${url}/api/v1/typebots/${typebotSlug}/startChat`, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: reqData + }; + + const request = await axios.request(config); + + return request.data; + + } catch (err) { + logger.info("Erro ao criar sessão do typebot: ", err) + throw err; + } + } + + + let sessionId + let dataStart + let status = false; + try { + const dataLimite = new Date() + dataLimite.setMinutes(dataLimite.getMinutes() - Number(typebotExpires)); + + + if (typebotExpires > 0 && ticket.updatedAt < dataLimite) { + await ticket.update({ + typebotSessionId: null, + isBot: true + }); + + await ticket.reload(); + } + + if (isNil(ticket.typebotSessionId)) { + dataStart = await createSession(msg, typebot, number); + sessionId = dataStart.sessionId + status = true; + await ticket.update({ + typebotSessionId: sessionId, + typebotStatus: true, + useIntegration: true, + integrationId: typebot.id + }) + } else { + sessionId = ticket.typebotSessionId; + status = ticket.typebotStatus; + } + + if (!status) return; + + //let body = getConversationMessage(msg); + + + if (body !== typebotKeywordFinish && body !== typebotKeywordRestart) { + let requestContinue + let messages + let input + if (dataStart?.messages.length === 0 || dataStart === undefined) { + const reqData = JSON.stringify({ + "message": body + }); + + let config = { + method: 'post', + maxBodyLength: Infinity, + url: `${url}/api/v1/sessions/${sessionId}/continueChat`, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: reqData + }; + requestContinue = await axios.request(config); + messages = requestContinue.data?.messages; + input = requestContinue.data?.input; + } else { + messages = dataStart?.messages; + input = dataStart?.input; + } + + if (messages?.length === 0) { + await wbot.sendMessage(`${number}@c.us`, { text: typebotUnknownMessage }); + } else { + for (const message of messages) { + if (message.type === 'text') { + let formattedText = ''; + let linkPreview = false; + for (const richText of message.content.richText) { + for (const element of richText.children) { + let text = ''; + + if (element.text) { + text = element.text; + } + if (element.type && element.children) { + for (const subelement of element.children) { + let text = ''; + + if (subelement.text) { + text = subelement.text; + } + + if (subelement.type && subelement.children) { + for (const subelement2 of subelement.children) { + let text = ''; + + if (subelement2.text) { + text = subelement2.text; + } + + if (subelement2.bold) { + text = `*${text}*`; + } + if (subelement2.italic) { + text = `_${text}_`; + } + if (subelement2.underline) { + text = `~${text}~`; + } + if (subelement2.url) { + const linkText = subelement2.children[0].text; + text = `[${linkText}](${subelement2.url})`; + linkPreview = true; + } + formattedText += text; + } + } + if (subelement.bold) { + text = `*${text}*`; + } + if (subelement.italic) { + text = `_${text}_`; + } + if (subelement.underline) { + text = `~${text}~`; + } + if (subelement.url) { + const linkText = subelement.children[0].text; + text = `[${linkText}](${subelement.url})`; + linkPreview = true; + } + formattedText += text; + } + } + + if (element.bold) { + text = `*${text}*` + } + if (element.italic) { + text = `_${text}_`; + } + if (element.underline) { + text = `~${text}~`; + } + + if (element.url) { + const linkText = element.children[0].text; + text = `[${linkText}](${element.url})`; + linkPreview = true; + } + + formattedText += text; + } + formattedText += '\n'; + } + formattedText = formattedText.replace('**', '').replace(/\n$/, ''); + + if (formattedText === "Invalid message. Please, try again.") { + formattedText = typebotUnknownMessage; + } + + if (formattedText.startsWith("#")) { + let gatilho = formattedText.replace("#", ""); + + try { + let jsonGatilho = JSON.parse(gatilho); + + if (jsonGatilho.stopBot && isNil(jsonGatilho.userId) && isNil(jsonGatilho.queueId)) { + await ticket.update({ + useIntegration: false, + isBot: false + }) + + return; + } + if (!isNil(jsonGatilho.queueId) && jsonGatilho.queueId > 0 && isNil(jsonGatilho.userId)) { + await UpdateTicketService({ + ticketData: { + queueId: jsonGatilho.queueId, + chatbot: false, + useIntegration: false, + integrationId: null + }, + ticketId: ticket.id, + companyId: ticket.companyId + }) + + return; + } + + if (!isNil(jsonGatilho.queueId) && jsonGatilho.queueId > 0 && !isNil(jsonGatilho.userId) && jsonGatilho.userId > 0) { + await UpdateTicketService({ + ticketData: { + queueId: jsonGatilho.queueId, + userId: jsonGatilho.userId, + chatbot: false, + useIntegration: false, + integrationId: null + }, + ticketId: ticket.id, + companyId: ticket.companyId + }) + + return; + } + } catch (err) { + throw err + } + } + + await wbot.presenceSubscribe(msg.key.remoteJid) + //await delay(2000) + await wbot.sendPresenceUpdate('composing', msg.key.remoteJid) + await delay(typebotDelayMessage) + await wbot.sendPresenceUpdate('paused', msg.key.remoteJid) + + + await wbot.sendMessage(msg.key.remoteJid, { text: formattedText }); + } + + if (message.type === 'audio') { + await wbot.presenceSubscribe(msg.key.remoteJid) + //await delay(2000) + await wbot.sendPresenceUpdate('composing', msg.key.remoteJid) + await delay(typebotDelayMessage) + await wbot.sendPresenceUpdate('paused', msg.key.remoteJid) + const media = { + audio: { + url: message.content.url, + mimetype: 'audio/mp4', + ptt: true + }, + } + await wbot.sendMessage(msg.key.remoteJid, media); + + } + + // if (message.type === 'embed') { + // await wbot.presenceSubscribe(msg.key.remoteJid) + // //await delay(2000) + // await wbot.sendPresenceUpdate('composing', msg.key.remoteJid) + // await delay(typebotDelayMessage) + // await wbot.sendPresenceUpdate('paused', msg.key.remoteJid) + // const media = { + + // document: { url: message.content.url }, + // mimetype: 'application/pdf', + // caption: "" + + // } + // await wbot.sendMessage(msg.key.remoteJid, media); + // } + + if (message.type === 'image') { + await wbot.presenceSubscribe(msg.key.remoteJid) + //await delay(2000) + await wbot.sendPresenceUpdate('composing', msg.key.remoteJid) + await delay(typebotDelayMessage) + await wbot.sendPresenceUpdate('paused', msg.key.remoteJid) + const media = { + image: { + url: message.content.url, + }, + + } + await wbot.sendMessage(msg.key.remoteJid, media); + } + + // if (message.type === 'video' ) { + // await wbot.presenceSubscribe(msg.key.remoteJid) + // //await delay(2000) + // await wbot.sendPresenceUpdate('composing', msg.key.remoteJid) + // await delay(typebotDelayMessage) + // await wbot.sendPresenceUpdate('paused', msg.key.remoteJid) + // const media = { + // video: { + // url: message.content.url, + // }, + + // } + // await wbot.sendMessage(msg.key.remoteJid, media); + // } + } + if (input) { + if (input.type === 'choice input') { + let formattedText = ''; + const items = input.items; + for (const item of items) { + formattedText += `▶️ ${item.content}\n`; + } + formattedText = formattedText.replace(/\n$/, ''); + await wbot.presenceSubscribe(msg.key.remoteJid) + //await delay(2000) + await wbot.sendPresenceUpdate('composing', msg.key.remoteJid) + await delay(typebotDelayMessage) + await wbot.sendPresenceUpdate('paused', msg.key.remoteJid) + await wbot.sendMessage(msg.key.remoteJid, { text: formattedText }); + + } + } + } + } + if (body === typebotKeywordRestart) { + await ticket.update({ + isBot: true, + typebotSessionId: null + + }) + + await ticket.reload(); + + await wbot.sendMessage(`${number}@c.us`, { text: typebotRestartMessage }) + + } + if (body === typebotKeywordFinish) { + await UpdateTicketService({ + ticketData: { + status: "closed", + useIntegration: false, + integrationId: null + }, + ticketId: ticket.id, + companyId: ticket.companyId + }) + + return; + } + } catch (error) { + logger.info("Error on typebotListener: ", error); + await ticket.update({ + typebotSessionId: null + }) + throw error; + } +} + +export default typebotListener; diff --git a/backend/src/services/UserServices/AuthUserService.ts b/backend/src/services/UserServices/AuthUserService.ts new file mode 100644 index 0000000..463adb4 --- /dev/null +++ b/backend/src/services/UserServices/AuthUserService.ts @@ -0,0 +1,61 @@ +import User from "../../models/User"; +import AppError from "../../errors/AppError"; +import { + createAccessToken, + createRefreshToken +} from "../../helpers/CreateTokens"; +import { SerializeUser } from "../../helpers/SerializeUser"; +import Queue from "../../models/Queue"; +import Company from "../../models/Company"; +import Setting from "../../models/Setting"; + +interface SerializedUser { + id: number; + name: string; + email: string; + profile: string; + queues: Queue[]; + companyId: number; +} + +interface Request { + email: string; + password: string; +} + +interface Response { + serializedUser: SerializedUser; + token: string; + refreshToken: string; +} + +const AuthUserService = async ({ + email, + password +}: Request): Promise => { + const user = await User.findOne({ + where: { email }, + include: ["queues", { model: Company, include: [{ model: Setting }] }] + }); + + if (!user) { + throw new AppError("ERR_INVALID_CREDENTIALS", 401); + } + + if (!(await user.checkPassword(password))) { + throw new AppError("ERR_INVALID_CREDENTIALS", 401); + } + + const token = createAccessToken(user); + const refreshToken = createRefreshToken(user); + + const serializedUser = await SerializeUser(user); + + return { + serializedUser, + token, + refreshToken + }; +}; + +export default AuthUserService; diff --git a/backend/src/services/UserServices/CreateUserService.ts b/backend/src/services/UserServices/CreateUserService.ts new file mode 100644 index 0000000..273cd23 --- /dev/null +++ b/backend/src/services/UserServices/CreateUserService.ts @@ -0,0 +1,107 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import { SerializeUser } from "../../helpers/SerializeUser"; +import User from "../../models/User"; +import Plan from "../../models/Plan"; +import Company from "../../models/Company"; + +interface Request { + email: string; + password: string; + name: string; + queueIds?: number[]; + companyId?: number; + profile?: string; + whatsappId?: number; + allTicket?:string; +} + +interface Response { + email: string; + name: string; + id: number; + profile: string; +} + +const CreateUserService = async ({ + email, + password, + name, + queueIds = [], + companyId, + profile = "admin", + whatsappId, + allTicket +}: Request): Promise => { + if (companyId !== undefined) { + const company = await Company.findOne({ + where: { + id: companyId + }, + include: [{ model: Plan, as: "plan" }] + }); + + if (company !== null) { + const usersCount = await User.count({ + where: { + companyId + } + }); + + if (usersCount >= company.plan.users) { + throw new AppError( + `Número máximo de usuários já alcançado: ${usersCount}` + ); + } + } + } + + const schema = Yup.object().shape({ + name: Yup.string().required().min(2), + email: Yup.string() + .email() + .required() + .test( + "Check-email", + "An user with this email already exists.", + async value => { + if (!value) return false; + const emailExists = await User.findOne({ + where: { email: value } + }); + return !emailExists; + } + ), + password: Yup.string().required().min(5) + }); + + try { + await schema.validate({ email, password, name }); + } catch (err) { + throw new AppError(err.message); + } + + const user = await User.create( + { + email, + password, + name, + companyId, + profile, + whatsappId: whatsappId || null, + allTicket + }, + { include: ["queues", "company"] } + ); + + await user.$set("queues", queueIds); + + await user.reload(); + + const serializedUser = SerializeUser(user); + + return serializedUser; +}; + +export default CreateUserService; diff --git a/backend/src/services/UserServices/DeleteUserService.ts b/backend/src/services/UserServices/DeleteUserService.ts new file mode 100644 index 0000000..96a9a79 --- /dev/null +++ b/backend/src/services/UserServices/DeleteUserService.ts @@ -0,0 +1,29 @@ +import User from "../../models/User"; +import AppError from "../../errors/AppError"; +import Ticket from "../../models/Ticket"; +import UpdateDeletedUserOpenTicketsStatus from "../../helpers/UpdateDeletedUserOpenTicketsStatus"; + +const DeleteUserService = async ( + id: string | number, + companyId: number +): Promise => { + const user = await User.findOne({ + where: { id } + }); + + if (!user) { + throw new AppError("ERR_NO_USER_FOUND", 404); + } + + const userOpenTickets: Ticket[] = await user.$get("tickets", { + where: { status: "open" } + }); + + if (userOpenTickets.length > 0) { + UpdateDeletedUserOpenTicketsStatus(userOpenTickets, companyId); + } + + await user.destroy(); +}; + +export default DeleteUserService; diff --git a/backend/src/services/UserServices/ListUsersService.ts b/backend/src/services/UserServices/ListUsersService.ts new file mode 100644 index 0000000..51b43f7 --- /dev/null +++ b/backend/src/services/UserServices/ListUsersService.ts @@ -0,0 +1,64 @@ +import { Sequelize, Op } from "sequelize"; +import Queue from "../../models/Queue"; +import Company from "../../models/Company"; +import User from "../../models/User"; + +interface Request { + searchParam?: string; + pageNumber?: string | number; + profile?: string; + companyId?: number; +} + +interface Response { + users: User[]; + count: number; + hasMore: boolean; +} + +const ListUsersService = async ({ + searchParam = "", + pageNumber = "1", + companyId +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + "$User.name$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("User.name")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } } + ], + companyId: { + [Op.eq]: companyId + } + }; + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: users } = await User.findAndCountAll({ + where: whereCondition, + attributes: ["name", "id", "email", "companyId", "profile", "createdAt"], + limit, + offset, + order: [["createdAt", "DESC"]], + include: [ + { model: Queue, as: "queues", attributes: ["id", "name", "color"] }, + { model: Company, as: "company", attributes: ["id", "name"] } + ] + }); + + const hasMore = count > offset + users.length; + + return { + users, + count, + hasMore + }; +}; + +export default ListUsersService; diff --git a/backend/src/services/UserServices/ShowUserService.ts b/backend/src/services/UserServices/ShowUserService.ts new file mode 100644 index 0000000..0634491 --- /dev/null +++ b/backend/src/services/UserServices/ShowUserService.ts @@ -0,0 +1,32 @@ +import User from "../../models/User"; +import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; +import Company from "../../models/Company"; + +const ShowUserService = async (id: string | number): Promise => { + const user = await User.findByPk(id, { + attributes: [ + "name", + "id", + "email", + "companyId", + "profile", + "super", + "tokenVersion", + "whatsappId", + "allTicket" + ], + include: [ + { model: Queue, as: "queues", attributes: ["id", "name", "color"] }, + { model: Company, as: "company", attributes: ["id", "name"] } + ] + }); + + if (!user) { + throw new AppError("ERR_NO_USER_FOUND", 404); + } + + return user; +}; + +export default ShowUserService; diff --git a/backend/src/services/UserServices/SimpleListService.ts b/backend/src/services/UserServices/SimpleListService.ts new file mode 100644 index 0000000..a484d4f --- /dev/null +++ b/backend/src/services/UserServices/SimpleListService.ts @@ -0,0 +1,28 @@ +import User from "../../models/User"; +import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; + +interface Params { + companyId: string | number; +} + +const SimpleListService = async ({ companyId }: Params): Promise => { + const users = await User.findAll({ + where: { + companyId + }, + attributes: ["name", "id", "email"], + include: [ + { model: Queue, as: 'queues' } + ], + order: [["id", "ASC"]] + }); + + if (!users) { + throw new AppError("ERR_NO_USER_FOUND", 404); + } + + return users; +}; + +export default SimpleListService; diff --git a/backend/src/services/UserServices/UpdateUserService.ts b/backend/src/services/UserServices/UpdateUserService.ts new file mode 100644 index 0000000..9f05829 --- /dev/null +++ b/backend/src/services/UserServices/UpdateUserService.ts @@ -0,0 +1,91 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import ShowUserService from "./ShowUserService"; +import Company from "../../models/Company"; +import User from "../../models/User"; + +interface UserData { + email?: string; + password?: string; + name?: string; + profile?: string; + companyId?: number; + queueIds?: number[]; + whatsappId?: number; + allTicket?: string; +} + +interface Request { + userData: UserData; + userId: string | number; + companyId: number; + requestUserId: number; +} + +interface Response { + id: number; + name: string; + email: string; + profile: string; +} + +const UpdateUserService = async ({ + userData, + userId, + companyId, + requestUserId +}: Request): Promise => { + const user = await ShowUserService(userId); + + const requestUser = await User.findByPk(requestUserId); + + if (requestUser.super === false && userData.companyId !== companyId) { + throw new AppError("O usuário não pertence à esta empresa"); + } + + const schema = Yup.object().shape({ + name: Yup.string().min(2), + email: Yup.string().email(), + profile: Yup.string(), + password: Yup.string(), + allTicket: Yup.string() + }); + + const { email, password, profile, name, queueIds = [], whatsappId, allTicket } = userData; + + try { + await schema.validate({ email, password, profile, name, allTicket }); + } catch (err: any) { + throw new AppError(err.message); + } + + await user.update({ + email, + password, + profile, + name, + whatsappId: whatsappId || null, + allTicket + }); + + await user.$set("queues", queueIds); + + await user.reload(); + + const company = await Company.findByPk(user.companyId); + + const serializedUser = { + id: user.id, + name: user.name, + email: user.email, + profile: user.profile, + companyId: user.companyId, + company, + queues: user.queues + }; + + return serializedUser; +}; + +export default UpdateUserService; diff --git a/backend/src/services/WbotServices/CheckIsValidContact.ts b/backend/src/services/WbotServices/CheckIsValidContact.ts new file mode 100644 index 0000000..1b567b0 --- /dev/null +++ b/backend/src/services/WbotServices/CheckIsValidContact.ts @@ -0,0 +1,26 @@ +import AppError from "../../errors/AppError"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; + +const CheckIsValidContact = async ( + number: string, + companyId: number +): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(companyId); + + const wbot = getWbot(defaultWhatsapp.id); + + try { + const isValidNumber = await wbot.onWhatsApp(`${number}`); + if (!isValidNumber) { + throw new AppError("invalidNumber"); + } + } catch (err: any) { + if (err.message === "invalidNumber") { + throw new AppError("ERR_WAPP_INVALID_CONTACT"); + } + throw new AppError("ERR_WAPP_CHECK_CONTACT"); + } +}; + +export default CheckIsValidContact; diff --git a/backend/src/services/WbotServices/CheckNumber.ts b/backend/src/services/WbotServices/CheckNumber.ts new file mode 100644 index 0000000..be4ecef --- /dev/null +++ b/backend/src/services/WbotServices/CheckNumber.ts @@ -0,0 +1,29 @@ +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; + +interface IOnWhatsapp { + jid: string; + exists: boolean; +} + +const checker = async (number: string, wbot: any) => { + const [validNumber] = await wbot.onWhatsApp(`${number}@s.whatsapp.net`); + return validNumber; +}; + +const CheckContactNumber = async ( + number: string, + companyId: number +): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(companyId); + + const wbot = getWbot(defaultWhatsapp.id); + const isNumberExit = await checker(number, wbot); + + if (!isNumberExit.exists) { + throw new Error("ERR_CHECK_NUMBER"); + } + return isNumberExit; +}; + +export default CheckContactNumber; diff --git a/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts b/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts new file mode 100644 index 0000000..6c9497a --- /dev/null +++ b/backend/src/services/WbotServices/DeleteWhatsAppMessage.ts @@ -0,0 +1,51 @@ +import { proto, WASocket } from "@whiskeysockets/baileys"; +import WALegacySocket from "@whiskeysockets/baileys" +import AppError from "../../errors/AppError"; +import GetTicketWbot from "../../helpers/GetTicketWbot"; +import GetWbotMessage from "../../helpers/GetWbotMessage"; +import Message from "../../models/Message"; +import Ticket from "../../models/Ticket"; + +const DeleteWhatsAppMessage = async (messageId: string): Promise => { + const message = await Message.findByPk(messageId, { + include: [ + { + model: Ticket, + as: "ticket", + include: ["contact"] + } + ] + }); + + if (!message) { + throw new AppError("No message found with this ID."); + } + + const { ticket } = message; + + const messageToDelete = await GetWbotMessage(ticket, messageId); + + try { + const wbot = await GetTicketWbot(ticket); + const messageDelete = messageToDelete as proto.WebMessageInfo; + + const menssageDelete = messageToDelete as Message; + + await (wbot as WASocket).sendMessage(menssageDelete.remoteJid, { + delete: { + id: menssageDelete.id, + remoteJid: menssageDelete.remoteJid, + participant: menssageDelete.participant, + fromMe: menssageDelete.fromMe + } + }); + + } catch (err) { + throw new AppError("ERR_DELETE_WAPP_MSG"); + } + await message.update({ isDeleted: true }); + + return message; +}; + +export default DeleteWhatsAppMessage; diff --git a/backend/src/services/WbotServices/GetProfilePicUrl.ts b/backend/src/services/WbotServices/GetProfilePicUrl.ts new file mode 100644 index 0000000..4ae0fa9 --- /dev/null +++ b/backend/src/services/WbotServices/GetProfilePicUrl.ts @@ -0,0 +1,22 @@ +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; + +const GetProfilePicUrl = async ( + number: string, + companyId: number +): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(companyId); + + const wbot = getWbot(defaultWhatsapp.id); + + let profilePicUrl: string; + try { + profilePicUrl = await wbot.profilePictureUrl(`${number}@s.whatsapp.net`); + } catch (error) { + profilePicUrl = `${process.env.FRONTEND_URL}/nopicture.png`; + } + + return profilePicUrl; +}; + +export default GetProfilePicUrl; diff --git a/backend/src/services/WbotServices/ImportContactsService.ts b/backend/src/services/WbotServices/ImportContactsService.ts new file mode 100644 index 0000000..eb9f168 --- /dev/null +++ b/backend/src/services/WbotServices/ImportContactsService.ts @@ -0,0 +1,82 @@ +import * as Sentry from "@sentry/node"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; +import Contact from "../../models/Contact"; +import { logger } from "../../utils/logger"; +import ShowBaileysService from "../BaileysServices/ShowBaileysService"; +import CreateContactService from "../ContactServices/CreateContactService"; +import { isString, isArray } from "lodash"; +import path from "path"; +import fs from 'fs'; + +const ImportContactsService = async (companyId: number): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(companyId); + const wbot = getWbot(defaultWhatsapp.id); + + let phoneContacts; + + try { + const contactsString = await ShowBaileysService(wbot.id); + phoneContacts = JSON.parse(JSON.stringify(contactsString.contacts)); + + const publicFolder = path.resolve(__dirname, "..", "..", "..", "public"); + const beforeFilePath = path.join(publicFolder, 'contatos_antes.txt'); + fs.writeFile(beforeFilePath, JSON.stringify(phoneContacts, null, 2), (err) => { + if (err) { + logger.error(`Failed to write contacts to file: ${err}`); + throw err; + } + console.log('O arquivo contatos_antes.txt foi criado!'); + }); + + } catch (err) { + Sentry.captureException(err); + logger.error(`Could not get whatsapp contacts from phone. Err: ${err}`); + } + + const publicFolder = path.resolve(__dirname, "..", "..", "..", "public"); + const afterFilePath = path.join(publicFolder, 'contatos_depois.txt'); + fs.writeFile(afterFilePath, JSON.stringify(phoneContacts, null, 2), (err) => { + if (err) { + logger.error(`Failed to write contacts to file: ${err}`); + throw err; + } + }); + + const phoneContactsList = isString(phoneContacts) + ? JSON.parse(phoneContacts) + : phoneContacts; + + if (isArray(phoneContactsList)) { + phoneContactsList.forEach(async ({ id, name, notify }) => { + if (id === "status@broadcast" || id.includes("g.us")) return; + const number = id.replace(/\D/g, ""); + + const existingContact = await Contact.findOne({ + where: { number, companyId } + }); + + if (existingContact) { + // Atualiza o nome do contato existente + existingContact.name = name || notify; + await existingContact.save(); + } else { + // Criar um novo contato + try { + await CreateContactService({ + number, + name: name || notify, + companyId + }); + } catch (error) { + Sentry.captureException(error); + logger.warn( + `Could not get whatsapp contacts from phone. Err: ${error}` + ); + } + } + }); + } +}; + +export default ImportContactsService; diff --git a/backend/src/services/WbotServices/SendWhatsAppMedia.ts b/backend/src/services/WbotServices/SendWhatsAppMedia.ts new file mode 100644 index 0000000..ff6f28b --- /dev/null +++ b/backend/src/services/WbotServices/SendWhatsAppMedia.ts @@ -0,0 +1,191 @@ +import { WAMessage, AnyMessageContent } from "@whiskeysockets/baileys"; +import * as Sentry from "@sentry/node"; +import fs from "fs"; +import { exec } from "child_process"; +import path from "path"; +import ffmpegPath from "@ffmpeg-installer/ffmpeg"; +import AppError from "../../errors/AppError"; +import GetTicketWbot from "../../helpers/GetTicketWbot"; +import Ticket from "../../models/Ticket"; +import mime from "mime-types"; +import formatBody from "../../helpers/Mustache"; + +interface Request { + media: Express.Multer.File; + ticket: Ticket; + body?: string; +} + +const publicFolder = path.resolve(__dirname, "..", "..", "..", "public"); + +const processAudio = async (audio: string): Promise => { + const outputAudio = `${publicFolder}/${new Date().getTime()}.mp3`; + return new Promise((resolve, reject) => { + exec( + `${ffmpegPath.path} -i ${audio} -vn -ab 128k -ar 44100 -f ipod ${outputAudio} -y`, + (error, _stdout, _stderr) => { + if (error) reject(error); + fs.unlinkSync(audio); + resolve(outputAudio); + } + ); + }); +}; + +const processAudioFile = async (audio: string): Promise => { + const outputAudio = `${publicFolder}/${new Date().getTime()}.mp3`; + return new Promise((resolve, reject) => { + exec( + `${ffmpegPath.path} -i ${audio} -vn -ar 44100 -ac 2 -b:a 192k ${outputAudio}`, + (error, _stdout, _stderr) => { + if (error) reject(error); + fs.unlinkSync(audio); + resolve(outputAudio); + } + ); + }); +}; + +export const getMessageOptions = async ( + fileName: string, + pathMedia: string, + body?: string +): Promise => { + const mimeType = mime.lookup(pathMedia); + const typeMessage = mimeType.split("/")[0]; + + try { + if (!mimeType) { + throw new Error("Invalid mimetype"); + } + let options: AnyMessageContent; + + if (typeMessage === "video") { + options = { + video: fs.readFileSync(pathMedia), + caption: body ? body : '', + fileName: fileName + // gifPlayback: true + }; + } else if (typeMessage === "audio") { + const typeAudio = true; //fileName.includes("audio-record-site"); + const convert = await processAudio(pathMedia); + if (typeAudio) { + options = { + audio: fs.readFileSync(convert), + mimetype: typeAudio ? "audio/mp4" : mimeType, + caption: body ? body : null, + ptt: true + }; + } else { + options = { + audio: fs.readFileSync(convert), + mimetype: typeAudio ? "audio/mp4" : mimeType, + caption: body ? body : null, + ptt: true + }; + } + } else if (typeMessage === "document") { + options = { + document: fs.readFileSync(pathMedia), + caption: body ? body : null, + fileName: fileName, + mimetype: mimeType + }; + } else if (typeMessage === "application") { + options = { + document: fs.readFileSync(pathMedia), + caption: body ? body : null, + fileName: fileName, + mimetype: mimeType + }; + } else { + options = { + image: fs.readFileSync(pathMedia), + caption: body ? body : null + }; + } + + return options; + } catch (e) { + Sentry.captureException(e); + console.log(e); + return null; + } +}; + +const SendWhatsAppMedia = async ({ + media, + ticket, + body +}: Request): Promise => { + try { + const wbot = await GetTicketWbot(ticket); + + const pathMedia = media.path; + const typeMessage = media.mimetype.split("/")[0]; + let options: AnyMessageContent; + const bodyMessage = formatBody(body, ticket.contact) + + if (typeMessage === "video") { + options = { + video: fs.readFileSync(pathMedia), + caption: bodyMessage, + fileName: media.originalname + // gifPlayback: true + }; + } else if (typeMessage === "audio") { + const typeAudio = media.originalname.includes("audio-record-site"); + if (typeAudio) { + const convert = await processAudio(media.path); + options = { + audio: fs.readFileSync(convert), + mimetype: typeAudio ? "audio/mp4" : media.mimetype, + ptt: true + }; + } else { + const convert = await processAudioFile(media.path); + options = { + audio: fs.readFileSync(convert), + mimetype: typeAudio ? "audio/mp4" : media.mimetype + }; + } + } else if (typeMessage === "document" || typeMessage === "text") { + options = { + document: fs.readFileSync(pathMedia), + caption: bodyMessage, + fileName: media.originalname, + mimetype: media.mimetype + }; + } else if (typeMessage === "application") { + options = { + document: fs.readFileSync(pathMedia), + caption: bodyMessage, + fileName: media.originalname, + mimetype: media.mimetype + }; + } else { + options = { + image: fs.readFileSync(pathMedia), + caption: bodyMessage, + }; + } + + const sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + ...options + } + ); + + await ticket.update({ lastMessage: bodyMessage }); + + return sentMessage; + } catch (err) { + Sentry.captureException(err); + console.log(err); + throw new AppError("ERR_SENDING_WAPP_MSG"); + } +}; + +export default SendWhatsAppMedia; diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts new file mode 100644 index 0000000..c770c42 --- /dev/null +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -0,0 +1,66 @@ +import { WAMessage } from "@whiskeysockets/baileys"; +import WALegacySocket from "@whiskeysockets/baileys" +import * as Sentry from "@sentry/node"; +import AppError from "../../errors/AppError"; +import GetTicketWbot from "../../helpers/GetTicketWbot"; +import Message from "../../models/Message"; +import Ticket from "../../models/Ticket"; + +import formatBody from "../../helpers/Mustache"; + +interface Request { + body: string; + ticket: Ticket; + quotedMsg?: Message; +} + +const SendWhatsAppMessage = async ({ + body, + ticket, + quotedMsg +}: Request): Promise => { + let options = {}; + const wbot = await GetTicketWbot(ticket); + const number = `${ticket.contact.number}@${ + ticket.isGroup ? "g.us" : "s.whatsapp.net" + }`; + if (quotedMsg) { + const chatMessages = await Message.findOne({ + where: { + id: quotedMsg.id + } + }); + + if (chatMessages) { + const msgFound = JSON.parse(chatMessages.dataJson); + + options = { + quoted: { + key: msgFound.key, + message: { + extendedTextMessage: msgFound.message.extendedTextMessage + } + } + }; + } + + } + + try { + const sentMessage = await wbot.sendMessage(number,{ + text: formatBody(body, ticket.contact) + }, + { + ...options + } + ); + await ticket.update({ lastMessage: formatBody(body, ticket.contact) }); + return sentMessage; + } catch (err) { + Sentry.captureException(err); + console.log(err); + throw new AppError("ERR_SENDING_WAPP_MSG"); + } +}; + +export default SendWhatsAppMessage; diff --git a/backend/src/services/WbotServices/StartAllWhatsAppsSessions.ts b/backend/src/services/WbotServices/StartAllWhatsAppsSessions.ts new file mode 100644 index 0000000..913509f --- /dev/null +++ b/backend/src/services/WbotServices/StartAllWhatsAppsSessions.ts @@ -0,0 +1,18 @@ +import ListWhatsAppsService from "../WhatsappService/ListWhatsAppsService"; +import { StartWhatsAppSession } from "./StartWhatsAppSession"; +import * as Sentry from "@sentry/node"; + +export const StartAllWhatsAppsSessions = async ( + companyId: number +): Promise => { + try { + const whatsapps = await ListWhatsAppsService({ companyId }); + if (whatsapps.length > 0) { + whatsapps.forEach(whatsapp => { + StartWhatsAppSession(whatsapp, companyId); + }); + } + } catch (e) { + Sentry.captureException(e); + } +}; diff --git a/backend/src/services/WbotServices/StartWhatsAppSession.ts b/backend/src/services/WbotServices/StartWhatsAppSession.ts new file mode 100644 index 0000000..ad08cb0 --- /dev/null +++ b/backend/src/services/WbotServices/StartWhatsAppSession.ts @@ -0,0 +1,29 @@ +import { initWASocket } from "../../libs/wbot"; +import Whatsapp from "../../models/Whatsapp"; +import { wbotMessageListener } from "./wbotMessageListener"; +import { getIO } from "../../libs/socket"; +import wbotMonitor from "./wbotMonitor"; +import { logger } from "../../utils/logger"; +import * as Sentry from "@sentry/node"; + +export const StartWhatsAppSession = async ( + whatsapp: Whatsapp, + companyId: number +): Promise => { + await whatsapp.update({ status: "OPENING" }); + + const io = getIO(); + io.to(`company-${whatsapp.companyId}-mainchannel`).emit("whatsappSession", { + action: "update", + session: whatsapp + }); + + try { + const wbot = await initWASocket(whatsapp); + wbotMessageListener(wbot, companyId); + wbotMonitor(wbot, whatsapp, companyId); + } catch (err) { + Sentry.captureException(err); + logger.error(err); + } +}; diff --git a/backend/src/services/WbotServices/providers.ts b/backend/src/services/WbotServices/providers.ts new file mode 100644 index 0000000..b8ee4c0 --- /dev/null +++ b/backend/src/services/WbotServices/providers.ts @@ -0,0 +1,1527 @@ +import { proto, WASocket } from "@whiskeysockets/baileys"; +import Contact from "../../models/Contact"; +import Setting from "../../models/Setting"; +import Ticket from "../../models/Ticket"; +import { getBodyMessage, isNumeric, sleep, validaCpfCnpj, sendMessageImage, sendMessageLink, makeid } from "./wbotMessageListener"; +import formatBody from "../../helpers/Mustache"; + +import puppeteer from "puppeteer"; + +import axios from 'axios'; +import UpdateTicketService from "../TicketServices/UpdateTicketService"; +import fs from 'fs'; + +export const provider = async (ticket: Ticket, msg: proto.IWebMessageInfo, companyId: number, contact: Contact, wbot: WASocket) => { + const filaescolhida = ticket.queue?.name + if (filaescolhida === "2ª Via de Boleto" || filaescolhida === "2 Via de Boleto") { + let cpfcnpj + cpfcnpj = getBodyMessage(msg); + cpfcnpj = cpfcnpj.replace(/\./g, ''); + cpfcnpj = cpfcnpj.replace('-', '') + cpfcnpj = cpfcnpj.replace('/', '') + cpfcnpj = cpfcnpj.replace(' ', '') + cpfcnpj = cpfcnpj.replace(',', '') + + const asaastoken = await Setting.findOne({ + where: { + key: "asaas", + companyId + } + }); + const ixcapikey = await Setting.findOne({ + where: { + key: "tokenixc", + companyId + } + }); + const urlixcdb = await Setting.findOne({ + where: { + key: "ipixc", + companyId + } + }); + const ipmkauth = await Setting.findOne({ + where: { + key: "ipmkauth", + companyId + } + }); + const clientidmkauth = await Setting.findOne({ + where: { + key: "clientidmkauth", + companyId + } + }); + const clientesecretmkauth = await Setting.findOne({ + where: { + key: "clientsecretmkauth", + companyId + } + }); + + let urlmkauth = ipmkauth.value + if (urlmkauth.substr(-1) === '/') { + urlmkauth = urlmkauth.slice(0, -1); + } + + //VARS + let url = `${urlmkauth}/api/`; + const Client_Id = clientidmkauth.value + const Client_Secret = clientesecretmkauth.value + const ixckeybase64 = btoa(ixcapikey.value); + const urlixc = urlixcdb.value + const asaastk = asaastoken.value + + const cnpj_cpf = getBodyMessage(msg); + let numberCPFCNPJ = cpfcnpj; + + if (urlmkauth != "" && Client_Id != "" && Client_Secret != "") { + if (isNumeric(numberCPFCNPJ) === true) { + if (cpfcnpj.length > 2) { + const isCPFCNPJ = validaCpfCnpj(numberCPFCNPJ) + if (isCPFCNPJ) { + const textMessage = { + text: formatBody(`Aguarde! Estamos consultando na base de dados!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, textMessage); + } catch (error) { + + } + + + axios({ + rejectUnauthorized: true, + method: 'get', + url, + auth: { + username: Client_Id, + password: Client_Secret + } + } as any) + .then(function (response) { + const jtw = response.data + var config = { + method: 'GET', + url: `${urlmkauth}/api/cliente/show/${numberCPFCNPJ}`, + headers: { + Authorization: `Bearer ${jtw}` + } + }; + axios.request(config as any) + .then(async function (response) { + if (response.data == 'NULL') { + const textMessage = { + text: formatBody(`Cadastro não localizado! *CPF/CNPJ* incorreto ou inválido. Tenta novamente!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, textMessage); + } catch (error) { + console.log('Não consegui enviar a mensagem!') + } + } else { + let nome + let cpf_cnpj + let qrcode + let valor + let bloqueado + let linhadig + let uuid_cliente + let referencia + let status + let datavenc + let descricao + let titulo + let statusCorrigido + let valorCorrigido + + nome = response.data.dados_cliente.titulos.nome + cpf_cnpj = response.data.dados_cliente.titulos.cpf_cnpj + valor = response.data.dados_cliente.titulos.valor + bloqueado = response.data.dados_cliente.titulos.bloqueado + uuid_cliente = response.data.dados_cliente.titulos.uuid_cliente + qrcode = response.data.dados_cliente.titulos.qrcode + linhadig = response.data.dados_cliente.titulos.linhadig + referencia = response.data.dados_cliente.titulos.referencia + status = response.data.dados_cliente.titulos.status + datavenc = response.data.dados_cliente.titulos.datavenc + descricao = response.data.dados_cliente.titulos.descricao + titulo = response.data.dados_cliente.titulos.titulo + statusCorrigido = status[0].toUpperCase() + status.substr(1); + valorCorrigido = valor.replace(".", ","); + + var curdate = new Date(datavenc) + const mesCorreto = curdate.getMonth() + 1 + const ano = ('0' + curdate.getFullYear()).slice(-4) + const mes = ('0' + mesCorreto).slice(-2) + const dia = ('0' + curdate.getDate()).slice(-2) + const anoMesDia = `${dia}/${mes}/${ano}` + + try { + const textMessage = { text: formatBody(`Localizei seu Cadastro! *${nome}* só mais um instante por favor!`, contact) }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, textMessage); + const bodyBoleto = { text: formatBody(`Segue a segunda-via da sua Fatura!\n\n*Nome:* ${nome}\n*Valor:* R$ ${valorCorrigido}\n*Data Vencimento:* ${anoMesDia}\n*Link:* ${urlmkauth}/boleto/21boleto.php?titulo=${titulo}\n\nVou mandar o *código de barras* na próxima mensagem para ficar mais fácil para você copiar!`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoleto); + const bodyLinha = { text: formatBody(`${linhadig}`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyLinha); + if (qrcode !== null) { + const bodyPdf = { text: formatBody(`Este é o *PIX COPIA E COLA*`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyqrcode = { text: formatBody(`${qrcode}`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + let linkBoleto = `https://chart.googleapis.com/chart?cht=qr&chs=500x500&chld=L|0&chl=${qrcode}` + await sleep(2000) + await sendMessageImage(wbot, contact, ticket, linkBoleto, "") + } + const bodyPdf = { text: formatBody(`Agora vou te enviar o boleto em *PDF* caso você precise.`, contact) }; + await sleep(2000) + const bodyPdfQr = { text: formatBody(`${bodyPdf}`, contact) }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdfQr); + await sleep(2000) + + //GERA O PDF + const nomePDF = `Boleto-${nome}-${dia}-${mes}-${ano}.pdf`; + (async () => { + const browser = await puppeteer.launch({ args: ['--no-sandbox'] }); + const page = await browser.newPage(); + const website_url = `${urlmkauth}/boleto/21boleto.php?titulo=${titulo}`; + await page.goto(website_url, { waitUntil: 'networkidle0' }); + await page.emulateMediaType('screen'); + // Downlaod the PDF + const pdf = await page.pdf({ + path: nomePDF, + printBackground: true, + format: 'A4', + }); + + await browser.close(); + await sendMessageLink(wbot, contact, ticket, nomePDF, nomePDF); + }); + + + if (bloqueado === 'sim') { + const bodyBloqueio = { text: formatBody(`${nome} vi tambem que a sua conexão esta bloqueada! Vou desbloquear para você por *48 horas*.`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBloqueio); + const bodyqrcode = { text: formatBody(`Estou liberando seu acesso. Por favor aguarde!`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + var optionsdesbloq = { + method: 'GET', + url: `${urlmkauth}/api/cliente/desbloqueio/${uuid_cliente}`, + headers: { + Authorization: `Bearer ${jtw}` + } + }; + axios.request(optionsdesbloq as any).then(async function (response) { + const bodyLiberado = { text: formatBody(`Pronto liberei! Vou precisar que você *retire* seu equipamento da tomada.\n\n*OBS: Somente retire da tomada.* \nAguarde 1 minuto e ligue novamente!`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyLiberado); + const bodyqrcode = { text: formatBody(`Veja se seu acesso voltou! Caso nao tenha voltado retorne o contato e fale com um atendente!`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + }).catch(async function (error) { + const bodyfinaliza = { text: formatBody(`Opss! Algo de errado aconteceu! Digite *#* para voltar ao menu anterior e fale com um atendente!`, contact) }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + }); + } + + + const bodyfinaliza = { text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact) }; + await sleep(12000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + + await sleep(2000) + fs.unlink(nomePDF, function (err) { + if (err) throw err; + console.log(err); + }) + + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + + } catch (error) { + console.log('11 Não consegui enviar a mensagem!') + } + } + }) + .catch(async function (error) { + try { + const bodyBoleto = { text: formatBody(`Não consegui encontrar seu cadastro.\n\nPor favor tente novamente!\nOu digite *#* para voltar ao *Menu Anterior*`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoleto); + } catch (error) { + console.log('111 Não consegui enviar a mensagem!') + } + + }); + }) + .catch(async function (error) { + const bodyfinaliza = { text: formatBody(`Opss! Algo de errado aconteceu! Digite *#* para voltar ao menu anterior e fale com um atendente!`, contact) }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + }); + } else { + const body = { text: formatBody(`Este CPF/CNPJ não é válido!\n\nPor favor tente novamente!\nOu digite *#* para voltar ao *Menu Anterior*`, contact) }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } + } + } + } + + if (asaastoken.value !== "") { + if (isNumeric(numberCPFCNPJ) === true) { + if (cpfcnpj.length > 2) { + const isCPFCNPJ = validaCpfCnpj(numberCPFCNPJ) + if (isCPFCNPJ) { + const body = { + text: formatBody(`Aguarde! Estamos consultando na base de dados!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } catch (error) { + } + var optionsc = { + method: 'GET', + url: 'https://www.asaas.com/api/v3/customers', + params: { cpfCnpj: numberCPFCNPJ }, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsc as any).then(async function (response) { + let nome; + let id_cliente; + let totalCount; + + nome = response?.data?.data[0]?.name; + id_cliente = response?.data?.data[0]?.id; + totalCount = response?.data?.totalCount; + + if (totalCount === 0) { + const body = { + text: formatBody(`Cadastro não localizado! *CPF/CNPJ* incorreto ou inválido. Tenta novamente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } else { + + const body = { + text: formatBody(`Localizei seu Cadastro! \n*${nome}* só mais um instante por favor!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + var optionsListpaymentOVERDUE = { + method: 'GET', + url: 'https://www.asaas.com/api/v3/payments', + params: { customer: id_cliente, status: 'OVERDUE' }, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsListpaymentOVERDUE as any).then(async function (response) { + let totalCount_overdue; + totalCount_overdue = response?.data?.totalCount; + + if (totalCount_overdue === 0) { + const body = { + text: formatBody(`Você não tem nenhuma fatura vencidada! \nVou te enviar a proxima fatura. Por favor aguarde!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + var optionsPENDING = { + method: 'GET', + url: 'https://www.asaas.com/api/v3/payments', + params: { customer: id_cliente, status: 'PENDING' }, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsPENDING as any).then(async function (response) { + function sortfunction(a, b) { + return a.dueDate.localeCompare(b.dueDate); + } + const ordenado = response?.data?.data.sort(sortfunction); + let id_payment_pending; + let value_pending; + let description_pending; + let invoiceUrl_pending; + let dueDate_pending; + let invoiceNumber_pending; + let totalCount_pending; + let value_pending_corrigida; + let dueDate_pending_corrigida; + + id_payment_pending = ordenado[0]?.id; + value_pending = ordenado[0]?.value; + description_pending = ordenado[0]?.description; + invoiceUrl_pending = ordenado[0]?.invoiceUrl; + dueDate_pending = ordenado[0]?.dueDate; + invoiceNumber_pending = ordenado[0]?.invoiceNumber; + totalCount_pending = response?.data?.totalCount; + + dueDate_pending_corrigida = dueDate_pending?.split('-')?.reverse()?.join('/'); + value_pending_corrigida = value_pending.toLocaleString('pt-br', { style: 'currency', currency: 'BRL' }); + + const bodyBoleto = { + text: formatBody(`Segue a segunda-via da sua Fatura!\n\n*Fatura:* ${invoiceNumber_pending}\n*Nome:* ${nome}\n*Valor:* R$ ${value_pending_corrigida}\n*Data Vencimento:* ${dueDate_pending_corrigida}\n*Descrição:*\n${description_pending}\n*Link:* ${invoiceUrl_pending}`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoleto); + //GET DADOS PIX + var optionsGetPIX = { + method: 'GET', + url: `https://www.asaas.com/api/v3/payments/${id_payment_pending}/pixQrCode`, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsGetPIX as any).then(async function (response) { + let success; + let payload; + + success = response?.data?.success; + payload = response?.data?.payload; + + if (success === true) { + const bodyPixCP = { + text: formatBody(`Este é o *PIX Copia e Cola*`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPixCP); + const bodyPix = { + text: formatBody(`${payload}`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPix); + let linkBoleto = `https://chart.googleapis.com/chart?cht=qr&chs=500x500&chld=L|0&chl=${payload}` + await sleep(2000) + await sendMessageImage(wbot, contact, ticket, linkBoleto, '') + var optionsBoletopend = { + method: 'GET', + url: `https://www.asaas.com/api/v3/payments/${id_payment_pending}/identificationField`, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsBoletopend as any).then(async function (response) { + let codigo_barras + codigo_barras = response.data.identificationField; + const bodycodigoBarras = { + text: formatBody(`${codigo_barras}`, contact), + }; + if (response.data?.errors?.code !== 'invalid_action') { + const bodycodigo = { + text: formatBody(`Este é o *Código de Barras*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodycodigo); + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodycodigoBarras); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await sleep(2000) + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } else { + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + + }).catch(async function (error) { + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + }); + } + + }).catch(async function (error) { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + }); + + }).catch(async function (error) { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + }); + } else { + let id_payment_overdue; + let value_overdue; + let description_overdue; + let invoiceUrl_overdue; + let dueDate_overdue; + let invoiceNumber_overdue; + + let value_overdue_corrigida; + let dueDate_overdue_corrigida; + + id_payment_overdue = response?.data?.data[0]?.id; + value_overdue = response?.data?.data[0]?.value; + description_overdue = response?.data?.data[0]?.description; + invoiceUrl_overdue = response?.data?.data[0]?.invoiceUrl; + dueDate_overdue = response?.data?.data[0]?.dueDate; + invoiceNumber_overdue = response?.data?.data[0]?.invoiceNumber; + + + dueDate_overdue_corrigida = dueDate_overdue?.split('-')?.reverse()?.join('/'); + value_overdue_corrigida = value_overdue.toLocaleString('pt-br', { style: 'currency', currency: 'BRL' }); + const body = { + text: formatBody(`Você tem *${totalCount_overdue}* fatura(s) vencidada(s)! \nVou te enviar. Por favor aguarde!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + const bodyBoleto = { + text: formatBody(`Segue a segunda-via da sua Fatura!\n\n*Fatura:* ${invoiceNumber_overdue}\n*Nome:* ${nome}\n*Valor:* R$ ${value_overdue_corrigida}\n*Data Vencimento:* ${dueDate_overdue_corrigida}\n*Descrição:*\n${description_overdue}\n*Link:* ${invoiceUrl_overdue}`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoleto); + //GET DADOS PIX + var optionsGetPIX = { + method: 'GET', + url: `https://www.asaas.com/api/v3/payments/${id_payment_overdue}/pixQrCode`, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsGetPIX as any).then(async function (response) { + let success; + let payload; + + success = response?.data?.success; + payload = response?.data?.payload; + if (success === true) { + + const bodyPixCP = { + text: formatBody(`Este é o *PIX Copia e Cola*`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPixCP); + const bodyPix = { + text: formatBody(`${payload}`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPix); + let linkBoleto = `https://chart.googleapis.com/chart?cht=qr&chs=500x500&chld=L|0&chl=${payload}` + await sleep(2000) + await sendMessageImage(wbot, contact, ticket, linkBoleto, '') + var optionsBoleto = { + method: 'GET', + url: `https://www.asaas.com/api/v3/payments/${id_payment_overdue}/identificationField`, + headers: { + 'Content-Type': 'application/json', + access_token: asaastk + } + }; + + axios.request(optionsBoleto as any).then(async function (response) { + + let codigo_barras + codigo_barras = response.data.identificationField; + const bodycodigoBarras = { + text: formatBody(`${codigo_barras}`, contact), + }; + if (response.data?.errors?.code !== 'invalid_action') { + const bodycodigo = { + text: formatBody(`Este é o *Código de Barras*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodycodigo); + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodycodigoBarras); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } else { + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + + }).catch(function (error) { + //console.error(error); + }); + + } + }).catch(function (error) { + + }); + + } + + }).catch(async function (error) { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + }); + } + }).catch(async function (error) { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + }); + } + } + } + } + + if (ixcapikey.value != "" && urlixcdb.value != "") { + if (isNumeric(numberCPFCNPJ) === true) { + if (cpfcnpj.length > 2) { + const isCPFCNPJ = validaCpfCnpj(numberCPFCNPJ) + if (isCPFCNPJ) { + if (numberCPFCNPJ.length <= 11) { + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{3})(\d)/, "$1.$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{3})(\d)/, "$1.$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{3})(\d{1,2})$/, "$1-$2") + } else { + numberCPFCNPJ = numberCPFCNPJ.replace(/^(\d{2})(\d)/, "$1.$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3") + numberCPFCNPJ = numberCPFCNPJ.replace(/\.(\d{3})(\d)/, ".$1/$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{4})(\d)/, "$1-$2") + } + //const token = await CheckSettingsHelper("OBTEM O TOKEN DO BANCO (dei insert na tabela settings)") + const body = { + text: formatBody(`Aguarde! Estamos consultando na base de dados!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } catch (error) { + } + var options = { + method: 'GET', + url: `${urlixc}/webservice/v1/cliente`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'cliente.cnpj_cpf', + query: numberCPFCNPJ, + oper: '=', + page: '1', + rp: '1', + sortname: 'cliente.cnpj_cpf', + sortorder: 'asc' + } + }; + + axios.request(options as any).then(async function (response) { + if (response.data.type === 'error') { + console.log("Error response", response.data.message); + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } if (response.data.total === 0) { + const body = { + text: formatBody(`Cadastro não localizado! *CPF/CNPJ* incorreto ou inválido. Tenta novamente!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } catch (error) { + } + } else { + + let nome; + let id; + let type; + + nome = response.data?.registros[0]?.razao + id = response.data?.registros[0]?.id + type = response.data?.type + + + const body = { + text: formatBody(`Localizei seu Cadastro! \n*${nome}* só mais um instante por favor!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + var boleto = { + method: 'GET', + url: `${urlixc}/webservice/v1/fn_areceber`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'fn_areceber.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'fn_areceber.data_vencimento', + sortorder: 'asc', + grid_param: '[{"TB":"fn_areceber.status", "OP" : "=", "P" : "A"}]' + } + }; + axios.request(boleto as any).then(async function (response) { + + + + let gateway_link; + let valor; + let datavenc; + let datavencCorrigida; + let valorCorrigido; + let linha_digitavel; + let impresso; + let idboleto; + + idboleto = response.data?.registros[0]?.id + gateway_link = response.data?.registros[0]?.gateway_link + valor = response.data?.registros[0]?.valor + datavenc = response.data?.registros[0]?.data_vencimento + linha_digitavel = response.data?.registros[0]?.linha_digitavel + impresso = response.data?.registros[0]?.impresso + valorCorrigido = valor.replace(".", ","); + datavencCorrigida = datavenc.split('-').reverse().join('/') + + + //INFORMAÇÕES BOLETO + const bodyBoleto = { + text: formatBody(`Segue a segunda-via da sua Fatura!\n\n*Fatura:* ${idboleto}\n*Nome:* ${nome}\n*Valor:* R$ ${valorCorrigido}\n*Data Vencimento:* ${datavencCorrigida}\n\nVou mandar o *código de barras* na próxima mensagem para ficar mais fácil para você copiar!`, contact), + }; + //await sleep(2000) + //await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoleto); + //LINHA DIGITAVEL + if (impresso !== "S") { + //IMPRIME BOLETO PARA GERAR CODIGO BARRAS + var boletopdf = { + method: 'GET', + url: `${urlixc}/webservice/v1/get_boleto`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + boletos: idboleto, + juro: 'N', + multa: 'N', + atualiza_boleto: 'N', + tipo_boleto: 'arquivo', + base64: 'S' + } + }; + + axios.request(boletopdf as any).then(function (response) { + }).catch(function (error) { + console.error(error); + }); + } + + //SE TIVER PIX ENVIA O PIX + var optionsPix = { + method: 'GET', + url: `${urlixc}/webservice/v1/get_pix`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { id_areceber: idboleto } + }; + + axios.request(optionsPix as any).then(async function (response) { + let tipo; + let pix; + + tipo = response.data?.type; + pix = response.data?.pix?.qrCode?.qrcode; + if (tipo === 'success') { + const bodyBoletoPix = { + text: formatBody(`Segue a segunda-via da sua Fatura!\n\n*Fatura:* ${idboleto}\n*Nome:* ${nome}\n*Valor:* R$ ${valorCorrigido}\n*Data Vencimento:* ${datavencCorrigida}\n\nVou te enviar o *Código de Barras* e o *PIX* basta clicar em qual você quer utlizar que já vai copiar! Depois basta realizar o pagamento no seu banco`, contact), + }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoletoPix); + const body_linhadigitavel = { + text: formatBody("Este é o *Código de Barras*", contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_linhadigitavel); + await sleep(2000) + const body_linha_digitavel = { + text: formatBody(`${linha_digitavel}`, contact), + }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_linha_digitavel); + const body_pix = { + text: formatBody("Este é o *PIX Copia e Cola*", contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_pix); + await sleep(2000) + const body_pix_dig = { + text: formatBody(`${pix}`, contact), + }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_pix_dig); + const body_pixqr = { + text: formatBody("QR CODE do *PIX*", contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_pixqr); + let linkBoleto = `https://chart.googleapis.com/chart?cht=qr&chs=500x500&chld=L|0&chl=${pix}` + await sleep(2000) + await sendMessageImage(wbot, contact, ticket, linkBoleto, '') + ///VE SE ESTA BLOQUEADO PARA LIBERAR! + var optionscontrato = { + method: 'POST', + url: `${urlixc}/webservice/v1/cliente_contrato`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'cliente_contrato.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'cliente_contrato.id', + sortorder: 'asc' + } + }; + axios.request(optionscontrato as any).then(async function (response) { + let status_internet; + let id_contrato; + status_internet = response.data?.registros[0]?.status_internet; + id_contrato = response.data?.registros[0]?.id; + if (status_internet !== 'A') { + const bodyPdf = { + text: formatBody(`*${nome}* vi tambem que a sua conexão esta bloqueada! Vou desbloquear para você.`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyqrcode = { + text: formatBody(`Estou liberando seu acesso. Por favor aguarde!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + //REALIZANDO O DESBLOQUEIO + var optionsdesbloqeuio = { + method: 'POST', + url: `${urlixc}/webservice/v1/desbloqueio_confianca`, + headers: { + Authorization: `Basic ${ixckeybase64}` + }, + data: { id: id_contrato } + }; + + axios.request(optionsdesbloqeuio as any).then(async function (response) { + let tipo; + let mensagem; + tipo = response.data?.tipo; + mensagem = response.data?.mensagem; + if (tipo === 'sucesso') { + //DESCONECTANDO O CLIENTE PARA VOLTAR O ACESSO + var optionsRadius = { + method: 'GET', + url: `${urlixc}/webservice/v1/radusuarios`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'radusuarios.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'radusuarios.id', + sortorder: 'asc' + } + }; + + axios.request(optionsRadius as any).then(async function (response) { + let tipo; + tipo = response.data?.type; + if (tipo === 'success') { + const body_mensagem = { + text: formatBody(`${mensagem}`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_mensagem); + const bodyPdf = { + text: formatBody(`Fiz os procedimentos de liberação! Agora aguarde até 5 minutos e veja se sua conexão irá retornar! .\n\nCaso não tenha voltado, retorne o contato e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + }).catch(function (error) { + console.error(error); + }); + //FIM DA DESCONEXÃO + } else { + var msgerrolbieracao = response.data.mensagem + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro e nao consegui desbloquear`, contact), + }; + const msg_errolbieracao = { + text: formatBody(`${msgerrolbieracao}`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, msg_errolbieracao); + const bodyerroatendent = { + text: formatBody(`Digite *#* para voltar o menu e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerroatendent); + } + + }).catch(async function (error) { + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + }); + } else { + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(8000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + + // + }).catch(async function (error) { + + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + }); + ///VE SE ESTA BLOQUEADO PARA LIBERAR! + } else { + const bodyBoleto = { + text: formatBody(`Segue a segunda-via da sua Fatura!\n\n*Fatura:* ${idboleto}\n*Nome:* ${nome}\n*Valor:* R$ ${valorCorrigido}\n*Data Vencimento:* ${datavencCorrigida}\n\nBasta clicar aqui em baixo em código de barras para copiar, apos isto basta realizar o pagamento em seu banco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyBoleto); + const body = { + text: formatBody(`Este é o *Codigo de Barras*`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + await sleep(2000) + const body_linha_digitavel = { + text: formatBody(`${linha_digitavel}`, contact), + }; + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_linha_digitavel); + ///VE SE ESTA BLOQUEADO PARA LIBERAR! + var optionscontrato = { + method: 'POST', + url: `${urlixc}/webservice/v1/cliente_contrato`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'cliente_contrato.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'cliente_contrato.id', + sortorder: 'asc' + } + }; + axios.request(optionscontrato as any).then(async function (response) { + let status_internet; + let id_contrato; + status_internet = response.data?.registros[0]?.status_internet; + id_contrato = response.data?.registros[0]?.id; + if (status_internet !== 'A') { + const bodyPdf = { + text: formatBody(`*${nome}* vi tambem que a sua conexão esta bloqueada! Vou desbloquear para você.`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyqrcode = { + text: formatBody(`Estou liberando seu acesso. Por favor aguarde!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + //REALIZANDO O DESBLOQUEIO + var optionsdesbloqeuio = { + method: 'POST', + url: `${urlixc}/webservice/v1/desbloqueio_confianca`, + headers: { + Authorization: `Basic ${ixckeybase64}` + }, + data: { id: id_contrato } + }; + + axios.request(optionsdesbloqeuio as any).then(async function (response) { + let tipo; + let mensagem; + tipo = response.data?.tipo; + mensagem = response.data?.mensagem; + if (tipo === 'sucesso') { + //DESCONECTANDO O CLIENTE PARA VOLTAR O ACESSO + var optionsRadius = { + method: 'GET', + url: `${urlixc}/webservice/v1/radusuarios`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'radusuarios.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'radusuarios.id', + sortorder: 'asc' + } + }; + + axios.request(optionsRadius as any).then(async function (response) { + let tipo; + tipo = response.data?.type; + const body_mensagem = { + text: formatBody(`${mensagem}`, contact), + }; + if (tipo === 'success') { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_mensagem); + const bodyPdf = { + text: formatBody(`Fiz os procedimentos de liberação! Agora aguarde até 5 minutos e veja se sua conexão irá retornar! .\n\nCaso não tenha voltado, retorne o contato e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } else { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_mensagem); + const bodyPdf = { + text: formatBody(`Vou precisar que você *retire* seu equipamento da tomada.\n\n*OBS: Somente retire da tomada.* \nAguarde 1 minuto e ligue novamente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyqrcode = { + text: formatBody(`Veja se seu acesso voltou! Caso não tenha voltado retorne o contato e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + }).catch(function (error) { + console.error(error); + }); + //FIM DA DESCONEXÃO + } else { + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro e nao consegui desbloquear! Digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + } + + }).catch(async function (error) { + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + }); + } else { + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + + // + }).catch(async function (error) { + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + }); + ///VE SE ESTA BLOQUEADO PARA LIBERAR! + } + }).catch(function (error) { + console.error(error); + }); + //FIM DO PÌX + + + + }).catch(function (error) { + console.error(error); + }); + + } + + }).catch(async function (error) { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + }); + } else { + const body = { + text: formatBody(`Este CPF/CNPJ não é válido!\n\nPor favor tente novamente!\nOu digite *#* para voltar ao *Menu Anterior*`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } + } + } + + + } + } + + if (filaescolhida === "Religue de Confiança" || filaescolhida === "Liberação em Confiança") { + let cpfcnpj + cpfcnpj = getBodyMessage(msg); + cpfcnpj = cpfcnpj.replace(/\./g, ''); + cpfcnpj = cpfcnpj.replace('-', '') + cpfcnpj = cpfcnpj.replace('/', '') + cpfcnpj = cpfcnpj.replace(' ', '') + cpfcnpj = cpfcnpj.replace(',', '') + + const asaastoken = await Setting.findOne({ + where: { + key: "asaas", + companyId + } + }); + const ixcapikey = await Setting.findOne({ + where: { + key: "tokenixc", + companyId + } + }); + const urlixcdb = await Setting.findOne({ + where: { + key: "ipixc", + companyId + } + }); + const ipmkauth = await Setting.findOne({ + where: { + key: "ipmkauth", + companyId + } + }); + const clientidmkauth = await Setting.findOne({ + where: { + key: "clientidmkauth", + companyId + } + }); + const clientesecretmkauth = await Setting.findOne({ + where: { + key: "clientsecretmkauth", + companyId + } + }); + + let urlmkauth = ipmkauth.value + if (urlmkauth.substr(-1) === '/') { + urlmkauth = urlmkauth.slice(0, -1); + } + + //VARS + let url = `${urlmkauth}/api/`; + const Client_Id = clientidmkauth.value + const Client_Secret = clientesecretmkauth.value + const ixckeybase64 = btoa(ixcapikey.value); + const urlixc = urlixcdb.value + const asaastk = asaastoken.value + + const cnpj_cpf = getBodyMessage(msg); + let numberCPFCNPJ = cpfcnpj; + + if (ixcapikey.value != "" && urlixcdb.value != "") { + if (isNumeric(numberCPFCNPJ) === true) { + if (cpfcnpj.length > 2) { + const isCPFCNPJ = validaCpfCnpj(numberCPFCNPJ) + if (isCPFCNPJ) { + if (numberCPFCNPJ.length <= 11) { + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{3})(\d)/, "$1.$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{3})(\d)/, "$1.$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{3})(\d{1,2})$/, "$1-$2") + } else { + numberCPFCNPJ = numberCPFCNPJ.replace(/^(\d{2})(\d)/, "$1.$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3") + numberCPFCNPJ = numberCPFCNPJ.replace(/\.(\d{3})(\d)/, ".$1/$2") + numberCPFCNPJ = numberCPFCNPJ.replace(/(\d{4})(\d)/, "$1-$2") + } + //const token = await CheckSettingsHelper("OBTEM O TOKEN DO BANCO (dei insert na tabela settings)") + const body = { + text: formatBody(`Aguarde! Estamos consultando na base de dados!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } catch (error) { + + } + var options = { + method: 'GET', + url: `${urlixc}/webservice/v1/cliente`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'cliente.cnpj_cpf', + query: numberCPFCNPJ, + oper: '=', + page: '1', + rp: '1', + sortname: 'cliente.cnpj_cpf', + sortorder: 'asc' + } + }; + + axios.request(options as any).then(async function (response) { + + if (response.data.type === 'error') { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } if (response.data.total === 0) { + const body = { + text: formatBody(`Cadastro não localizado! *CPF/CNPJ* incorreto ou inválido. Tenta novamente!`, contact), + }; + try { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } catch (error) { + + } + } else { + + let nome; + let id; + let type; + + nome = response.data?.registros[0]?.razao + id = response.data?.registros[0]?.id + type = response.data?.type + + + const body = { + text: formatBody(`Localizei seu Cadastro! \n*${nome}* só mais um instante por favor!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + ///VE SE ESTA BLOQUEADO PARA LIBERAR! + var optionscontrato = { + method: 'POST', + url: `${urlixc}/webservice/v1/cliente_contrato`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'cliente_contrato.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'cliente_contrato.id', + sortorder: 'asc' + } + }; + axios.request(optionscontrato as any).then(async function (response) { + let status_internet; + let id_contrato; + status_internet = response.data?.registros[0]?.status_internet; + id_contrato = response.data?.registros[0]?.id; + if (status_internet !== 'A') { + const bodyPdf = { + text: formatBody(`*${nome}* a sua conexão esta bloqueada! Vou desbloquear para você.`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyqrcode = { + text: formatBody(`Estou liberando seu acesso. Por favor aguarde!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + //REALIZANDO O DESBLOQUEIO + var optionsdesbloqeuio = { + method: 'POST', + url: `${urlixc}/webservice/v1/desbloqueio_confianca`, + headers: { + Authorization: `Basic ${ixckeybase64}` + }, + data: { id: id_contrato } + }; + + axios.request(optionsdesbloqeuio as any).then(async function (response) { + let tipo; + let mensagem; + tipo = response.data?.tipo; + mensagem = response.data?.mensagem; + const body_mensagem = { + text: formatBody(`${mensagem}`, contact), + }; + if (tipo === 'sucesso') { + //DESCONECTANDO O CLIENTE PARA VOLTAR O ACESSO + var optionsRadius = { + method: 'GET', + url: `${urlixc}/webservice/v1/radusuarios`, + headers: { + ixcsoft: 'listar', + Authorization: `Basic ${ixckeybase64}` + }, + data: { + qtype: 'radusuarios.id_cliente', + query: id, + oper: '=', + page: '1', + rp: '1', + sortname: 'radusuarios.id', + sortorder: 'asc' + } + }; + + axios.request(optionsRadius as any).then(async function (response) { + let tipo; + tipo = response.data?.type; + + if (tipo === 'success') { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_mensagem); + const bodyPdf = { + text: formatBody(`Fiz os procedimentos de liberação! Agora aguarde até 5 minutos e veja se sua conexão irá retornar! .\n\nCaso não tenha voltado, retorne o contato e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } else { + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_mensagem); + const bodyPdf = { + text: formatBody(`Vou precisar que você *retire* seu equipamento da tomada.\n\n*OBS: Somente retire da tomada.* \nAguarde 1 minuto e ligue novamente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyPdf); + const bodyqrcode = { + text: formatBody(`Veja se seu acesso voltou! Caso não tenha voltado retorne o contato e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyqrcode); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + }).catch(function (error) { + console.error(error); + }); + //FIM DA DESCONEXÃO + + } else { + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro e nao consegui desbloquear!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body_mensagem); + const bodyerroatendente = { + text: formatBody(`Digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerroatendente); + } /* else { + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro e nao consegui desbloquear! Digite *#* e fale com um atendente!` + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`,bodyerro); + } */ + + }).catch(async function (error) { + + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + }); + } else { + const bodysembloqueio = { + text: formatBody(`Sua Conexão não está bloqueada! Caso esteja com dificuldades de navegação, retorne o contato e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodysembloqueio); + const bodyfinaliza = { + text: formatBody(`Estamos finalizando esta conversa! Caso precise entre em contato conosco!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyfinaliza); + await UpdateTicketService({ + ticketData: { status: "closed" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + } + + // + }).catch(async function (error) { + + const bodyerro = { + text: formatBody(`Ops! Ocorreu um erro digite *#* e fale com um atendente!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, bodyerro); + }); + + } + + }).catch(async function (error) { + const body = { + text: formatBody(`*Opss!!!!*\nOcorreu um erro! Digite *#* e fale com um *Atendente*!`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + }); + } else { + const body = { + text: formatBody(`Este CPF/CNPJ não é válido!\n\nPor favor tente novamente!\nOu digite *#* para voltar ao *Menu Anterior*`, contact), + }; + await sleep(2000) + await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, body); + } + } + } + } + } + +} diff --git a/backend/src/services/WbotServices/wbotClosedTickets.ts b/backend/src/services/WbotServices/wbotClosedTickets.ts new file mode 100644 index 0000000..b29d41a --- /dev/null +++ b/backend/src/services/WbotServices/wbotClosedTickets.ts @@ -0,0 +1,118 @@ +import { Op } from "sequelize"; +import Ticket from "../../models/Ticket" +import Whatsapp from "../../models/Whatsapp" +import { getIO } from "../../libs/socket" +import formatBody from "../../helpers/Mustache"; +import SendWhatsAppMessage from "./SendWhatsAppMessage"; +import moment from "moment"; +import ShowTicketService from "../TicketServices/ShowTicketService"; +import { verifyMessage } from "./wbotMessageListener"; +import TicketTraking from "../../models/TicketTraking"; + +export const ClosedAllOpenTickets = async (companyId: number): Promise => { + + // @ts-ignore: Unreachable code error + const closeTicket = async (ticket: any, currentStatus: any, body: any) => { + if (currentStatus === 'nps') { + + await ticket.update({ + status: "closed", + //userId: ticket.userId || null, + lastMessage: body, + unreadMessages: 0, + amountUseBotQueues: 0 + }); + + } else if (currentStatus === 'open') { + + await ticket.update({ + status: "closed", + // userId: ticket.userId || null, + lastMessage: body, + unreadMessages: 0, + amountUseBotQueues: 0 + }); + + } else { + + await ticket.update({ + status: "closed", + //userId: ticket.userId || null, + unreadMessages: 0 + }); + } + }; + + const io = getIO(); + try { + + const { rows: tickets } = await Ticket.findAndCountAll({ + where: { status: { [Op.in]: ["open"] }, companyId }, + order: [["updatedAt", "DESC"]] + }); + + tickets.forEach(async ticket => { + const showTicket = await ShowTicketService(ticket.id, companyId); + const whatsapp = await Whatsapp.findByPk(showTicket?.whatsappId); + const ticketTraking = await TicketTraking.findOne({ + where: { + ticketId: ticket.id, + finishedAt: null, + } + }) + + if (!whatsapp) return; + + let { + expiresInactiveMessage, //mensage de encerramento por inatividade + expiresTicket //tempo em horas para fechar ticket automaticamente + } = whatsapp + + + // @ts-ignore: Unreachable code error + if (expiresTicket && expiresTicket !== "" && + // @ts-ignore: Unreachable code error + expiresTicket !== "0" && Number(expiresTicket) > 0) { + + //mensagem de encerramento por inatividade + const bodyExpiresMessageInactive = formatBody(`\u200e ${expiresInactiveMessage}`, showTicket.contact); + + const dataLimite = new Date() + dataLimite.setMinutes(dataLimite.getMinutes() - Number(expiresTicket)); + + if (showTicket.status === "open" && !showTicket.isGroup) { + + const dataUltimaInteracaoChamado = new Date(showTicket.updatedAt) + + if (dataUltimaInteracaoChamado < dataLimite && showTicket.fromMe) { + + closeTicket(showTicket, showTicket.status, bodyExpiresMessageInactive); + + if (expiresInactiveMessage !== "" && expiresInactiveMessage !== undefined) { + const sentMessage = await SendWhatsAppMessage({ body: bodyExpiresMessageInactive, ticket: showTicket }); + + await verifyMessage(sentMessage, showTicket, showTicket.contact); + } + + await ticketTraking.update({ + finishedAt: moment().toDate(), + closedAt: moment().toDate(), + whatsappId: ticket.whatsappId, + userId: ticket.userId, + }) + + io.to("open").emit(`company-${companyId}-ticket`, { + action: "delete", + ticketId: showTicket.id + }); + + } + } + } + }); + + } catch (e: any) { + console.log('e', e) + } + +} diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts new file mode 100644 index 0000000..813697f --- /dev/null +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -0,0 +1,2312 @@ +import path, { join } from "path"; +import { promisify } from "util"; +import { readFile, writeFile } from "fs"; +import * as Sentry from "@sentry/node"; +import { isNil, isNull, head } from "lodash"; + +import { + downloadMediaMessage, + extractMessageContent, + getContentType, + jidNormalizedUser, + MessageUpsertType, + proto, + WAMessage, + WAMessageStubType, + WAMessageUpdate, + WASocket, +} from "@whiskeysockets/baileys"; +import Contact from "../../models/Contact"; +import Ticket from "../../models/Ticket"; +import Message from "../../models/Message"; + +import { getIO } from "../../libs/socket"; +import CreateMessageService from "../MessageServices/CreateMessageService"; +import { logger } from "../../utils/logger"; +import CreateOrUpdateContactService from "../ContactServices/CreateOrUpdateContactService"; +import FindOrCreateTicketService from "../TicketServices/FindOrCreateTicketService"; +import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; +import UpdateTicketService from "../TicketServices/UpdateTicketService"; +import formatBody from "../../helpers/Mustache"; +import { Store } from "../../libs/store"; +import TicketTraking from "../../models/TicketTraking"; +import UserRating from "../../models/UserRating"; +import SendWhatsAppMessage from "./SendWhatsAppMessage"; +import moment from "moment"; +import Queue from "../../models/Queue"; +import QueueOption from "../../models/QueueOption"; +import FindOrCreateATicketTrakingService from "../TicketServices/FindOrCreateATicketTrakingService"; +import VerifyCurrentSchedule from "../CompanyService/VerifyCurrentSchedule"; +import Campaign from "../../models/Campaign"; +import CampaignShipping from "../../models/CampaignShipping"; +import { Op } from "sequelize"; +import { campaignQueue, parseToMilliseconds, randomValue } from "../../queues"; +import User from "../../models/User"; +import Setting from "../../models/Setting"; +import { cacheLayer } from "../../libs/cache"; +import { provider } from "./providers"; +import { debounce } from "../../helpers/Debounce"; +import { ChatCompletionRequestMessage, Configuration, OpenAIApi } from "openai"; +import ffmpeg from "fluent-ffmpeg"; +import { + SpeechConfig, + SpeechSynthesizer, + AudioConfig +} from "microsoft-cognitiveservices-speech-sdk"; +import typebotListener from "../TypebotServices/typebotListener"; +import QueueIntegrations from "../../models/QueueIntegrations"; +import ShowQueueIntegrationService from "../QueueIntegrationServices/ShowQueueIntegrationService"; + +const request = require("request"); + +const fs = require('fs') + +type Session = WASocket & { + id?: number; + store?: Store; +}; + +interface SessionOpenAi extends OpenAIApi { + id?: number; +} +const sessionsOpenAi: SessionOpenAi[] = []; + +interface ImessageUpsert { + messages: proto.IWebMessageInfo[]; + type: MessageUpsertType; +} + +interface IMe { + name: string; + id: string; +} + +interface IMessage { + messages: WAMessage[]; + isLatest: boolean; +} + +export const isNumeric = (value: string) => /^-?\d+$/.test(value); + +const writeFileAsync = promisify(writeFile); + +const getTypeMessage = (msg: proto.IWebMessageInfo): string => { + return getContentType(msg.message); +}; + +export function validaCpfCnpj(val) { + if (val.length == 11) { + var cpf = val.trim(); + + cpf = cpf.replace(/\./g, ''); + cpf = cpf.replace('-', ''); + cpf = cpf.split(''); + + var v1 = 0; + var v2 = 0; + var aux = false; + + for (var i = 1; cpf.length > i; i++) { + if (cpf[i - 1] != cpf[i]) { + aux = true; + } + } + + if (aux == false) { + return false; + } + + for (var i = 0, p = 10; (cpf.length - 2) > i; i++, p--) { + v1 += cpf[i] * p; + } + + v1 = ((v1 * 10) % 11); + + if (v1 == 10) { + v1 = 0; + } + + if (v1 != cpf[9]) { + return false; + } + + for (var i = 0, p = 11; (cpf.length - 1) > i; i++, p--) { + v2 += cpf[i] * p; + } + + v2 = ((v2 * 10) % 11); + + if (v2 == 10) { + v2 = 0; + } + + if (v2 != cpf[10]) { + return false; + } else { + return true; + } + } else if (val.length == 14) { + var cnpj = val.trim(); + + cnpj = cnpj.replace(/\./g, ''); + cnpj = cnpj.replace('-', ''); + cnpj = cnpj.replace('/', ''); + cnpj = cnpj.split(''); + + var v1 = 0; + var v2 = 0; + var aux = false; + + for (var i = 1; cnpj.length > i; i++) { + if (cnpj[i - 1] != cnpj[i]) { + aux = true; + } + } + + if (aux == false) { + return false; + } + + for (var i = 0, p1 = 5, p2 = 13; (cnpj.length - 2) > i; i++, p1--, p2--) { + if (p1 >= 2) { + v1 += cnpj[i] * p1; + } else { + v1 += cnpj[i] * p2; + } + } + + v1 = (v1 % 11); + + if (v1 < 2) { + v1 = 0; + } else { + v1 = (11 - v1); + } + + if (v1 != cnpj[12]) { + return false; + } + + for (var i = 0, p1 = 6, p2 = 14; (cnpj.length - 1) > i; i++, p1--, p2--) { + if (p1 >= 2) { + v2 += cnpj[i] * p1; + } else { + v2 += cnpj[i] * p2; + } + } + + v2 = (v2 % 11); + + if (v2 < 2) { + v2 = 0; + } else { + v2 = (11 - v2); + } + + if (v2 != cnpj[13]) { + return false; + } else { + return true; + } + } else { + return false; + } +} + +function timeout(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export async function sleep(time) { + await timeout(time); +} +export const sendMessageImage = async ( + wbot: Session, + contact, + ticket: Ticket, + url: string, + caption: string +) => { + + let sentMessage + try { + sentMessage = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + image: url ? { url } : fs.readFileSync(`public/temp/${caption}-${makeid(10)}`), + fileName: caption, + caption: caption, + mimetype: 'image/jpeg' + } + ); + } catch (error) { + sentMessage = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + text: formatBody('Não consegui enviar o PDF, tente novamente!', contact) + } + ); + } + verifyMessage(sentMessage, ticket, contact); +}; + +export const sendMessageLink = async ( + wbot: Session, + contact: Contact, + ticket: Ticket, + url: string, + caption: string +) => { + + let sentMessage + try { + sentMessage = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, { + document: url ? { url } : fs.readFileSync(`public/temp/${caption}-${makeid(10)}`), + fileName: caption, + caption: caption, + mimetype: 'application/pdf' + } + ); + } catch (error) { + sentMessage = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, { + text: formatBody('Não consegui enviar o PDF, tente novamente!', contact) + } + ); + } + verifyMessage(sentMessage, ticket, contact); +}; + +export function makeid(length) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + + +const getBodyButton = (msg: proto.IWebMessageInfo): string => { + if (msg.key.fromMe && msg?.message?.viewOnceMessage?.message?.buttonsMessage?.contentText) { + let bodyMessage = `*${msg?.message?.viewOnceMessage?.message?.buttonsMessage?.contentText}*`; + + for (const buton of msg.message?.viewOnceMessage?.message?.buttonsMessage?.buttons) { + bodyMessage += `\n\n${buton.buttonText?.displayText}`; + } + return bodyMessage; + } + + if (msg.key.fromMe && msg?.message?.viewOnceMessage?.message?.listMessage) { + let bodyMessage = `*${msg?.message?.viewOnceMessage?.message?.listMessage?.description}*`; + for (const buton of msg.message?.viewOnceMessage?.message?.listMessage?.sections) { + for (const rows of buton.rows) { + bodyMessage += `\n\n${rows.title}`; + } + } + + return bodyMessage; + } +}; + +const msgLocation = (image, latitude, longitude) => { + if (image) { + var b64 = Buffer.from(image).toString("base64"); + + let data = `data:image/png;base64, ${b64} | https://maps.google.com/maps?q=${latitude}%2C${longitude}&z=17&hl=pt-BR|${latitude}, ${longitude} `; + return data; + } +}; + +export const getBodyMessage = (msg: proto.IWebMessageInfo): string | null => { + + try { + let type = getTypeMessage(msg); + + const types = { + conversation: msg?.message?.conversation, + editedMessage: msg?.message?.editedMessage?.message?.protocolMessage?.editedMessage?.conversation, + imageMessage: msg.message?.imageMessage?.caption, + videoMessage: msg.message?.videoMessage?.caption, + extendedTextMessage: msg.message?.extendedTextMessage?.text, + buttonsResponseMessage: msg.message?.buttonsResponseMessage?.selectedButtonId, + templateButtonReplyMessage: msg.message?.templateButtonReplyMessage?.selectedId, + messageContextInfo: msg.message?.buttonsResponseMessage?.selectedButtonId || msg.message?.listResponseMessage?.title, + buttonsMessage: getBodyButton(msg) || msg.message?.listResponseMessage?.singleSelectReply?.selectedRowId, + viewOnceMessage: getBodyButton(msg) || msg.message?.listResponseMessage?.singleSelectReply?.selectedRowId, + stickerMessage: "sticker", + contactMessage: msg.message?.contactMessage?.vcard, + contactsArrayMessage: "varios contatos", + //locationMessage: `Latitude: ${msg.message.locationMessage?.degreesLatitude} - Longitude: ${msg.message.locationMessage?.degreesLongitude}`, + locationMessage: msgLocation( + msg.message?.locationMessage?.jpegThumbnail, + msg.message?.locationMessage?.degreesLatitude, + msg.message?.locationMessage?.degreesLongitude + ), + liveLocationMessage: `Latitude: ${msg.message?.liveLocationMessage?.degreesLatitude} - Longitude: ${msg.message?.liveLocationMessage?.degreesLongitude}`, + documentMessage: msg.message?.documentMessage?.title, + documentWithCaptionMessage: msg.message?.documentWithCaptionMessage?.message?.documentMessage?.caption, + audioMessage: "Áudio", + listMessage: getBodyButton(msg) || msg.message?.listResponseMessage?.title, + listResponseMessage: msg.message?.listResponseMessage?.singleSelectReply?.selectedRowId, + reactionMessage: msg.message?.reactionMessage?.text || "reaction", + }; + + const objKey = Object.keys(types).find(key => key === type); + + if (!objKey) { + logger.warn(`#### Nao achou o type 152: ${type} +${JSON.stringify(msg)}`); + Sentry.setExtra("Mensagem", { BodyMsg: msg.message, msg, type }); + Sentry.captureException( + new Error("Novo Tipo de Mensagem em getTypeMessage") + ); + } + return types[type]; + } catch (error) { + Sentry.setExtra("Error getTypeMessage", { msg, BodyMsg: msg.message }); + Sentry.captureException(error); + console.log(error); + } +}; + + +export const getQuotedMessage = (msg: proto.IWebMessageInfo): any => { + const body = + msg.message.imageMessage.contextInfo || + msg.message.videoMessage.contextInfo || + msg.message?.documentMessage || + msg.message.extendedTextMessage.contextInfo || + msg.message.buttonsResponseMessage.contextInfo || + msg.message.listResponseMessage.contextInfo || + msg.message.templateButtonReplyMessage.contextInfo || + msg.message.buttonsResponseMessage?.contextInfo || + msg?.message?.buttonsResponseMessage?.selectedButtonId || + msg.message.listResponseMessage?.singleSelectReply?.selectedRowId || + msg?.message?.listResponseMessage?.singleSelectReply.selectedRowId || + msg.message.listResponseMessage?.contextInfo; + msg.message.senderKeyDistributionMessage; + + // testar isso + + return extractMessageContent(body[Object.keys(body).values().next().value]); +}; +export const getQuotedMessageId = (msg: proto.IWebMessageInfo) => { + const body = extractMessageContent(msg.message)[ + Object.keys(msg?.message).values().next().value + ]; + + return body?.contextInfo?.stanzaId; +}; + +const getMeSocket = (wbot: Session): IMe => { + return { + id: jidNormalizedUser((wbot as WASocket).user.id), + name: (wbot as WASocket).user.name + } +}; + +const getSenderMessage = ( + msg: proto.IWebMessageInfo, + wbot: Session +): string => { + const me = getMeSocket(wbot); + if (msg.key.fromMe) return me.id; + + const senderId = msg.participant || msg.key.participant || msg.key.remoteJid || undefined; + + return senderId && jidNormalizedUser(senderId); +}; + +const getContactMessage = async (msg: proto.IWebMessageInfo, wbot: Session) => { + const isGroup = msg.key.remoteJid.includes("g.us"); + const rawNumber = msg.key.remoteJid.replace(/\D/g, ""); + return isGroup + ? { + id: getSenderMessage(msg, wbot), + name: msg.pushName + } + : { + id: msg.key.remoteJid, + name: msg.key.fromMe ? rawNumber : msg.pushName + }; +}; + +const downloadMedia = async (msg: proto.IWebMessageInfo) => { + + let buffer + try { + buffer = await downloadMediaMessage( + msg, + 'buffer', + {} + ) + } catch (err) { + + + console.error('Erro ao baixar mídia:', err); + + // Trate o erro de acordo com as suas necessidades + } + + let filename = msg.message?.documentMessage?.fileName || ""; + + const mineType = + msg.message?.imageMessage || + msg.message?.audioMessage || + msg.message?.videoMessage || + msg.message?.stickerMessage || + msg.message?.documentMessage || + msg.message?.documentWithCaptionMessage?.message?.documentMessage || + msg.message?.extendedTextMessage?.contextInfo?.quotedMessage?.imageMessage || + msg.message?.extendedTextMessage?.contextInfo?.quotedMessage?.videoMessage; + + if (!mineType) + console.log(msg) + + if (!filename) { + const ext = mineType.mimetype.split("/")[1].split(";")[0]; + filename = `${new Date().getTime()}.${ext}`; + } else { + filename = `${new Date().getTime()}_${filename}`; + } + + const media = { + data: buffer, + mimetype: mineType.mimetype, + filename + }; + + return media; +} + + +const verifyContact = async ( + msgContact: IMe, + wbot: Session, + companyId: number +): Promise => { + let profilePicUrl: string; + try { + profilePicUrl = await wbot.profilePictureUrl(msgContact.id); + } catch (e) { + Sentry.captureException(e); + profilePicUrl = `${process.env.FRONTEND_URL}/nopicture.png`; + } + + const contactData = { + name: msgContact?.name || msgContact.id.replace(/\D/g, ""), + number: msgContact.id.replace(/\D/g, ""), + profilePicUrl, + isGroup: msgContact.id.includes("g.us"), + companyId, + whatsappId: wbot.id + }; + + + + const contact = CreateOrUpdateContactService(contactData); + + return contact; +}; + +const verifyQuotedMessage = async ( + msg: proto.IWebMessageInfo +): Promise => { + if (!msg) return null; + const quoted = getQuotedMessageId(msg); + + if (!quoted) return null; + + const quotedMsg = await Message.findOne({ + where: { id: quoted }, + }); + + if (!quotedMsg) return null; + + return quotedMsg; +}; + +const sanitizeName = (name: string): string => { + let sanitized = name.split(" ")[0]; + sanitized = sanitized.replace(/[^a-zA-Z0-9]/g, ""); + return sanitized.substring(0, 60); +}; +const convertTextToSpeechAndSaveToFile = ( + text: string, + filename: string, + subscriptionKey: string, + serviceRegion: string, + voice: string = "pt-BR-FabioNeural", + audioToFormat: string = "mp3" +): Promise => { + return new Promise((resolve, reject) => { + const speechConfig = SpeechConfig.fromSubscription( + subscriptionKey, + serviceRegion + ); + speechConfig.speechSynthesisVoiceName = voice; + const audioConfig = AudioConfig.fromAudioFileOutput(`${filename}.wav`); + const synthesizer = new SpeechSynthesizer(speechConfig, audioConfig); + synthesizer.speakTextAsync( + text, + result => { + if (result) { + convertWavToAnotherFormat( + `${filename}.wav`, + `${filename}.${audioToFormat}`, + audioToFormat + ) + .then(output => { + resolve(); + }) + .catch(error => { + console.error(error); + reject(error); + }); + } else { + reject(new Error("No result from synthesizer")); + } + synthesizer.close(); + }, + error => { + console.error(`Error: ${error}`); + synthesizer.close(); + reject(error); + } + ); + }); +}; + +const convertWavToAnotherFormat = ( + inputPath: string, + outputPath: string, + toFormat: string +) => { + return new Promise((resolve, reject) => { + ffmpeg() + .input(inputPath) + .toFormat(toFormat) + .on("end", () => resolve(outputPath)) + .on("error", (err: { message: any }) => + reject(new Error(`Error converting file: ${err.message}`)) + ) + .save(outputPath); + }); +}; + +const deleteFileSync = (path: string): void => { + try { + fs.unlinkSync(path); + } catch (error) { + console.error("Erro ao deletar o arquivo:", error); + } +}; + +const keepOnlySpecifiedChars = (str: string) => { + return str.replace(/[^a-zA-Z0-9áéíóúÁÉÍÓÚâêîôûÂÊÎÔÛãõÃÕçÇ!?.,;:\s]/g, ""); +}; +const handleOpenAi = async ( + msg: proto.IWebMessageInfo, + wbot: Session, + ticket: Ticket, + contact: Contact, + mediaSent: Message | undefined +): Promise => { + const bodyMessage = getBodyMessage(msg); + + if (!bodyMessage) return; + + + let { prompt } = await ShowWhatsAppService(wbot.id, ticket.companyId); + + + if (!prompt && !isNil(ticket?.queue?.prompt)) { + prompt = ticket.queue.prompt; + } + + if (!prompt) return; + + if (msg.messageStubType) return; + + const publicFolder: string = path.resolve( + __dirname, + "..", + "..", + "..", + "public" + ); + + let openai: SessionOpenAi; + const openAiIndex = sessionsOpenAi.findIndex(s => s.id === wbot.id); + + + if (openAiIndex === -1) { + const configuration = new Configuration({ + apiKey: prompt.apiKey + }); + openai = new OpenAIApi(configuration); + openai.id = wbot.id; + sessionsOpenAi.push(openai); + } else { + openai = sessionsOpenAi[openAiIndex]; + } + + const messages = await Message.findAll({ + where: { ticketId: ticket.id }, + order: [["createdAt", "ASC"]], + limit: prompt.maxMessages + }); + + const promptSystem = `Nas respostas utilize o nome ${sanitizeName( + contact.name || "Amigo(a)" + )} para identificar o cliente.\nSua resposta deve usar no máximo ${prompt.maxTokens + } tokens e cuide para não truncar o final.\nSempre que possível, mencione o nome dele para ser mais personalizado o atendimento e mais educado. Quando a resposta requer uma transferência para o setor de atendimento, comece sua resposta com 'Ação: Transferir para o setor de atendimento'.\n + ${prompt.prompt}\n`; + + let messagesOpenAi: ChatCompletionRequestMessage[] = []; + + if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { + messagesOpenAi = []; + messagesOpenAi.push({ role: "system", content: promptSystem }); + for ( + let i = 0; + i < Math.min(prompt.maxMessages, messages.length); + i++ + ) { + const message = messages[i]; + if (message.mediaType === "chat") { + if (message.fromMe) { + messagesOpenAi.push({ role: "assistant", content: message.body }); + } else { + messagesOpenAi.push({ role: "user", content: message.body }); + } + } + } + messagesOpenAi.push({ role: "user", content: bodyMessage! }); + + const chat = await openai.createChatCompletion({ + model: "gpt-3.5-turbo-1106", + messages: messagesOpenAi, + max_tokens: prompt.maxTokens, + temperature: prompt.temperature + }); + + let response = chat.data.choices[0].message?.content; + + if (response?.includes("Ação: Transferir para o setor de atendimento")) { + await transferQueue(prompt.queueId, ticket, contact); + response = response + .replace("Ação: Transferir para o setor de atendimento", "") + .trim(); + } + + if (prompt.voice === "texto") { + const sentMessage = await wbot.sendMessage(msg.key.remoteJid!, { + text: response! + }); + await verifyMessage(sentMessage!, ticket, contact); + } else { + const fileNameWithOutExtension = `${ticket.id}_${Date.now()}`; + convertTextToSpeechAndSaveToFile( + keepOnlySpecifiedChars(response!), + `${publicFolder}/${fileNameWithOutExtension}`, + prompt.voiceKey, + prompt.voiceRegion, + prompt.voice, + "mp3" + ).then(async () => { + try { + const sendMessage = await wbot.sendMessage(msg.key.remoteJid!, { + audio: { url: `${publicFolder}/${fileNameWithOutExtension}.mp3` }, + mimetype: "audio/mpeg", + ptt: true + }); + await verifyMediaMessage(sendMessage!, ticket, contact); + deleteFileSync(`${publicFolder}/${fileNameWithOutExtension}.mp3`); + deleteFileSync(`${publicFolder}/${fileNameWithOutExtension}.wav`); + } catch (error) { + console.log(`Erro para responder com audio: ${error}`); + } + }); + } + } else if (msg.message?.audioMessage) { + const mediaUrl = mediaSent!.mediaUrl!.split("/").pop(); + const file = fs.createReadStream(`${publicFolder}/${mediaUrl}`) as any; + const transcription = await openai.createTranscription(file, "whisper-1"); + + messagesOpenAi = []; + messagesOpenAi.push({ role: "system", content: promptSystem }); + for ( + let i = 0; + i < Math.min(prompt.maxMessages, messages.length); + i++ + ) { + const message = messages[i]; + if (message.mediaType === "chat") { + if (message.fromMe) { + messagesOpenAi.push({ role: "assistant", content: message.body }); + } else { + messagesOpenAi.push({ role: "user", content: message.body }); + } + } + } + messagesOpenAi.push({ role: "user", content: transcription.data.text }); + const chat = await openai.createChatCompletion({ + model: "gpt-3.5-turbo-1106", + messages: messagesOpenAi, + max_tokens: prompt.maxTokens, + temperature: prompt.temperature + }); + let response = chat.data.choices[0].message?.content; + + if (response?.includes("Ação: Transferir para o setor de atendimento")) { + await transferQueue(prompt.queueId, ticket, contact); + response = response + .replace("Ação: Transferir para o setor de atendimento", "") + .trim(); + } + if (prompt.voice === "texto") { + const sentMessage = await wbot.sendMessage(msg.key.remoteJid!, { + text: response! + }); + await verifyMessage(sentMessage!, ticket, contact); + } else { + const fileNameWithOutExtension = `${ticket.id}_${Date.now()}`; + convertTextToSpeechAndSaveToFile( + keepOnlySpecifiedChars(response!), + `${publicFolder}/${fileNameWithOutExtension}`, + prompt.voiceKey, + prompt.voiceRegion, + prompt.voice, + "mp3" + ).then(async () => { + try { + const sendMessage = await wbot.sendMessage(msg.key.remoteJid!, { + audio: { url: `${publicFolder}/${fileNameWithOutExtension}.mp3` }, + mimetype: "audio/mpeg", + ptt: true + }); + await verifyMediaMessage(sendMessage!, ticket, contact); + deleteFileSync(`${publicFolder}/${fileNameWithOutExtension}.mp3`); + deleteFileSync(`${publicFolder}/${fileNameWithOutExtension}.wav`); + } catch (error) { + console.log(`Erro para responder com audio: ${error}`); + } + }); + } + } + messagesOpenAi = []; +}; + +const transferQueue = async ( + queueId: number, + ticket: Ticket, + contact: Contact +): Promise => { + await UpdateTicketService({ + ticketData: { queueId: queueId, useIntegration: false, promptId: null }, + ticketId: ticket.id, + companyId: ticket.companyId + }); +}; + +const verifyMediaMessage = async ( + msg: proto.IWebMessageInfo, + ticket: Ticket, + contact: Contact +): Promise => { + const io = getIO(); + const quotedMsg = await verifyQuotedMessage(msg); + const media = await downloadMedia(msg); + + if (!media) { + throw new Error("ERR_WAPP_DOWNLOAD_MEDIA"); + } + + if (!media.filename) { + const ext = media.mimetype.split("/")[1].split(";")[0]; + media.filename = `${new Date().getTime()}.${ext}`; + } + + try { + await writeFileAsync( + join(__dirname, "..", "..", "..", "public", media.filename), + media.data, + "base64" + ); + } catch (err) { + Sentry.captureException(err); + logger.error(err); + } + + const body = getBodyMessage(msg); + + + const messageData = { + id: msg.key.id, + ticketId: ticket.id, + contactId: msg.key.fromMe ? undefined : contact.id, + body: body ? formatBody(body, ticket.contact) : media.filename, + fromMe: msg.key.fromMe, + read: msg.key.fromMe, + mediaUrl: media.filename, + mediaType: media.mimetype.split("/")[0], + quotedMsgId: quotedMsg?.id, + ack: msg.status, + remoteJid: msg.key.remoteJid, + participant: msg.key.participant, + dataJson: JSON.stringify(msg), + }; + + await ticket.update({ + lastMessage: body || media.filename, + }); + + const newMessage = await CreateMessageService({ + messageData, + companyId: ticket.companyId, + }); + + if (!msg.key.fromMe && ticket.status === "closed") { + await ticket.update({ status: "pending" }); + await ticket.reload({ + include: [ + { model: Queue, as: "queue" }, + { model: User, as: "user" }, + { model: Contact, as: "contact" }, + ], + }); + + io.to(`company-${ticket.companyId}-closed`) + .to(`queue-${ticket.queueId}-closed`) + .emit(`company-${ticket.companyId}-ticket`, { + action: "delete", + ticket, + ticketId: ticket.id, + }); + + io.to(`company-${ticket.companyId}-${ticket.status}`) + .to(`queue-${ticket.queueId}-${ticket.status}`) + .to(ticket.id.toString()) + .emit(`company-${ticket.companyId}-ticket`, { + action: "update", + ticket, + ticketId: ticket.id, + }); + } + + return newMessage; +}; + +export const verifyMessage = async ( + msg: proto.IWebMessageInfo, + ticket: Ticket, + contact: Contact +) => { + const io = getIO(); + const quotedMsg = await verifyQuotedMessage(msg); + const body = getBodyMessage(msg); + const isEdited = getTypeMessage(msg) == 'editedMessage'; + + const messageData = { + id: isEdited ? msg?.message?.editedMessage?.message?.protocolMessage?.key?.id : msg.key.id, + ticketId: ticket.id, + contactId: msg.key.fromMe ? undefined : contact.id, + body, + fromMe: msg.key.fromMe, + mediaType: getTypeMessage(msg), + read: msg.key.fromMe, + quotedMsgId: quotedMsg?.id, + ack: msg.status, + remoteJid: msg.key.remoteJid, + participant: msg.key.participant, + dataJson: JSON.stringify(msg), + isEdited: isEdited, + }; + + await ticket.update({ + lastMessage: body + }); + + await CreateMessageService({ messageData, companyId: ticket.companyId }); + + if (!msg.key.fromMe && ticket.status === "closed") { + await ticket.update({ status: "pending" }); + await ticket.reload({ + include: [ + { model: Queue, as: "queue" }, + { model: User, as: "user" }, + { model: Contact, as: "contact" } + ] + }); + + io.to(`company-${ticket.companyId}-closed`) + .to(`queue-${ticket.queueId}-closed`) + .emit(`company-${ticket.companyId}-ticket`, { + action: "delete", + ticket, + ticketId: ticket.id + }); + + io.to(`company-${ticket.companyId}-${ticket.status}`) + .to(`queue-${ticket.queueId}-${ticket.status}`) + .emit(`company-${ticket.companyId}-ticket`, { + action: "update", + ticket, + ticketId: ticket.id + }); + } +}; + +const isValidMsg = (msg: proto.IWebMessageInfo): boolean => { + if (msg.key.remoteJid === "status@broadcast") return false; + try { + const msgType = getTypeMessage(msg); + if (!msgType) { + return; + } + + const ifType = + msgType === "conversation" || + msgType === "extendedTextMessage" || + msgType === "editedMessage" || + msgType === "audioMessage" || + msgType === "videoMessage" || + msgType === "imageMessage" || + msgType === "documentMessage" || + msgType === "documentWithCaptionMessage" || + msgType === "stickerMessage" || + msgType === "buttonsResponseMessage" || + msgType === "buttonsMessage" || + msgType === "messageContextInfo" || + msgType === "locationMessage" || + msgType === "liveLocationMessage" || + msgType === "contactMessage" || + msgType === "voiceMessage" || + msgType === "mediaMessage" || + msgType === "contactsArrayMessage" || + msgType === "reactionMessage" || + msgType === "ephemeralMessage" || + msgType === "protocolMessage" || + msgType === "listResponseMessage" || + msgType === "listMessage" || + msgType === "viewOnceMessage"; + + if (!ifType) { + logger.warn(`#### Nao achou o type em isValidMsg: ${msgType} +${JSON.stringify(msg?.message)}`); + Sentry.setExtra("Mensagem", { BodyMsg: msg.message, msg, msgType }); + Sentry.captureException(new Error("Novo Tipo de Mensagem em isValidMsg")); + } + + return !!ifType; + } catch (error) { + Sentry.setExtra("Error isValidMsg", { msg }); + Sentry.captureException(error); + } +}; + + +const Push = (msg: proto.IWebMessageInfo) => { + return msg.pushName; +} + +const verifyQueue = async ( + wbot: Session, + msg: proto.IWebMessageInfo, + ticket: Ticket, + contact: Contact, + mediaSent?: Message | undefined +) => { + const companyId = ticket.companyId; + + const { queues, greetingMessage, maxUseBotQueues, timeUseBotQueues } = await ShowWhatsAppService( + wbot.id!, + ticket.companyId + ) + + + + if (queues.length === 1) { + + const sendGreetingMessageOneQueues = await Setting.findOne({ + where: { + key: "sendGreetingMessageOneQueues", + companyId: ticket.companyId + } + }); + + if (greetingMessage.length > 1 && sendGreetingMessageOneQueues?.value === "enabled") { + const body = formatBody(`${greetingMessage}`, contact); + + await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + { + text: body + } + ); + } + + const firstQueue = head(queues); + let chatbot = false; + if (firstQueue?.options) { + chatbot = firstQueue.options.length > 0; + } + + //inicia integração dialogflow/n8n + if ( + !msg.key.fromMe && + !ticket.isGroup && + !isNil(queues[0]?.integrationId) + ) { + const integrations = await ShowQueueIntegrationService(queues[0].integrationId, companyId); + + await handleMessageIntegration(msg, wbot, integrations, ticket) + + await ticket.update({ + useIntegration: true, + integrationId: integrations.id + }) + // return; + } + //inicia integração openai + if ( + !msg.key.fromMe && + !ticket.isGroup && + !isNil(queues[0]?.promptId) + ) { + + + + await handleOpenAi(msg, wbot, ticket, contact, mediaSent); + + + await ticket.update({ + useIntegration: true, + promptId: queues[0]?.promptId + }) + // return; + } + + await UpdateTicketService({ + ticketData: { queueId: firstQueue.id, chatbot, status: "pending" }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + + return; + } + + const selectedOption = getBodyMessage(msg); + const choosenQueue = queues[+selectedOption - 1]; + + const buttonActive = await Setting.findOne({ + where: { + key: "chatBotType", + companyId + } + }); + + + + const botText = async () => { + let options = ""; + + queues.forEach((queue, index) => { + options += `*[ ${index + 1} ]* - ${queue.name}\n`; + }); + + + const textMessage = { + text: formatBody(`\u200e${greetingMessage}\n\n${options}`, contact), + }; + + const sendMsg = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + textMessage + ); + + await verifyMessage(sendMsg, ticket, ticket.contact); + }; + + if (choosenQueue) { + let chatbot = false; + if (choosenQueue?.options) { + chatbot = choosenQueue.options.length > 0; + } + + await UpdateTicketService({ + ticketData: { queueId: choosenQueue.id, chatbot }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + + + /* Tratamento para envio de mensagem quando a fila está fora do expediente */ + if (choosenQueue.options.length === 0) { + const queue = await Queue.findByPk(choosenQueue.id); + const { schedules }: any = queue; + const now = moment(); + const weekday = now.format("dddd").toLowerCase(); + let schedule; + if (Array.isArray(schedules) && schedules.length > 0) { + schedule = schedules.find((s) => s.weekdayEn === weekday && s.startTime !== "" && s.startTime !== null && s.endTime !== "" && s.endTime !== null); + } + + if (queue.outOfHoursMessage !== null && queue.outOfHoursMessage !== "" && !isNil(schedule)) { + const startTime = moment(schedule.startTime, "HH:mm"); + const endTime = moment(schedule.endTime, "HH:mm"); + + if (now.isBefore(startTime) || now.isAfter(endTime)) { + const body = formatBody(`\u200e ${queue.outOfHoursMessage}\n\n*[ # ]* - Voltar ao Menu Principal`, ticket.contact); + const sentMessage = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, { + text: body, + } + ); + await verifyMessage(sentMessage, ticket, contact); + await UpdateTicketService({ + ticketData: { queueId: null, chatbot }, + ticketId: ticket.id, + companyId: ticket.companyId, + }); + return; + } + } + + //inicia integração dialogflow/n8n + if ( + !msg.key.fromMe && + !ticket.isGroup && + choosenQueue.integrationId + ) { + const integrations = await ShowQueueIntegrationService(choosenQueue.integrationId, companyId); + + await handleMessageIntegration(msg, wbot, integrations, ticket) + + await ticket.update({ + useIntegration: true, + integrationId: integrations.id + }) + // return; + } + + //inicia integração openai + if ( + !msg.key.fromMe && + !ticket.isGroup && + !isNil(choosenQueue?.promptId) + ) { + await handleOpenAi(msg, wbot, ticket, contact, mediaSent); + + + await ticket.update({ + useIntegration: true, + promptId: choosenQueue?.promptId + }) + // return; + } + + const body = formatBody(`\u200e${choosenQueue.greetingMessage}`, ticket.contact + ); + if (choosenQueue.greetingMessage) { + const sentMessage = await wbot.sendMessage( + `${contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, { + text: body, + } + ); + await verifyMessage(sentMessage, ticket, contact); + } + } + + } else { + + if (maxUseBotQueues && maxUseBotQueues !== 0 && ticket.amountUsedBotQueues >= maxUseBotQueues) { + // await UpdateTicketService({ + // ticketData: { queueId: queues[0].id }, + // ticketId: ticket.id + // }); + + return; + } + + //Regra para desabilitar o chatbot por x minutos/horas após o primeiro envio + const ticketTraking = await FindOrCreateATicketTrakingService({ ticketId: ticket.id, companyId }); + let dataLimite = new Date(); + let Agora = new Date(); + + + if (ticketTraking.chatbotAt !== null) { + dataLimite.setMinutes(ticketTraking.chatbotAt.getMinutes() + (Number(timeUseBotQueues))); + + if (ticketTraking.chatbotAt !== null && Agora < dataLimite && timeUseBotQueues !== "0" && ticket.amountUsedBotQueues !== 0) { + return + } + } + await ticketTraking.update({ + chatbotAt: null + }) + + if (buttonActive.value === "text") { + return botText(); + } + + } + +}; + + +export const verifyRating = (ticketTraking: TicketTraking) => { + if ( + ticketTraking && + ticketTraking.finishedAt === null && + ticketTraking.userId !== null && + ticketTraking.ratingAt !== null + ) { + return true; + } + return false; +}; + +export const handleRating = async ( + rate: number, + ticket: Ticket, + ticketTraking: TicketTraking +) => { + const io = getIO(); + + const { complationMessage } = await ShowWhatsAppService( + ticket.whatsappId, + ticket.companyId + ); + + let finalRate = rate; + + if (rate < 1) { + finalRate = 1; + } + if (rate > 5) { + finalRate = 5; + } + + await UserRating.create({ + ticketId: ticketTraking.ticketId, + companyId: ticketTraking.companyId, + userId: ticketTraking.userId, + rate: finalRate, + }); + + if (complationMessage) { + const body = formatBody(`\u200e${complationMessage}`, ticket.contact); + await SendWhatsAppMessage({ body, ticket }); + } + + await ticketTraking.update({ + finishedAt: moment().toDate(), + rated: true, + }); + + await ticket.update({ + queueId: null, + chatbot: null, + queueOptionId: null, + userId: null, + status: "closed", + }); + + io.to(`company-${ticket.companyId}-open`) + .to(`queue-${ticket.queueId}-open`) + .emit(`company-${ticket.companyId}-ticket`, { + action: "delete", + ticket, + ticketId: ticket.id, + }); + + io.to(`company-${ticket.companyId}-${ticket.status}`) + .to(`queue-${ticket.queueId}-${ticket.status}`) + .to(ticket.id.toString()) + .emit(`company-${ticket.companyId}-ticket`, { + action: "update", + ticket, + ticketId: ticket.id, + }); +}; + +const handleChartbot = async (ticket: Ticket, msg: WAMessage, wbot: Session, dontReadTheFirstQuestion: boolean = false) => { + + + + const queue = await Queue.findByPk(ticket.queueId, { + include: [ + { + model: QueueOption, + as: "options", + where: { parentId: null }, + order: [ + ["option", "ASC"], + ["createdAt", "ASC"], + ], + }, + ], + }); + + + + + const messageBody = getBodyMessage(msg); + + if (messageBody == "#") { + // voltar para o menu inicial + await ticket.update({ queueOptionId: null, chatbot: false, queueId: null }); + await verifyQueue(wbot, msg, ticket, ticket.contact); + return; + } + + // voltar para o menu anterior + if (!isNil(queue) && !isNil(ticket.queueOptionId) && messageBody == "0") { + const option = await QueueOption.findByPk(ticket.queueOptionId); + await ticket.update({ queueOptionId: option?.parentId }); + + // escolheu uma opção + } else if (!isNil(queue) && !isNil(ticket.queueOptionId)) { + const count = await QueueOption.count({ + where: { parentId: ticket.queueOptionId }, + }); + let option: any = {}; + if (count == 1) { + option = await QueueOption.findOne({ + where: { parentId: ticket.queueOptionId }, + }); + } else { + option = await QueueOption.findOne({ + where: { + option: messageBody || "", + parentId: ticket.queueOptionId, + }, + }); + } + if (option) { + await ticket.update({ queueOptionId: option?.id }); + } + + // não linha a primeira pergunta + } else if (!isNil(queue) && isNil(ticket.queueOptionId) && !dontReadTheFirstQuestion) { + const option = queue?.options.find((o) => o.option == messageBody); + if (option) { + await ticket.update({ queueOptionId: option?.id }); + } + } + + await ticket.reload(); + + if (!isNil(queue) && isNil(ticket.queueOptionId)) { + + const queueOptions = await QueueOption.findAll({ + where: { queueId: ticket.queueId, parentId: null }, + order: [ + ["option", "ASC"], + ["createdAt", "ASC"], + ], + }); + + const companyId = ticket.companyId; + + const buttonActive = await Setting.findOne({ + where: { + key: "chatBotType", + companyId + } + }); + + // const botList = async () => { + // const sectionsRows = []; + + // queues.forEach((queue, index) => { + // sectionsRows.push({ + // title: queue.name, + // rowId: `${index + 1}` + // }); + // }); + + // const sections = [ + // { + // rows: sectionsRows + // } + // ]; + + + // const listMessage = { + // text: formatBody(`\u200e${queue.greetingMessage}`, ticket.contact), + // buttonText: "Escolha uma opção", + // sections + // }; + + // const sendMsg = await wbot.sendMessage( + // `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + // listMessage + // ); + + // await verifyMessage(sendMsg, ticket, ticket.contact); + // } + + const botButton = async () => { + const buttons = []; + queueOptions.forEach((option, i) => { + buttons.push({ + buttonId: `${option.option}`, + buttonText: { displayText: option.title }, + type: 4 + }); + }); + buttons.push({ + buttonId: `#`, + buttonText: { displayText: "Menu inicial *[ 0 ]* Menu anterior" }, + type: 4 + }); + + const buttonMessage = { + text: formatBody(`\u200e${queue.greetingMessage}`, ticket.contact), + buttons, + headerType: 4 + }; + + const sendMsg = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + buttonMessage + ); + + await verifyMessage(sendMsg, ticket, ticket.contact); + } + + const botText = async () => { + let options = ""; + + queueOptions.forEach((option, i) => { + options += `*[ ${option.option} ]* - ${option.title}\n`; + }); + //options += `\n*[ 0 ]* - Menu anterior`; + options += `\n*[ # ]* - Menu inicial`; + + const textMessage = { + text: formatBody(`\u200e${queue.greetingMessage}\n\n${options}`, ticket.contact), + }; + + const sendMsg = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + textMessage + ); + + await verifyMessage(sendMsg, ticket, ticket.contact); + }; + + // if (buttonActive.value === "list") { + // return botList(); + // }; + + if (buttonActive.value === "button" && QueueOption.length <= 4) { + return botButton(); + } + + if (buttonActive.value === "text") { + return botText(); + } + + if (buttonActive.value === "button" && QueueOption.length > 4) { + return botText(); + } + } else if (!isNil(queue) && !isNil(ticket.queueOptionId)) { + const currentOption = await QueueOption.findByPk(ticket.queueOptionId); + const queueOptions = await QueueOption.findAll({ + where: { parentId: ticket.queueOptionId }, + order: [ + ["option", "ASC"], + ["createdAt", "ASC"], + ], + }); + + if (queueOptions.length > -1) { + + const companyId = ticket.companyId; + const buttonActive = await Setting.findOne({ + where: { + key: "chatBotType", + companyId + } + }); + + const botList = async () => { + const sectionsRows = []; + + queueOptions.forEach((option, i) => { + sectionsRows.push({ + title: option.title, + rowId: `${option.option}` + }); + }); + sectionsRows.push({ + title: "Menu inicial *[ 0 ]* Menu anterior", + rowId: `#` + }); + const sections = [ + { + rows: sectionsRows + } + ]; + + const listMessage = { + text: formatBody(`\u200e${currentOption.message}`, ticket.contact), + buttonText: "Escolha uma opção", + sections + }; + + const sendMsg = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + listMessage + ); + + await verifyMessage(sendMsg, ticket, ticket.contact); + } + + const botButton = async () => { + const buttons = []; + queueOptions.forEach((option, i) => { + buttons.push({ + buttonId: `${option.option}`, + buttonText: { displayText: option.title }, + type: 4 + }); + }); + buttons.push({ + buttonId: `#`, + buttonText: { displayText: "Menu inicial *[ 0 ]* Menu anterior" }, + type: 4 + }); + + const buttonMessage = { + text: formatBody(`\u200e${currentOption.message}`, ticket.contact), + buttons, + headerType: 4 + }; + + const sendMsg = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + buttonMessage + ); + + await verifyMessage(sendMsg, ticket, ticket.contact); + } + + const botText = async () => { + + let options = ""; + + queueOptions.forEach((option, i) => { + options += `*[ ${option.option} ]* - ${option.title}\n`; + }); + options += `\n*[ 0 ]* - Menu anterior`; + options += `\n*[ # ]* - Menu inicial`; + const textMessage = { + text: formatBody(`\u200e${currentOption.message}\n\n${options}`, ticket.contact), + }; + + const sendMsg = await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net"}`, + textMessage + ); + + await verifyMessage(sendMsg, ticket, ticket.contact); + }; + + if (buttonActive.value === "list") { + return botList(); + }; + + if (buttonActive.value === "button" && QueueOption.length <= 4) { + return botButton(); + } + + if (buttonActive.value === "text") { + return botText(); + } + + if (buttonActive.value === "button" && QueueOption.length > 4) { + return botText(); + } + } + } +} + +export const handleMessageIntegration = async ( + msg: proto.IWebMessageInfo, + wbot: Session, + queueIntegration: QueueIntegrations, + ticket: Ticket +): Promise => { + const msgType = getTypeMessage(msg); + + if (queueIntegration.type === "n8n" || queueIntegration.type === "webhook") { + if (queueIntegration?.urlN8N) { + const options = { + method: "POST", + url: queueIntegration?.urlN8N, + headers: { + "Content-Type": "application/json" + }, + json: msg + }; + try { + request(options, function (error, response) { + if (error) { + throw new Error(error); + } + else { + console.log(response.body); + } + }); + } catch (error) { + throw new Error(error); + } + } + + } else if (queueIntegration.type === "typebot") { + console.log("entrou no typebot") + // await typebots(ticket, msg, wbot, queueIntegration); + await typebotListener({ ticket, msg, wbot, typebot: queueIntegration }); + + } +} + +const handleMessage = async ( + msg: proto.IWebMessageInfo, + wbot: Session, + companyId: number +): Promise => { + + let mediaSent: Message | undefined; + + if (!isValidMsg(msg)) return; + try { + let msgContact: IMe; + let groupContact: Contact | undefined; + + const isGroup = msg.key.remoteJid?.endsWith("@g.us"); + + const msgIsGroupBlock = await Setting.findOne({ + where: { + companyId, + key: "CheckMsgIsGroup", + }, + }); + + const bodyMessage = getBodyMessage(msg); + const msgType = getTypeMessage(msg); + + const hasMedia = + msg.message?.audioMessage || + msg.message?.imageMessage || + msg.message?.videoMessage || + msg.message?.documentMessage || + msg.message?.documentWithCaptionMessage || + msg.message.stickerMessage; + if (msg.key.fromMe) { + if (/\u200e/.test(bodyMessage)) return; + + if ( + !hasMedia && + msgType !== "conversation" && + msgType !== "extendedTextMessage" && + msgType !== "vcard" + ) + return; + msgContact = await getContactMessage(msg, wbot); + } else { + msgContact = await getContactMessage(msg, wbot); + } + + if (msgIsGroupBlock?.value === "enabled" && isGroup) return; + + if (isGroup) { + const grupoMeta = await wbot.groupMetadata(msg.key.remoteJid); + const msgGroupContact = { + id: grupoMeta.id, + name: grupoMeta.subject + }; + groupContact = await verifyContact(msgGroupContact, wbot, companyId); + } + + const whatsapp = await ShowWhatsAppService(wbot.id!, companyId); + const contact = await verifyContact(msgContact, wbot, companyId); + + let unreadMessages = 0; + + + if (msg.key.fromMe) { + await cacheLayer.set(`contacts:${contact.id}:unreads`, "0"); + } else { + const unreads = await cacheLayer.get(`contacts:${contact.id}:unreads`); + unreadMessages = +unreads + 1; + await cacheLayer.set( + `contacts:${contact.id}:unreads`, + `${unreadMessages}` + ); + } + + const lastMessage = await Message.findOne({ + where: { + contactId: contact.id, + companyId, + }, + order: [["createdAt", "DESC"]], + }); + + if (unreadMessages === 0 && whatsapp.complationMessage && formatBody(whatsapp.complationMessage, contact).trim().toLowerCase() === lastMessage?.body.trim().toLowerCase()) { + return; + } + + const ticket = await FindOrCreateTicketService(contact, wbot.id!, unreadMessages, companyId, groupContact); + + + + await provider(ticket, msg, companyId, contact, wbot as WASocket); + + // voltar para o menu inicial + + if (bodyMessage == "#") { + await ticket.update({ + queueOptionId: null, + chatbot: false, + queueId: null, + }); + await verifyQueue(wbot, msg, ticket, ticket.contact); + return; + } + + + const ticketTraking = await FindOrCreateATicketTrakingService({ + ticketId: ticket.id, + companyId, + whatsappId: whatsapp?.id + }); + + try { + if (!msg.key.fromMe) { + /** + * Tratamento para avaliação do atendente + */ + + // // dev Ricardo: insistir a responder avaliação + // const rate_ = Number(bodyMessage); + + // if ((ticket?.lastMessage.includes('_Insatisfeito_') || ticket?.lastMessage.includes('Por favor avalie nosso atendimento.')) && (!isFinite(rate_))) { + // const debouncedSentMessage = debounce( + // async () => { + // await wbot.sendMessage( + // `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net" + // }`, + // { + // text: 'Por favor avalie nosso atendimento.' + // } + // ); + // }, + // 1000, + // ticket.id + // ); + // debouncedSentMessage(); + // return; + // } + // // dev Ricardo + + if (ticketTraking !== null && verifyRating(ticketTraking)) { + + handleRating(parseFloat(bodyMessage), ticket, ticketTraking); + return; + } + } + } catch (e) { + Sentry.captureException(e); + console.log(e); + } + + // Atualiza o ticket se a ultima mensagem foi enviada por mim, para que possa ser finalizado. + try { + await ticket.update({ + fromMe: msg.key.fromMe, + }); + } catch (e) { + Sentry.captureException(e); + console.log(e); + } + + if (hasMedia) { + mediaSent = await verifyMediaMessage(msg, ticket, contact); + } else { + await verifyMessage(msg, ticket, contact); + } + + const currentSchedule = await VerifyCurrentSchedule(companyId); + const scheduleType = await Setting.findOne({ + where: { + companyId, + key: "scheduleType" + } + }); + + + try { + if (!msg.key.fromMe && scheduleType) { + /** + * Tratamento para envio de mensagem quando a empresa está fora do expediente + */ + if ( + scheduleType.value === "company" && + !isNil(currentSchedule) && + (!currentSchedule || currentSchedule.inActivity === false) + ) { + const body = `\u200e ${whatsapp.outOfHoursMessage}`; + + const debouncedSentMessage = debounce( + async () => { + await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net" + }`, + { + text: body + } + ); + }, + 3000, + ticket.id + ); + debouncedSentMessage(); + return; + } + + + if (scheduleType.value === "queue" && ticket.queueId !== null) { + + /** + * Tratamento para envio de mensagem quando a fila está fora do expediente + */ + const queue = await Queue.findByPk(ticket.queueId); + + const { schedules }: any = queue; + const now = moment(); + const weekday = now.format("dddd").toLowerCase(); + let schedule = null; + + if (Array.isArray(schedules) && schedules.length > 0) { + schedule = schedules.find( + s => + s.weekdayEn === weekday && + s.startTime !== "" && + s.startTime !== null && + s.endTime !== "" && + s.endTime !== null + ); + } + + if ( + scheduleType.value === "queue" && + queue.outOfHoursMessage !== null && + queue.outOfHoursMessage !== "" && + !isNil(schedule) + ) { + const startTime = moment(schedule.startTime, "HH:mm"); + const endTime = moment(schedule.endTime, "HH:mm"); + + if (now.isBefore(startTime) || now.isAfter(endTime)) { + const body = `${queue.outOfHoursMessage}`; + const debouncedSentMessage = debounce( + async () => { + await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net" + }`, + { + text: body + } + ); + }, + 3000, + ticket.id + ); + debouncedSentMessage(); + return; + } + } + } + + } + } catch (e) { + Sentry.captureException(e); + console.log(e); + } + + try { + if (!msg.key.fromMe) { + if (ticketTraking !== null && verifyRating(ticketTraking)) { + handleRating(parseFloat(bodyMessage), ticket, ticketTraking); + return; + } + } + } catch (e) { + Sentry.captureException(e); + console.log(e); + } + + //openai na conexao + if ( + !ticket.queue && + !isGroup && + !msg.key.fromMe && + !ticket.userId && + !isNil(whatsapp.promptId) + ) { + await handleOpenAi(msg, wbot, ticket, contact, mediaSent); + } + + //integraçao na conexao + if ( + !msg.key.fromMe && + !ticket.isGroup && + !ticket.queue && + !ticket.user && + ticket.chatbot && + !isNil(whatsapp.integrationId) && + !ticket.useIntegration + ) { + + const integrations = await ShowQueueIntegrationService(whatsapp.integrationId, companyId); + + await handleMessageIntegration(msg, wbot, integrations, ticket) + + return + } + + //openai na fila + if ( + !isGroup && + !msg.key.fromMe && + !ticket.userId && + !isNil(ticket.promptId) && + ticket.useIntegration && + ticket.queueId + + ) { + await handleOpenAi(msg, wbot, ticket, contact, mediaSent); + } + + if ( + !msg.key.fromMe && + !ticket.isGroup && + !ticket.userId && + ticket.integrationId && + ticket.useIntegration && + ticket.queue + ) { + + console.log("entrou no type 1974") + const integrations = await ShowQueueIntegrationService(ticket.integrationId, companyId); + + await handleMessageIntegration(msg, wbot, integrations, ticket) + + } + + if ( + !ticket.queue && + !ticket.isGroup && + !msg.key.fromMe && + !ticket.userId && + whatsapp.queues.length >= 1 && + !ticket.useIntegration + ) { + + await verifyQueue(wbot, msg, ticket, contact); + + if (ticketTraking.chatbotAt === null) { + await ticketTraking.update({ + chatbotAt: moment().toDate(), + }) + } + } + + const dontReadTheFirstQuestion = ticket.queue === null; + + await ticket.reload(); + + try { + //Fluxo fora do expediente + if (!msg.key.fromMe && scheduleType && ticket.queueId !== null) { + /** + * Tratamento para envio de mensagem quando a fila está fora do expediente + */ + const queue = await Queue.findByPk(ticket.queueId); + + const { schedules }: any = queue; + const now = moment(); + const weekday = now.format("dddd").toLowerCase(); + let schedule = null; + + if (Array.isArray(schedules) && schedules.length > 0) { + schedule = schedules.find( + s => + s.weekdayEn === weekday && + s.startTime !== "" && + s.startTime !== null && + s.endTime !== "" && + s.endTime !== null + ); + } + + if ( + scheduleType.value === "queue" && + queue.outOfHoursMessage !== null && + queue.outOfHoursMessage !== "" && + !isNil(schedule) + ) { + const startTime = moment(schedule.startTime, "HH:mm"); + const endTime = moment(schedule.endTime, "HH:mm"); + + if (now.isBefore(startTime) || now.isAfter(endTime)) { + const body = queue.outOfHoursMessage; + const debouncedSentMessage = debounce( + async () => { + await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net" + }`, + { + text: body + } + ); + }, + 3000, + ticket.id + ); + debouncedSentMessage(); + return; + } + } + } + } catch (e) { + Sentry.captureException(e); + console.log(e); + } + + + + if (!whatsapp?.queues?.length && !ticket.userId && !isGroup && !msg.key.fromMe) { + + const lastMessage = await Message.findOne({ + where: { + ticketId: ticket.id, + fromMe: true + }, + order: [["createdAt", "DESC"]] + }); + + if (lastMessage && lastMessage.body.includes(whatsapp.greetingMessage)) { + return; + } + + if (whatsapp.greetingMessage) { + + const debouncedSentMessage = debounce( + async () => { + await wbot.sendMessage( + `${ticket.contact.number}@${ticket.isGroup ? "g.us" : "s.whatsapp.net" + }`, + { + text: whatsapp.greetingMessage + } + ); + }, + 1000, + ticket.id + ); + debouncedSentMessage(); + return; + } + + } + + + if (whatsapp.queues.length == 1 && ticket.queue) { + if (ticket.chatbot && !msg.key.fromMe) { + await handleChartbot(ticket, msg, wbot); + } + } + if (whatsapp.queues.length > 1 && ticket.queue) { + if (ticket.chatbot && !msg.key.fromMe) { + await handleChartbot(ticket, msg, wbot, dontReadTheFirstQuestion); + } + } + + } catch (err) { + console.log(err) + Sentry.captureException(err); + logger.error(`Error handling whatsapp message: Err: ${err}`); + } +}; + +const handleMsgAck = async ( + msg: WAMessage, + chat: number | null | undefined +) => { + await new Promise((r) => setTimeout(r, 500)); + const io = getIO(); + + try { + const messageToUpdate = await Message.findByPk(msg.key.id, { + include: [ + "contact", + { + model: Message, + as: "quotedMsg", + include: ["contact"], + }, + ], + }); + + if (!messageToUpdate) return; + await messageToUpdate.update({ ack: chat }); + io.to(messageToUpdate.ticketId.toString()).emit( + `company-${messageToUpdate.companyId}-appMessage`, + { + action: "update", + message: messageToUpdate, + } + ); + } catch (err) { + Sentry.captureException(err); + logger.error(`Error handling message ack. Err: ${err}`); + } +}; + +const verifyRecentCampaign = async ( + message: proto.IWebMessageInfo, + companyId: number +) => { + if (!message.key.fromMe) { + const number = message.key.remoteJid.replace(/\D/g, ""); + const campaigns = await Campaign.findAll({ + where: { companyId, status: "EM_ANDAMENTO", confirmation: true }, + }); + if (campaigns) { + const ids = campaigns.map((c) => c.id); + const campaignShipping = await CampaignShipping.findOne({ + where: { campaignId: { [Op.in]: ids }, number, confirmation: null }, + }); + + if (campaignShipping) { + await campaignShipping.update({ + confirmedAt: moment(), + confirmation: true, + }); + await campaignQueue.add( + "DispatchCampaign", + { + campaignShippingId: campaignShipping.id, + campaignId: campaignShipping.campaignId, + }, + { + delay: parseToMilliseconds(randomValue(0, 10)), + } + ); + } + } + } +}; + +const verifyCampaignMessageAndCloseTicket = async ( + message: proto.IWebMessageInfo, + companyId: number +) => { + const io = getIO(); + const body = getBodyMessage(message); + const isCampaign = /\u200c/.test(body); + if (message.key.fromMe && isCampaign) { + const messageRecord = await Message.findOne({ + where: { id: message.key.id!, companyId }, + }); + const ticket = await Ticket.findByPk(messageRecord.ticketId); + await ticket.update({ status: "closed" }); + + io.to(`company-${ticket.companyId}-open`) + .to(`queue-${ticket.queueId}-open`) + .emit(`company-${ticket.companyId}-ticket`, { + action: "delete", + ticket, + ticketId: ticket.id, + }); + + io.to(`company-${ticket.companyId}-${ticket.status}`) + .to(`queue-${ticket.queueId}-${ticket.status}`) + .to(ticket.id.toString()) + .emit(`company-${ticket.companyId}-ticket`, { + action: "update", + ticket, + ticketId: ticket.id, + }); + } +}; + +const filterMessages = (msg: WAMessage): boolean => { + if (msg.message?.protocolMessage) return false; + + if ( + [ + WAMessageStubType.REVOKE, + WAMessageStubType.E2E_DEVICE_CHANGED, + WAMessageStubType.E2E_IDENTITY_CHANGED, + WAMessageStubType.CIPHERTEXT + ].includes(msg.messageStubType as WAMessageStubType) + ) + return false; + + return true; +}; + +const wbotMessageListener = async (wbot: Session, companyId: number): Promise => { + try { + wbot.ev.on("messages.upsert", async (messageUpsert: ImessageUpsert) => { + const messages = messageUpsert.messages + .filter(filterMessages) + .map(msg => msg); + + if (!messages) return; + + messages.forEach(async (message: proto.IWebMessageInfo) => { + + const messageExists = await Message.count({ + where: { id: message.key.id!, companyId } + }); + + if (!messageExists) { + await handleMessage(message, wbot, companyId); + await verifyRecentCampaign(message, companyId); + await verifyCampaignMessageAndCloseTicket(message, companyId); + } + }); + }); + + wbot.ev.on("messages.update", (messageUpdate: WAMessageUpdate[]) => { + if (messageUpdate.length === 0) return; + messageUpdate.forEach(async (message: WAMessageUpdate) => { + (wbot as WASocket)!.readMessages([message.key]) + + handleMsgAck(message, message.update.status); + }); + }); + + // wbot.ev.on("messages.set", async (messageSet: IMessage) => { + // messageSet.messages.filter(filterMessages).map(msg => msg); + // }); + } catch (error) { + Sentry.captureException(error); + logger.error(`Error handling wbot message listener. Err: ${error}`); + } +}; + +export { wbotMessageListener, handleMessage }; diff --git a/backend/src/services/WbotServices/wbotMonitor.ts b/backend/src/services/WbotServices/wbotMonitor.ts new file mode 100644 index 0000000..cb2115c --- /dev/null +++ b/backend/src/services/WbotServices/wbotMonitor.ts @@ -0,0 +1,117 @@ +import { + WASocket, + BinaryNode, + Contact as BContact, +} from "@whiskeysockets/baileys"; +import * as Sentry from "@sentry/node"; + +import { Op } from "sequelize"; +// import { getIO } from "../../libs/socket"; +import { Store } from "../../libs/store"; +import Contact from "../../models/Contact"; +import Setting from "../../models/Setting"; +import Ticket from "../../models/Ticket"; +import Whatsapp from "../../models/Whatsapp"; +import { logger } from "../../utils/logger"; +import createOrUpdateBaileysService from "../BaileysServices/CreateOrUpdateBaileysService"; +import CreateMessageService from "../MessageServices/CreateMessageService"; + +type Session = WASocket & { + id?: number; + store?: Store; +}; + +interface IContact { + contacts: BContact[]; +} + +const wbotMonitor = async ( + wbot: Session, + whatsapp: Whatsapp, + companyId: number +): Promise => { + try { + wbot.ws.on("CB:call", async (node: BinaryNode) => { + const content = node.content[0] as any; + + if (content.tag === "offer") { + const { from, id } = node.attrs; + + } + + if (content.tag === "terminate") { + const sendMsgCall = await Setting.findOne({ + where: { key: "call", companyId }, + }); + + if (sendMsgCall.value === "disabled") { + await wbot.sendMessage(node.attrs.from, { + text: + "*Mensagem Automática:*\n\nAs chamadas de voz e vídeo estão desabilitas para esse WhatsApp, favor enviar uma mensagem de texto. Obrigado", + }); + + const number = node.attrs.from.replace(/\D/g, ""); + + const contact = await Contact.findOne({ + where: { companyId, number }, + }); + + const ticket = await Ticket.findOne({ + where: { + contactId: contact.id, + whatsappId: wbot.id, + //status: { [Op.or]: ["close"] }, + companyId + }, + }); + // se não existir o ticket não faz nada. + if (!ticket) return; + + const date = new Date(); + const hours = date.getHours(); + const minutes = date.getMinutes(); + + const body = `Chamada de voz/vídeo perdida às ${hours}:${minutes}`; + const messageData = { + id: content.attrs["call-id"], + ticketId: ticket.id, + contactId: contact.id, + body, + fromMe: false, + mediaType: "call_log", + read: true, + quotedMsgId: null, + ack: 1, + }; + + await ticket.update({ + lastMessage: body, + }); + + + if(ticket.status === "closed") { + await ticket.update({ + status: "pending", + }); + } + + return CreateMessageService({ messageData, companyId: companyId }); + } + } + }); + + wbot.ev.on("contacts.upsert", async (contacts: BContact[]) => { + + await createOrUpdateBaileysService({ + whatsappId: whatsapp.id, + contacts, + }); + }); + + } catch (err) { + Sentry.captureException(err); + logger.error(err); + } +}; + +export default wbotMonitor; diff --git a/backend/src/services/WhatsappService/AssociateWhatsappQueue.ts b/backend/src/services/WhatsappService/AssociateWhatsappQueue.ts new file mode 100644 index 0000000..5f840f7 --- /dev/null +++ b/backend/src/services/WhatsappService/AssociateWhatsappQueue.ts @@ -0,0 +1,12 @@ +import Whatsapp from "../../models/Whatsapp"; + +const AssociateWhatsappQueue = async ( + whatsapp: Whatsapp, + queueIds: number[] +): Promise => { + await whatsapp.$set("queues", queueIds); + + await whatsapp.reload(); +}; + +export default AssociateWhatsappQueue; diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts new file mode 100644 index 0000000..3073a25 --- /dev/null +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -0,0 +1,178 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Whatsapp from "../../models/Whatsapp"; +import Company from "../../models/Company"; +import Plan from "../../models/Plan"; +import AssociateWhatsappQueue from "./AssociateWhatsappQueue"; + +interface Request { + name: string; + companyId: number; + queueIds?: number[]; + greetingMessage?: string; + complationMessage?: string; + outOfHoursMessage?: string; + ratingMessage?: string; + status?: string; + isDefault?: boolean; + token?: string; + provider?: string; + //sendIdQueue?: number; + //timeSendQueue?: number; + transferQueueId?: number; + timeToTransfer?: number; + promptId?: number; + maxUseBotQueues?: number; + timeUseBotQueues?: number; + expiresTicket?: number; + expiresInactiveMessage?: string; +} + +interface Response { + whatsapp: Whatsapp; + oldDefaultWhatsapp: Whatsapp | null; +} + +const CreateWhatsAppService = async ({ + name, + status = "OPENING", + queueIds = [], + greetingMessage, + complationMessage, + outOfHoursMessage, + ratingMessage, + isDefault = false, + companyId, + token = "", + provider = "beta", + //timeSendQueue, + //sendIdQueue, + transferQueueId, + timeToTransfer, + promptId, + maxUseBotQueues = 3, + timeUseBotQueues = 0, + expiresTicket = 0, + expiresInactiveMessage = "" +}: Request): Promise => { + const company = await Company.findOne({ + where: { + id: companyId + }, + include: [{ model: Plan, as: "plan" }] + }); + + if (company !== null) { + const whatsappCount = await Whatsapp.count({ + where: { + companyId + } + }); + + if (whatsappCount >= company.plan.connections) { + throw new AppError( + `Número máximo de conexões já alcançado: ${whatsappCount}` + ); + } + } + + const schema = Yup.object().shape({ + name: Yup.string() + .required() + .min(2) + .test( + "Check-name", + "Esse nome já está sendo utilizado por outra conexão", + async value => { + if (!value) return false; + const nameExists = await Whatsapp.findOne({ + where: { name: value } + }); + return !nameExists; + } + ), + isDefault: Yup.boolean().required() + }); + + try { + await schema.validate({ name, status, isDefault }); + } catch (err: any) { + throw new AppError(err.message); + } + + const whatsappFound = await Whatsapp.findOne({ where: { companyId } }); + + isDefault = !whatsappFound; + + let oldDefaultWhatsapp: Whatsapp | null = null; + + if (isDefault) { + oldDefaultWhatsapp = await Whatsapp.findOne({ + where: { isDefault: true, companyId } + }); + if (oldDefaultWhatsapp) { + await oldDefaultWhatsapp.update({ isDefault: false, companyId }); + } + } + + if (queueIds.length > 1 && !greetingMessage) { + throw new AppError("ERR_WAPP_GREETING_REQUIRED"); + } + + if (token !== null && token !== "") { + const tokenSchema = Yup.object().shape({ + token: Yup.string() + .required() + .min(2) + .test( + "Check-token", + "This whatsapp token is already used.", + async value => { + if (!value) return false; + const tokenExists = await Whatsapp.findOne({ + where: { token: value } + }); + return !tokenExists; + } + ) + }); + + try { + await tokenSchema.validate({ token }); + } catch (err: any) { + throw new AppError(err.message); + } + } + + const whatsapp = await Whatsapp.create( + { + name, + status, + greetingMessage, + complationMessage, + outOfHoursMessage, + ratingMessage, + isDefault, + companyId, + token, + provider, + //timeSendQueue, + //sendIdQueue, + transferQueueId, + timeToTransfer, + promptId, + maxUseBotQueues, + timeUseBotQueues, + expiresTicket, + expiresInactiveMessage + }, + { include: ["queues"] } + ); + + await AssociateWhatsappQueue(whatsapp, queueIds); + + return { whatsapp, oldDefaultWhatsapp }; +}; + +export default CreateWhatsAppService; diff --git a/backend/src/services/WhatsappService/DeleteWhatsAppService.ts b/backend/src/services/WhatsappService/DeleteWhatsAppService.ts new file mode 100644 index 0000000..ff516b8 --- /dev/null +++ b/backend/src/services/WhatsappService/DeleteWhatsAppService.ts @@ -0,0 +1,16 @@ +import Whatsapp from "../../models/Whatsapp"; +import AppError from "../../errors/AppError"; + +const DeleteWhatsAppService = async (id: string): Promise => { + const whatsapp = await Whatsapp.findOne({ + where: { id } + }); + + if (!whatsapp) { + throw new AppError("ERR_NO_WAPP_FOUND", 404); + } + + await whatsapp.destroy(); +}; + +export default DeleteWhatsAppService; diff --git a/backend/src/services/WhatsappService/ListWhatsAppsService.ts b/backend/src/services/WhatsappService/ListWhatsAppsService.ts new file mode 100644 index 0000000..e1e51a7 --- /dev/null +++ b/backend/src/services/WhatsappService/ListWhatsAppsService.ts @@ -0,0 +1,36 @@ +import { FindOptions } from "sequelize/types"; +import Queue from "../../models/Queue"; +import Whatsapp from "../../models/Whatsapp"; + +interface Request { + companyId: number; + session?: number | string; +} + +const ListWhatsAppsService = async ({ + session, + companyId +}: Request): Promise => { + const options: FindOptions = { + where: { + companyId + }, + include: [ + { + model: Queue, + as: "queues", + attributes: ["id", "name", "color", "greetingMessage"] + } + ] + }; + + if (session !== undefined && session == 0) { + options.attributes = { exclude: ["session"] }; + } + + const whatsapps = await Whatsapp.findAll(options); + + return whatsapps; +}; + +export default ListWhatsAppsService; diff --git a/backend/src/services/WhatsappService/ShowWhatsAppService.ts b/backend/src/services/WhatsappService/ShowWhatsAppService.ts new file mode 100644 index 0000000..7a1751d --- /dev/null +++ b/backend/src/services/WhatsappService/ShowWhatsAppService.ts @@ -0,0 +1,46 @@ +import Whatsapp from "../../models/Whatsapp"; +import AppError from "../../errors/AppError"; +import Queue from "../../models/Queue"; +import QueueOption from "../../models/QueueOption"; +import { FindOptions } from "sequelize/types"; +import Prompt from "../../models/Prompt"; + +const ShowWhatsAppService = async ( + id: string | number, + companyId: number, + session?: any +): Promise => { + const findOptions: FindOptions = { + include: [ + { + model: Queue, + as: "queues", + attributes: ["id", "name", "color", "greetingMessage", "integrationId", "promptId"], + include: [{ model: QueueOption, as: "options" }] + }, + { + model: Prompt, + as: "prompt", + } + ], + order: [["queues", "orderQueue", "ASC"]] + }; + + if (session !== undefined && session == 0) { + findOptions.attributes = { exclude: ["session"] }; + } + + const whatsapp = await Whatsapp.findByPk(id, findOptions); + + if (whatsapp?.companyId !== companyId) { + throw new AppError("Não é possível acessar registros de outra empresa"); + } + + if (!whatsapp) { + throw new AppError("ERR_NO_WAPP_FOUND", 404); + } + + return whatsapp; +}; + +export default ShowWhatsAppService; diff --git a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts new file mode 100644 index 0000000..f42fd80 --- /dev/null +++ b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts @@ -0,0 +1,130 @@ +import * as Yup from "yup"; +import { Op } from "sequelize"; + +import AppError from "../../errors/AppError"; +import Whatsapp from "../../models/Whatsapp"; +import ShowWhatsAppService from "./ShowWhatsAppService"; +import AssociateWhatsappQueue from "./AssociateWhatsappQueue"; + +interface WhatsappData { + name?: string; + status?: string; + session?: string; + isDefault?: boolean; + greetingMessage?: string; + complationMessage?: string; + outOfHoursMessage?: string; + ratingMessage?: string; + queueIds?: number[]; + token?: string; + //sendIdQueue?: number; + //timeSendQueue?: number; + transferQueueId?: number; + timeToTransfer?: number; + promptId?: number; + maxUseBotQueues?: number; + timeUseBotQueues?: number; + expiresTicket?: number; + expiresInactiveMessage?: string; + +} + +interface Request { + whatsappData: WhatsappData; + whatsappId: string; + companyId: number; +} + +interface Response { + whatsapp: Whatsapp; + oldDefaultWhatsapp: Whatsapp | null; +} + +const UpdateWhatsAppService = async ({ + whatsappData, + whatsappId, + companyId +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string().min(2), + status: Yup.string(), + isDefault: Yup.boolean() + }); + + const { + name, + status, + isDefault, + session, + greetingMessage, + complationMessage, + outOfHoursMessage, + ratingMessage, + queueIds = [], + token, + //timeSendQueue, + //sendIdQueue = null, + transferQueueId, + timeToTransfer, + promptId, + maxUseBotQueues, + timeUseBotQueues, + expiresTicket, + expiresInactiveMessage + } = whatsappData; + + try { + await schema.validate({ name, status, isDefault }); + } catch (err: any) { + throw new AppError(err.message); + } + + if (queueIds.length > 1 && !greetingMessage) { + throw new AppError("ERR_WAPP_GREETING_REQUIRED"); + } + + let oldDefaultWhatsapp: Whatsapp | null = null; + + if (isDefault) { + oldDefaultWhatsapp = await Whatsapp.findOne({ + where: { + isDefault: true, + id: { [Op.not]: whatsappId }, + companyId + } + }); + if (oldDefaultWhatsapp) { + await oldDefaultWhatsapp.update({ isDefault: false }); + } + } + + const whatsapp = await ShowWhatsAppService(whatsappId, companyId); + + await whatsapp.update({ + name, + status, + session, + greetingMessage, + complationMessage, + outOfHoursMessage, + ratingMessage, + isDefault, + companyId, + token, + //timeSendQueue, + //sendIdQueue, + transferQueueId, + timeToTransfer, + promptId, + maxUseBotQueues, + timeUseBotQueues, + expiresTicket, + expiresInactiveMessage + }); + + await AssociateWhatsappQueue(whatsapp, queueIds); + + return { whatsapp, oldDefaultWhatsapp }; +}; + +export default UpdateWhatsAppService; diff --git a/backend/src/utils/logger.ts b/backend/src/utils/logger.ts new file mode 100644 index 0000000..0a499eb --- /dev/null +++ b/backend/src/utils/logger.ts @@ -0,0 +1,14 @@ +import pino from "pino"; + +const logger = pino({ + transport: { + target: 'pino-pretty', + options: { + levelFirst: true, + translateTime: true, + colorize: true, + } + } +}); + +export { logger }; diff --git a/backend/src/wbotTransferTicketQueue.ts b/backend/src/wbotTransferTicketQueue.ts new file mode 100644 index 0000000..1bd32ba --- /dev/null +++ b/backend/src/wbotTransferTicketQueue.ts @@ -0,0 +1,82 @@ +import { Op } from "sequelize"; +import TicketTraking from "./models/TicketTraking"; +import { format } from "date-fns"; +import moment from "moment"; +import Ticket from "./models/Ticket"; +import Whatsapp from "./models/Whatsapp"; +import { getIO } from "./libs/socket"; +import { logger } from "./utils/logger"; +import ShowTicketService from "./services/TicketServices/ShowTicketService"; + + +export const TransferTicketQueue = async (): Promise => { + + const io = getIO(); + + //buscar os tickets que em pendentes e sem fila + const tickets = await Ticket.findAll({ + where: { + status: "pending", + queueId: { + [Op.is]: null + }, + }, + + }); + + // varrer os tickets e verificar se algum deles está com o tempo estourado + tickets.forEach(async ticket => { + + + + const wpp = await Whatsapp.findOne({ + where: { + id: ticket.whatsappId + } + }); + + if (!wpp || !wpp.timeToTransfer || !wpp.transferQueueId || wpp.timeToTransfer == 0) return; + + let dataLimite = new Date(ticket.updatedAt); + dataLimite.setMinutes(dataLimite.getMinutes() + wpp.timeToTransfer); + + if (new Date() > dataLimite) { + + await ticket.update({ + + queueId: wpp.transferQueueId, + + }); + + const ticketTraking = await TicketTraking.findOne({ + where: { + ticketId: ticket.id + }, + order: [["createdAt", "DESC"]] + }); + + await ticketTraking.update({ + queuedAt: moment().toDate(), + queueId: wpp.transferQueueId, + }); + + const currentTicket = await ShowTicketService(ticket.id, ticket.companyId); + + io.to(ticket.status) + .to("notification") + .to(ticket.id.toString()) + .emit(`company-${ticket.companyId}-ticket`, { + action: "update", + ticket: currentTicket, + traking: "created ticket 33" + }); + + logger.info(`Transferencia de ticket automatica ticket id ${ticket.id} para a fila ${wpp.transferQueueId}`); + + } + + + }); + + +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..4a869e4 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "outDir": "./dist", + "strict": false, + "strictPropertyInitialization": false, + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/frontend/.DS_Store b/frontend/.DS_Store new file mode 100644 index 0000000..6d5c9df Binary files /dev/null and b/frontend/.DS_Store differ diff --git a/frontend/.env.exemple b/frontend/.env.exemple new file mode 100644 index 0000000..4d65288 --- /dev/null +++ b/frontend/.env.exemple @@ -0,0 +1,2 @@ +REACT_APP_BACKEND_URL=https://url front +REACT_APP_HOURS_CLOSE_TICKETS_AUTO = 24 diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..b8c74ae --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,26964 @@ +{ + "name": "frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.1.0", + "dependencies": { + "@date-io/date-fns": "^2.14.0", + "@emotion/styled": "^11.10.6", + "@material-ui/core": "4.12.3", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.56", + "@material-ui/pickers": "^3.3.10", + "@material-ui/styles": "^4.11.5", + "@mui/material": "^5.10.13", + "@mui/x-date-pickers": "^6.0.1", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.0.4", + "@testing-library/user-event": "^12.1.7", + "axios": "^0.21.1", + "bootstrap": "^5.2.3", + "chart.js": "^3.9.1", + "chartjs-plugin-datalabels": "^2.1.0", + "context": "^4.0.0", + "date-fns": "^2.16.1", + "emoji-mart": "^3.0.0", + "formik": "^2.2.0", + "formik-material-ui": "^3.0.1", + "gn-api-sdk-node": "^3.0.2", + "i18next": "^19.8.2", + "i18next-browser-languagedetector": "^6.0.1", + "jsonwebtoken": "^9.0.2", + "markdown-to-jsx": "^7.1.0", + "material-ui-color": "^1.2.0", + "mic-recorder-to-mp3": "^2.2.2", + "moment": "^2.29.1", + "qrcode.react": "^1.0.0", + "query-string": "^7.0.0", + "react": "^17.0.2", + "react-big-calendar": "^1.8.7", + "react-bootstrap": "^2.7.0", + "react-chartjs-2": "^4.3.1", + "react-color": "^2.19.3", + "react-copy-to-clipboard": "^5.1.0", + "react-csv": "^2.2.2", + "react-currency-format": "^1.1.0", + "react-dom": "^17.0.2", + "react-icons": "^4.4.0", + "react-input-mask": "^2.0.4", + "react-modal-image": "^2.5.0", + "react-number-format": "^4.6.4", + "react-qr-code": "^2.0.7", + "react-query": "^3.39.3", + "react-router-dom": "^5.2.0", + "react-scripts": "3.4.3", + "react-text-mask": "^5.5.0", + "react-toastify": "9.0.0", + "react-trello": "^2.2.11", + "recharts": "^2.0.2", + "socket.io-client": "^4.7.5", + "styled-components": "^5.3.5", + "text-mask-addons": "^3.8.0", + "use-debounce": "^7.0.0", + "use-sound": "^2.0.1", + "uuid": "^8.3.2", + "yup": "^0.32.8" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==" + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", + "dependencies": { + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", + "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz", + "integrity": "sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", + "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", + "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", + "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", + "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", + "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.1.tgz", + "integrity": "sha512-05RJdO/cCrtVWuAaSn1tS3bH8jbsJa/Y1uD186u6J4C/1mnHFxseeuWpsqr9anvo7TUulev7tm7GDwRV+VuhDw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz", + "integrity": "sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", + "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", + "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", + "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", + "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", + "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", + "dependencies": { + "@babel/helper-module-imports": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", + "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", + "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", + "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", + "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", + "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", + "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", + "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", + "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", + "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", + "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", + "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz", + "integrity": "sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-flow": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", + "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", + "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", + "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", + "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", + "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", + "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", + "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", + "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", + "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", + "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", + "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", + "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", + "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", + "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", + "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", + "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", + "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", + "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", + "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", + "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz", + "integrity": "sha512-QXp1U9x0R7tkiGB0FOk8o74jhnap0FlZ5gNkRIWdG3eP+SvMFg118e1zaWewDzgABb106QSKpVsD3Wgd8t6ifA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", + "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.1.tgz", + "integrity": "sha512-kDJgnPujTmAZ/9q2CN4m2/lRsUUPDvsG3+tSHWUJIzMGTt5U/b/fwWd3RO3n+5mjLrsBrVa5eKFRVSQbi3dF1w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.1.tgz", + "integrity": "sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", + "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", + "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", + "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz", + "integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==", + "dependencies": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "resolve": "^1.8.1", + "semver": "^5.5.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", + "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", + "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", + "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", + "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", + "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.4.tgz", + "integrity": "sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-typescript": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", + "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", + "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", + "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", + "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.4.tgz", + "integrity": "sha512-7Kl6cSmYkak0FK/FXjSEnLJ1N9T/WA2RkMhu17gZ/dsxKJUuTYNIylahPTzqpLyJN4WhDif8X0XK1R8Wsguo/A==", + "dependencies": { + "@babel/compat-data": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.4", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.4", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", + "@babel/plugin-transform-classes": "^7.24.1", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.1", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.1", + "@babel/plugin-transform-parameters": "^7.24.1", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.1", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.1", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz", + "integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-transform-typescript": "^7.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.4.tgz", + "integrity": "sha512-VOQOexSilscN24VEY810G/PqtpFvx/z6UqDIjIWbDe2368HhDLkYN5TYwaEz/+eRCUkhJ2WaNLLmQAlxzfWj4w==", + "dependencies": { + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "dependencies": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } + }, + "node_modules/@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@csstools/normalize.css": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", + "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" + }, + "node_modules/@date-io/core": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@date-io/core/-/core-2.17.0.tgz", + "integrity": "sha512-+EQE8xZhRM/hsY0CDTVyayMDDY5ihc4MqXCrPxooKw19yAzUIC6uUqsZeaOFNL9YKTNxYKrJP5DFgE8o5xRCOw==" + }, + "node_modules/@date-io/date-fns": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-2.17.0.tgz", + "integrity": "sha512-L0hWZ/mTpy3Gx/xXJ5tq5CzHo0L7ry6KEO9/w/JWiFWFLZgiNVo3ex92gOl3zmzjHqY/3Ev+5sehAr8UnGLEng==", + "dependencies": { + "@date-io/core": "^2.17.0" + }, + "peerDependencies": { + "date-fns": "^2.0.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + } + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, + "node_modules/@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "deprecated": "Moved to 'npm install @sideway/address'" + }, + "node_modules/@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "deprecated": "This version has been deprecated and is no longer supported or maintained" + }, + "node_modules/@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "deprecated": "This version has been deprecated and is no longer supported or maintained" + }, + "node_modules/@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "deprecated": "Switch to 'npm install joi'", + "dependencies": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "node_modules/@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "deprecated": "This version has been deprecated and is no longer supported or maintained", + "dependencies": { + "@hapi/hoek": "^8.3.0" + } + }, + "node_modules/@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dependencies": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@jest/console/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/console/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "dependencies": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/core/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/core/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/core/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "node_modules/@jest/core/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/core/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/core/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@jest/core/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@jest/core/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/core/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/core/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/core/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/@jest/core/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@jest/core/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/core/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dependencies": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/environment/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/environment/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/environment/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dependencies": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/fake-timers/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/fake-timers/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/fake-timers/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "node_modules/@jest/fake-timers/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/fake-timers/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/fake-timers/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/fake-timers/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@jest/fake-timers/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@jest/fake-timers/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/fake-timers/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/fake-timers/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@jest/fake-timers/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/fake-timers/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/fake-timers/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/fake-timers/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dependencies": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/reporters/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/reporters/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/reporters/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@jest/reporters/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/reporters/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/reporters/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/test-result/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/test-result/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/test-result/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dependencies": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/transform/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/transform/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/transform/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/transform/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@jest/transform/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@jest/transform/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/transform/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/transform/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@jest/transform/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/transform/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@material-ui/core": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz", + "integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.4", + "@material-ui/system": "^4.12.1", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/core/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@material-ui/icons": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz", + "integrity": "sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==", + "dependencies": { + "@babel/runtime": "^7.4.4" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@material-ui/core": "^4.0.0", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/lab": { + "version": "4.0.0-alpha.61", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.61.tgz", + "integrity": "sha512-rSzm+XKiNUjKegj8bzt5+pygZeckNLOr+IjykH8sYdVk7dE9y2ZuUSofiMV2bJk3qU+JHwexmw+q0RyNZB9ugg==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@material-ui/core": "^4.12.1", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/lab/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@material-ui/pickers": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.3.11.tgz", + "integrity": "sha512-pDYjbjUeabapijS2FpSwK/ruJdk7IGeAshpLbKDa3PRRKRy7Nv6sXxAvUg2F+lID/NwUKgBmCYS5bzrl7Xxqzw==", + "deprecated": "This package no longer supported. It has been relaced by @mui/x-date-pickers", + "dependencies": { + "@babel/runtime": "^7.6.0", + "@date-io/core": "1.x", + "@types/styled-jsx": "^2.2.8", + "clsx": "^1.0.2", + "react-transition-group": "^4.0.0", + "rifm": "^0.7.0" + }, + "peerDependencies": { + "@date-io/core": "^1.3.6", + "@material-ui/core": "^4.0.0", + "prop-types": "^15.6.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@material-ui/pickers/node_modules/@date-io/core": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@date-io/core/-/core-1.3.13.tgz", + "integrity": "sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA==" + }, + "node_modules/@material-ui/styles": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/styles/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@material-ui/styles/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@material-ui/system": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/system/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/utils": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@material-ui/utils/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz", + "integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz", + "integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.15", + "@mui/system": "^5.15.15", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mui/private-theming": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", + "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.14", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", + "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.14", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mui/types": { + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", + "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "6.19.9", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.19.9.tgz", + "integrity": "sha512-B2m4Fv/fOme5qmV6zuE3QnWQSvj3zKtI2OvikPz5prpiCcIxqpeytkQ7VfrWH3/Aqd5yhG1Yr4IgbqG0ymIXGg==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@mui/utils": "^5.14.16", + "@types/react-transition-group": "^4.4.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.8.6", + "@mui/system": "^5.8.0", + "date-fns": "^2.25.0 || ^3.2.0", + "date-fns-jalali": "^2.13.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.2.tgz", + "integrity": "sha512-0gKkgDYdnq1w+ey8KzG9l+H5Z821qh9vVjztk55rUg71vTk/Eaebeir+WtzcLLwTjw3m/asIjx8Y59y1lJZhBw==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.8.tgz", + "integrity": "sha512-6ndCv3oZ7r9vuP1Ok9KH55TM1/UkdBnP/fSraW0DFDMbPMzWKhVKeFAIEUCRCSdzayjZDcFYK6xbMlipN9dmMA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz", + "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==" + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz", + "integrity": "sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz", + "integrity": "sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz", + "integrity": "sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz", + "integrity": "sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz", + "integrity": "sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz", + "integrity": "sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.3.tgz", + "integrity": "sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^4.2.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^4.2.0", + "@svgr/babel-plugin-svg-dynamic-title": "^4.3.3", + "@svgr/babel-plugin-svg-em-dimensions": "^4.2.0", + "@svgr/babel-plugin-transform-react-native-svg": "^4.2.0", + "@svgr/babel-plugin-transform-svg-component": "^4.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/core": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.3.tgz", + "integrity": "sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w==", + "dependencies": { + "@svgr/plugin-jsx": "^4.3.3", + "camelcase": "^5.3.1", + "cosmiconfig": "^5.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/core/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/core/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/core/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz", + "integrity": "sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg==", + "dependencies": { + "@babel/types": "^7.4.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz", + "integrity": "sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w==", + "dependencies": { + "@babel/core": "^7.4.5", + "@svgr/babel-preset": "^4.3.3", + "@svgr/hast-util-to-babel-ast": "^4.3.2", + "svg-parser": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz", + "integrity": "sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w==", + "dependencies": { + "cosmiconfig": "^5.2.1", + "merge-deep": "^3.0.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@svgr/webpack": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-4.3.3.tgz", + "integrity": "sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg==", + "dependencies": { + "@babel/core": "^7.4.5", + "@babel/plugin-transform-react-constant-elements": "^7.0.0", + "@babel/preset-env": "^7.4.5", + "@babel/preset-react": "^7.0.0", + "@svgr/core": "^4.3.3", + "@svgr/plugin-jsx": "^4.3.3", + "@svgr/plugin-svgo": "^4.3.1", + "loader-utils": "^1.2.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.10.tgz", + "integrity": "sha512-CU+RF9FySljn7HVSkkjiB84hWkvTaI3rtLvF433+jRSBL2hMu3zX5bGhHS8C80SM++h4xy8hBSnUHFQHmRXSBw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", + "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.7.tgz", + "integrity": "sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^7.28.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@testing-library/react/node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@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" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "7.31.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz", + "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.6", + "lz-string": "^1.4.4", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@testing-library/react/node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==" + }, + "node_modules/@testing-library/react/node_modules/@types/yargs": { + "version": "15.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", + "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@testing-library/react/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@testing-library/react/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@testing-library/react/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@testing-library/user-event": { + "version": "12.8.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz", + "integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/lodash": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, + "node_modules/@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + }, + "node_modules/@types/q": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==" + }, + "node_modules/@types/react": { + "version": "17.0.80", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz", + "integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "^0.16", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" + }, + "node_modules/@types/styled-jsx": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@types/styled-jsx/-/styled-jsx-2.2.9.tgz", + "integrity": "sha512-W/iTlIkGEyTBGTEvZCey8EgQlQ5l0DwMqi3iOXlLs2kyBwYTXHKEiU6IZ5EwoRwngL8/dGYuzezSup89ttVHLw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", + "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", + "dependencies": { + "@typescript-eslint/experimental-utils": "2.34.0", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^2.0.0", + "eslint": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", + "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", + "dependencies": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", + "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", + "dependencies": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.34.0", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", + "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", + "dependencies": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "dependencies": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "dependencies": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dependencies": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz", + "integrity": "sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==", + "dependencies": { + "assert": "1.4.1", + "camelcase": "5.0.0", + "loader-utils": "1.2.3", + "object-path": "0.11.4", + "regex-parser": "2.2.10" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==" + }, + "node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/anymatch/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==" + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-equal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.2.tgz", + "integrity": "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", + "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha512-N+aAxov+CKVS3JuhDIQFr24XvZvwE96Wlhk9dytTg/GmwWoghdOvR8dspx8MVz71O+Y0pA3UPqHF68D6iy8UvQ==", + "dependencies": { + "util": "0.10.3" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-each": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/autosize": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.4.tgz", + "integrity": "sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/babel-code-frame/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==" + }, + "node_modules/babel-code-frame/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" + } + }, + "node_modules/babel-extract-comments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", + "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", + "dependencies": { + "babylon": "^6.18.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dependencies": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-jest/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/babel-jest/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/babel-jest/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-jest/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dependencies": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 6.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dependencies": { + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", + "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.1", + "core-js-compat": "^3.36.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", + "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "lodash": "^4.17.21", + "picomatch": "^2.3.1" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==" + }, + "node_modules/babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==", + "dependencies": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "node_modules/babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dependencies": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.2.tgz", + "integrity": "sha512-k58RtQOKH21NyKtzptoAvtAODuAJJs3ZhqBMl456/GnXEQ/0La92pNmwgWoMn5pBTrsvk3YYXdY7zpY4e3UIxA==", + "dependencies": { + "@babel/core": "7.9.0", + "@babel/plugin-proposal-class-properties": "7.8.3", + "@babel/plugin-proposal-decorators": "7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3", + "@babel/plugin-proposal-numeric-separator": "7.8.3", + "@babel/plugin-proposal-optional-chaining": "7.9.0", + "@babel/plugin-transform-flow-strip-types": "7.9.0", + "@babel/plugin-transform-react-display-name": "7.8.3", + "@babel/plugin-transform-runtime": "7.9.0", + "@babel/preset-env": "7.9.0", + "@babel/preset-react": "7.9.1", + "@babel/preset-typescript": "7.9.0", + "@babel/runtime": "7.9.0", + "babel-plugin-macros": "2.8.0", + "babel-plugin-transform-react-remove-prop-types": "0.4.24" + } + }, + "node_modules/babel-preset-react-app/node_modules/@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/babel-preset-react-app/node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz", + "integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-preset-react-app/node_modules/@babel/preset-env": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz", + "integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==", + "dependencies": { + "@babel/compat-data": "^7.9.0", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-numeric-separator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.9.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.9.0", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.8.3", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.9.0", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.9.0", + "@babel/plugin-transform-modules-commonjs": "^7.9.0", + "@babel/plugin-transform-modules-systemjs": "^7.9.0", + "@babel/plugin-transform-modules-umd": "^7.9.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.8.7", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.9.0", + "browserslist": "^4.9.1", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-preset-react-app/node_modules/@babel/preset-modules": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-preset-react-app/node_modules/@babel/preset-react": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.1.tgz", + "integrity": "sha512-aJBYF23MPj0RNdp/4bHnAP0NVqqZRr9kl0NAOP4nJCex6OYVio59+dnQzsAWFuogdLyeaKA1hmfUIVZkY5J+TQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-transform-react-display-name": "^7.8.3", + "@babel/plugin-transform-react-jsx": "^7.9.1", + "@babel/plugin-transform-react-jsx-development": "^7.9.0", + "@babel/plugin-transform-react-jsx-self": "^7.9.0", + "@babel/plugin-transform-react-jsx-source": "^7.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-preset-react-app/node_modules/@babel/runtime": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.0.tgz", + "integrity": "sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/babel-preset-react-app/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/babel-preset-react-app/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-preset-react-app/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/babel-preset-react-app/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "bin": { + "babylon": "bin/babylon.js" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dependencies": { + "resolve": "1.1.7" + } + }, + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==" + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dependencies": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cacache/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001612", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", + "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/chart.js": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", + "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" + }, + "node_modules/chartjs-plugin-datalabels": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz", + "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==", + "peerDependencies": { + "chart.js": ">=3.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/chokidar/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==", + "dependencies": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/coa/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/coa/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/coa/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha512-xzhzTJ5eC+gmIzvZq+C3kCJHsp9os6tJkrigDRZclyGtOKINbZtE8n1Tzmeh32jW+BUDPbvZpibwvJHBLGMVwg==", + "dependencies": { + "arity-n": "^1.0.4" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==" + }, + "node_modules/contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/context": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/context/-/context-4.0.0.tgz", + "integrity": "sha512-B5XTstvpRXt79RkiLR+/XqSCDPrtAsdIZAhZuBCj1bXzuHy7HVbhee+aJ/pHP/+pYrP7YAHdqDH91vfuwqD9/w==", + "dependencies": { + "vest-utils": "^0.0.5" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-concurrently/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.0.tgz", + "integrity": "sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz", + "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==", + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.0.tgz", + "integrity": "sha512-d3BrpyFr5eD4KcbRvQ3FTUx/KWmaDesr7+a3+1+P46IUnNoEt+oiLijPINZMEon7w9oGkIINWxrBAU9DEciwFQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "node_modules/css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "dependencies": { + "postcss": "^7.0.5" + }, + "bin": { + "css-blank-pseudo": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "bin": { + "css-has-pseudo": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-has-pseudo/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-loader": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", + "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==", + "dependencies": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.23", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.1.1", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.2", + "schema-utils": "^2.6.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "dependencies": { + "postcss": "^7.0.5" + }, + "bin": { + "css-prefers-color-scheme": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dependencies": { + "cssom": "0.3.x" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/cyclist": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.2.tgz", + "integrity": "sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dependencies": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-arithmetic": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz", + "integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==" + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==" + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dependencies": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dependencies": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dir-glob/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "deprecated": "Use your platform's native DOMException instead", + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.747", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz", + "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==" + }, + "node_modules/elliptic": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/emoji-mart": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.1.tgz", + "integrity": "sha512-sxpmMKxqLvcscu6mFn9ITHeZNkGzIvD0BSNFE/LJESPbCA8s1jM6bCDPjWbV31xHq7JXaxgpHxLB54RCbBZSlg==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0-0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/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" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-react-app": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz", + "integrity": "sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ==", + "dependencies": { + "confusing-browser-globals": "^1.0.9" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "2.x", + "@typescript-eslint/parser": "2.x", + "babel-eslint": "10.x", + "eslint": "6.x", + "eslint-plugin-flowtype": "3.x || 4.x", + "eslint-plugin-import": "2.x", + "eslint-plugin-jsx-a11y": "6.x", + "eslint-plugin-react": "7.x", + "eslint-plugin-react-hooks": "1.x || 2.x" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-loader": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-3.0.3.tgz", + "integrity": "sha512-+YRqB95PnNvxNp1HEjQmvf9KNvCin5HXYYseOXVC2U0KEcw4IkQ2IQEBG46j7+gW39bMzeu0GsUhVbBY3Votpw==", + "deprecated": "This loader has been deprecated. Please use eslint-webpack-plugin", + "dependencies": { + "fs-extra": "^8.1.0", + "loader-fs-cache": "^1.0.2", + "loader-utils": "^1.2.3", + "object-hash": "^2.0.1", + "schema-utils": "^2.6.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz", + "integrity": "sha512-W5hLjpFfZyZsXfo5anlu7HM970JBDqbEshAJUkeczP6BFCIfJXuiIBQXyberLRtOStT0OGPF8efeTbxlHk4LpQ==", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": ">=6.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", + "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", + "dependencies": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "2.x - 6.x" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", + "dependencies": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", + "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", + "dependencies": { + "@babel/runtime": "^7.4.5", + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.2", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^7.0.2", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==", + "dependencies": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/eslint-plugin-react": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz", + "integrity": "sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==", + "dependencies": { + "array-includes": "^3.1.1", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.3", + "object.entries": "^1.1.1", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.15.1", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.2", + "xregexp": "^4.3.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz", + "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==", + "engines": { + "node": ">=7" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "engines": { + "node": ">=6.5.0" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==" + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fast-glob/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "deprecated": "This module is no longer supported." + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dependencies": { + "flat-cache": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dependencies": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "node_modules/filesize": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", + "integrity": "sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dependencies": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash." + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz", + "integrity": "sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ==", + "dependencies": { + "babel-code-frame": "^6.22.0", + "chalk": "^2.4.1", + "chokidar": "^3.3.0", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + }, + "engines": { + "node": ">=6.11.5", + "yarn": ">=1.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formik": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.5.tgz", + "integrity": "sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/formik-material-ui": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/formik-material-ui/-/formik-material-ui-3.0.1.tgz", + "integrity": "sha512-N8oxZIdhY70npRv86IfF6Zaaps9RL3a37XRdq02WDroB3XZC1mXs6lA/zQ09ZYFWYJp/UjI80SKVpVa/xJOJJA==", + "peerDependencies": { + "@material-ui/core": ">=4.0.0", + "formik": ">=2.0.0", + "react": ">=16.8.0", + "tiny-warning": ">=1.0.2" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globalize": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/globalize/-/globalize-0.1.1.tgz", + "integrity": "sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==" + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" + }, + "node_modules/globby/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gn-api-sdk-node": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/gn-api-sdk-node/-/gn-api-sdk-node-3.0.4.tgz", + "integrity": "sha512-DZKeTj9bw+Ybe2ZZG9oeWlOEPUm3wsSpLnr/ZAEhxR2Q+b4xZ9juo0tblvc2qi6Q+hnEs03LOR+ULr+kt8mYkQ==", + "dependencies": { + "axios": "^1.2.1", + "randomstring": "^1.2.2" + } + }, + "node_modules/gn-api-sdk-node/node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/gn-api-sdk-node/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==" + }, + "node_modules/gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", + "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==" + }, + "node_modules/html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dependencies": { + "whatwg-encoding": "^1.0.1" + } + }, + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "node_modules/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dependencies": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-webpack-plugin": { + "version": "4.0.0-beta.11", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz", + "integrity": "sha512-4Xzepf0qWxf8CGg7/WQM5qBB2Lc/NFI7MhU59eUDTkuQp3skZczH4UA1d6oQyDEIoMDgERVhRyTdtUPZ5s5HBg==", + "deprecated": "please switch to a stable version", + "dependencies": { + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dependencies": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==" + }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, + "node_modules/i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.8.tgz", + "integrity": "sha512-Svm+MduCElO0Meqpj1kJAriTC6OhI41VhlT/A0UPjGoPZBhAHIaGE5EfsHlTpgdH09UVX7rcc72pSDDBeKSQQA==", + "dependencies": { + "@babel/runtime": "^7.19.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==" + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", + "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==" + }, + "node_modules/immutability-helper": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.9.1.tgz", + "integrity": "sha512-r/RmRG8xO06s/k+PIaif2r5rGc3j4Yhc01jSBfwPCXDLYZwp/yxralI37Df1mwmuzcCsen/E/ITKcTEvc1PQmQ==", + "dependencies": { + "invariant": "^2.2.0" + } + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==", + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==" + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dependencies": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ip": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", + "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dependencies": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dependencies": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "dependencies": { + "html-escaper": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dependencies": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dependencies": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-changed-files/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-changed-files/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-changed-files/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dependencies": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-cli/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-cli/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-cli/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-cli/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-cli/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-cli/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-cli/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-config/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-config/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-config/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-config/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-config/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-config/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-config/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-config/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-config/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-config/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/jest-config/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-config/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-config/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dependencies": { + "detect-newline": "^2.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dependencies": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-each/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-each/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-each/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-each/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-each/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-each/node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-each/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-each/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/jest-each/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-each/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dependencies": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom-fourteen": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-1.0.1.tgz", + "integrity": "sha512-DojMX1sY+at5Ep+O9yME34CdidZnO3/zfPh8UW+918C5fIZET5vCjfkegixmsi7AtdYfkr4bPlIzmWnlvQkP7Q==", + "dependencies": { + "@jest/environment": "^24.3.0", + "@jest/fake-timers": "^24.3.0", + "@jest/types": "^24.3.0", + "jest-mock": "^24.0.0", + "jest-util": "^24.0.0", + "jsdom": "^14.1.0" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "dependencies": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/jest-environment-jsdom-fourteen/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-jsdom/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-environment-jsdom/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-environment-jsdom/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dependencies": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-node/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-environment-node/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-node/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-node/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-node/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-environment-node/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-environment-node/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-environment-node/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-node/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-environment-node/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-environment-node/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dependencies": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 6" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/jest-haste-map/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-haste-map/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-haste-map/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-haste-map/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-haste-map/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-haste-map/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-haste-map/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-haste-map/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-haste-map/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/jest-haste-map/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-haste-map/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-haste-map/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-haste-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-haste-map/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-jasmine2/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "node_modules/jest-jasmine2/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-jasmine2/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-jasmine2/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-jasmine2/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-jasmine2/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-jasmine2/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-jasmine2/node_modules/diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-jasmine2/node_modules/expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-jasmine2/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dependencies": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dependencies": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-jasmine2/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/jest-jasmine2/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-jasmine2/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-jasmine2/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-jasmine2/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dependencies": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-leak-detector/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-leak-detector/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-leak-detector/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-leak-detector/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-leak-detector/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-leak-detector/node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dependencies": { + "@jest/types": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-mock/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-mock/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-mock/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dependencies": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dependencies": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-resolve/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-resolve/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-resolve/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-resolve/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dependencies": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runner/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runner/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-runner/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "node_modules/jest-runner/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runner/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-runner/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-runner/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runner/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runner/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runner/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runner/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dependencies": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runtime/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runtime/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-runtime/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "node_modules/jest-runtime/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runtime/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-runtime/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-runtime/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runtime/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runtime/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-runtime/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-runtime/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runtime/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-runtime/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dependencies": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-snapshot/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "node_modules/jest-snapshot/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-snapshot/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-snapshot/node_modules/expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-snapshot/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dependencies": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dependencies": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/jest-snapshot/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-snapshot/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-snapshot/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dependencies": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-validate/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-validate/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-validate/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-validate/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-validate/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watch-typeahead": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.4.2.tgz", + "integrity": "sha512-f7VpLebTdaXs81rg/oj4Vg/ObZy2QtGzAmGLNsqUS5G5KtSN68tFcIsbvNODfNyQxU78g7D8x77o3bgfBTR+2Q==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.1", + "jest-regex-util": "^24.9.0", + "jest-watcher": "^24.3.0", + "slash": "^3.0.0", + "string-length": "^3.1.0", + "strip-ansi": "^5.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watch-typeahead/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watch-typeahead/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-watch-typeahead/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-watch-typeahead/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", + "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", + "dependencies": { + "astral-regex": "^1.0.0", + "strip-ansi": "^5.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dependencies": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-watcher/node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-watcher/node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-watcher/node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watcher/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/jest-watcher/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-watcher/node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-watcher/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-watcher/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dependencies": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "node_modules/jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dependencies": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + } + }, + "node_modules/jsdom/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", + "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", + "dependencies": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json-stable-stringify/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonwebtoken/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "dependencies": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/just-curry-it": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.2.1.tgz", + "integrity": "sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/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" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/lamejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/lamejs/-/lamejs-1.2.1.tgz", + "integrity": "sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==", + "dependencies": { + "use-strict": "1.0.1" + } + }, + "node_modules/last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "dependencies": { + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "deprecated": "use String.prototype.padStart()" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dependencies": { + "leven": "^3.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", + "dependencies": { + "find-cache-dir": "^0.1.1", + "mkdirp": "^0.5.1" + } + }, + "node_modules/loader-fs-cache/node_modules/find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha512-Z9XSBoNE7xQiV6MSgPuCfyMokH2K7JdpRkOYE1+mu3d4BFJtx3GW+f6Bo4q8IX6rlf5MYbLBKW0pjl2cWdkm2A==", + "dependencies": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache/node_modules/pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha512-c6pv3OE78mcZ92ckebVDqg0aWSoKhOTbwCV6qbCWMk546mAL9pZln0+QsN/yQ7fkucd4+yJPLrCBXNt8Ruk+Eg==", + "dependencies": { + "find-up": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-to-jsx": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.4.7.tgz", + "integrity": "sha512-0+ls1IQZdU6cwM1yu0ZjjiVWYtkbExSyUIFU2ZeDIFuZM1W42Mh4OlJ4nb4apX4H8smxDHRdFaoIVJGwfv5hkg==", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, + "node_modules/material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, + "node_modules/material-ui-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/material-ui-color/-/material-ui-color-1.2.0.tgz", + "integrity": "sha512-bD2Rww+hakJxD2/19uxc280Vh292DnRStLke2LDFavVtGd5fzOz09zIrHO3ZHlMkJFsvwx6IwiB4/932ftv0sQ==", + "peerDependencies": { + "@material-ui/core": "^4.9.5", + "material-ui-popup-state": "^1.5.3", + "prop-types": "^15.7.2", + "react": "^16.0.0 || ^17.0.0", + "react-dom": "^16.0.0 || ^17.0.0" + } + }, + "node_modules/material-ui-popup-state": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/material-ui-popup-state/-/material-ui-popup-state-1.9.3.tgz", + "integrity": "sha512-+Ete5Tzw5rXlYfmqptOS8kBUH8vnK5OJsd6IQ7SHtLjU0PsvsmM73M/k8ot0xkX4RmPGuNRsFbK3mlCe/ClQuw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@material-ui/types": "^6.0.1", + "classnames": "^2.2.6", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@material-ui/core": "^4.0.0 || ^5.0.0-beta", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/material-ui-popup-state/node_modules/@material-ui/types": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-6.0.2.tgz", + "integrity": "sha512-/XUca4wUb9pWimLLdM1PE8KS8rTbDEGohSGkGtk3WST7lm23m+8RYv9uOmrvOg/VSsl4bMiOv4t2/LCb+RLbTg==", + "peer": true, + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/merge-deep": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz", + "integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==", + "dependencies": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mic-recorder-to-mp3": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/mic-recorder-to-mp3/-/mic-recorder-to-mp3-2.2.2.tgz", + "integrity": "sha512-xDkOaHbojW3bdKOGn9CI5dT+Mc0RrfczsX/Y1zGJp3FUB4zei5ZKFnNm7Nguc9v910wkd7T3csnCTq5EtCF3Zw==", + "dependencies": { + "lamejs": "^1.2.0" + }, + "peerDependencies": { + "webrtc-adapter": ">=4.1.1" + } + }, + "node_modules/microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dependencies": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.4.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==", + "dependencies": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-object/node_modules/for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/move-concurrently/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/nan": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "optional": true + }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "node_modules/node-libs-browser/node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/node-notifier": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", + "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==", + "dependencies": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-url/node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url/node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==" + }, + "node_modules/nwsapi": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", + "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-path": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", + "integrity": "sha512-ICbQN+aw/eAASDtaC7+SJXSAruz7fvvNjxMFfS3mTdvZaaiuuw81XXYu+9CSJeUVrS3YpRhTr862YGywMQUOWg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", + "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", + "dependencies": { + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "gopd": "^1.0.1", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimize-css-assets-webpack-plugin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", + "integrity": "sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==", + "dependencies": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^3.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==" + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA==", + "dependencies": { + "p-reduce": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==" + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "node_modules/pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "dependencies": { + "ts-pnp": "^1.1.6" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, + "node_modules/portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz", + "integrity": "sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig==", + "dependencies": { + "postcss": "^7" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "browserslist": "^4" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "dependencies": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "dependencies": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-selectors/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "dependencies": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz", + "integrity": "sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA==", + "dependencies": { + "postcss": "^7.0.0" + } + }, + "node_modules/postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-load-config/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dependencies": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dependencies": { + "postcss": "^7.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dependencies": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "node_modules/postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-8.0.1.tgz", + "integrity": "sha512-rt9JMS/m9FHIRroDDBGSMsyW1c0fkvOJPy62ggxSHUldJO7B195TqFMqIf+lY5ezpDcYOV4j86aUp3/XbxzCCQ==", + "dependencies": { + "@csstools/normalize.css": "^10.1.0", + "browserslist": "^4.6.2", + "postcss": "^7.0.17", + "postcss-browser-comments": "^3.0.0", + "sanitize.css": "^10.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "dependencies": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-safe-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", + "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "dependencies": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-selector-not": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", + "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", + "dependencies": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "peer": true + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/prop-types-extra/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, + "node_modules/qrcode.react": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.1.tgz", + "integrity": "sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg==", + "dependencies": { + "loose-envify": "^1.4.0", + "prop-types": "^15.6.0", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "^15.5.3 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-lDVjxQQFoCG1jcrP06LNo2lbWp4QTShEXnhActFBwYuHprllQV6VUpwreApsYqCgD+N1mHoqJ/BI/4eV4R2GYg==" + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill/node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomstring": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.3.0.tgz", + "integrity": "sha512-gY7aQ4i1BgwZ8I1Op4YseITAyiDiajeZOPQUbIq9TPGPhUm5FX59izIaOpmKbME1nmnEiABf28d9K2VSii6BBg==", + "dependencies": { + "randombytes": "2.0.3" + }, + "bin": { + "randomstring": "bin/randomstring" + }, + "engines": { + "node": "*" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-app-polyfill": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz", + "integrity": "sha512-OfBnObtnGgLGfweORmdZbyEz+3dgVePQBb3zipiaDsMHV1NpWm0rDFYIVXFV/AK+x4VIIfWHhrdMIeoTLyRr2g==", + "dependencies": { + "core-js": "^3.5.0", + "object-assign": "^4.1.1", + "promise": "^8.0.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.3", + "whatwg-fetch": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/react-big-calendar": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.11.6.tgz", + "integrity": "sha512-F8TpYul2pVc6a1TmFGFufwCpRlY55YJgx+JCDjhHnZwD2lwpKiAaDlnOjBPsm3OiijVQBHWUKrZwbli3V4lVyw==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "clsx": "^1.2.1", + "date-arithmetic": "^4.1.0", + "dayjs": "^1.11.7", + "dom-helpers": "^5.2.1", + "globalize": "^0.1.1", + "invariant": "^2.2.4", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "luxon": "^3.2.1", + "memoize-one": "^6.0.0", + "moment": "^2.29.4", + "moment-timezone": "^0.5.40", + "prop-types": "^15.8.1", + "react-overlays": "^5.2.1", + "uncontrollable": "^7.2.1" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17 || ^18", + "react-dom": "^16.14.0 || ^17 || ^18" + } + }, + "node_modules/react-bootstrap": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.2.tgz", + "integrity": "sha512-UvB7mRqQjivdZNxJNEA2yOQRB7L9N43nBnKc33K47+cH90/ujmnMwatTCwQLu83gLhrzAl8fsa6Lqig/KLghaA==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.8", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-chartjs-2": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz", + "integrity": "sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA==", + "peerDependencies": { + "chart.js": "^3.5.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-color": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", + "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "dependencies": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "dependencies": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^15.3.0 || 16 || 17 || 18" + } + }, + "node_modules/react-csv": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz", + "integrity": "sha512-RG5hOcZKZFigIGE8LxIEV/OgS1vigFQT4EkaHeKgyuCbUAu9Nbd/1RYq++bJcJJ9VOqO/n9TZRADsXNDR4VEpw==" + }, + "node_modules/react-currency-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/react-currency-format/-/react-currency-format-1.1.0.tgz", + "integrity": "sha512-WWrEOIp/3GbDSk1wlhFXaBc7IHGT3IwL306DHbGP3GVr4YFa0iS5hHPbKjHa0haruGL4Ly+WG4/5jBHpUtgqZg==", + "dependencies": { + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^0.14 || ^15.0.0-rc || ^15.0.0 || ^16.0.0-rc || ^16.0.0 || ^17.0.0", + "react-dom": "^0.14 || ^15.0.0-rc || ^15.0.0 || ^16.0.0-rc || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/react-dev-utils": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz", + "integrity": "sha512-XxTbgJnYZmxuPtY3y/UV0D8/65NKkmaia4rXzViknVnZeVlklSh8u6TnaEYPfAi/Gh1TP4mEOXHI6jQOPbeakQ==", + "dependencies": { + "@babel/code-frame": "7.8.3", + "address": "1.1.2", + "browserslist": "4.10.0", + "chalk": "2.4.2", + "cross-spawn": "7.0.1", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "2.0.0", + "filesize": "6.0.1", + "find-up": "4.1.0", + "fork-ts-checker-webpack-plugin": "3.1.1", + "global-modules": "2.0.0", + "globby": "8.0.2", + "gzip-size": "5.1.1", + "immer": "1.10.0", + "inquirer": "7.0.4", + "is-root": "2.1.0", + "loader-utils": "1.2.3", + "open": "^7.0.2", + "pkg-up": "3.1.0", + "react-error-overlay": "^6.0.7", + "recursive-readdir": "2.2.2", + "shell-quote": "1.7.2", + "strip-ansi": "6.0.0", + "text-table": "0.2.0" + }, + "engines": { + "node": ">=8.10" + } + }, + "node_modules/react-dev-utils/node_modules/@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dependencies": { + "@babel/highlight": "^7.8.3" + } + }, + "node_modules/react-dev-utils/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/browserslist": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.10.0.tgz", + "integrity": "sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA==", + "dependencies": { + "caniuse-lite": "^1.0.30001035", + "electron-to-chromium": "^1.3.378", + "node-releases": "^1.1.52", + "pkg-up": "^3.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + }, + "node_modules/react-dev-utils/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/react-dev-utils/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" + }, + "node_modules/react-dev-utils/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/react-dev-utils/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/react-dev-utils/node_modules/cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/react-dev-utils/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react-dev-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/inquirer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", + "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-dev-utils/node_modules/inquirer/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-dev-utils/node_modules/inquirer/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/react-dev-utils/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/react-dev-utils/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/node-releases": { + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==" + }, + "node_modules/react-dev-utils/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "node_modules/react-icons": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-input-mask": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-input-mask/-/react-input-mask-2.0.4.tgz", + "integrity": "sha512-1hwzMr/aO9tXfiroiVCx5EtKohKwLk/NT8QlJXHQ4N+yJJFyUuMT+zfTpLBwX/lK3PkuMlievIffncpMZ3HGRQ==", + "dependencies": { + "invariant": "^2.2.4", + "warning": "^4.0.2" + }, + "peerDependencies": { + "react": ">=0.14.0", + "react-dom": ">=0.14.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal-image": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-modal-image/-/react-modal-image-2.6.0.tgz", + "integrity": "sha512-NNc1xPKzFAn0VsNMdJ8NXt6c54aL/z0fcoYmw9qn4SBUONdGl+8LOQ0sTfo0wtdzcjLiby/ncloHcAL+UI+wIA==" + }, + "node_modules/react-number-format": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-4.9.4.tgz", + "integrity": "sha512-Gq20Z3ugqPLFgeaidnx5on9cNpbQZntPN3QgNAL/WJrNNlQnNznY0LCx7g8xtssmRBw0/hw+SCqw6zAcajooiA==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-overlays": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz", + "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==", + "dependencies": { + "@babel/runtime": "^7.13.8", + "@popperjs/core": "^2.11.6", + "@restart/hooks": "^0.4.7", + "@types/warning": "^3.0.0", + "dom-helpers": "^5.2.0", + "prop-types": "^15.7.2", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-popopo": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/react-popopo/-/react-popopo-2.1.9.tgz", + "integrity": "sha512-zXOpcLSpaLZmBxhdtenJzQPLjY81XknVS/tXH4Kv5BBrnYIUPHvVdGmS7+o9s7DjCzzdK7AdVwtG+FVSO0cZ8g==", + "dependencies": { + "classnames": ">= 2.0", + "prop-types": "^15.7.2", + "react": ">= 16.3", + "react-dom": ">= 16.3", + "styled-components": ">= 4.0" + }, + "peerDependencies": { + "classnames": ">= 2.0", + "react": ">= 16.3", + "react-dom": ">= 16.3", + "styled-components": ">= 4.0" + } + }, + "node_modules/react-qr-code": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz", + "integrity": "sha512-k+pzP5CKLEGBRwZsDPp98/CAJeXlsYRHM2iZn1Sd5Th/HnKhIZCSg27PXO58zk8z02RaEryg+60xa4vyywMJwg==", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x", + "react-native-svg": "*" + }, + "peerDependenciesMeta": { + "react-native-svg": { + "optional": true + } + } + }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-scripts": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz", + "integrity": "sha512-oSnoWmii/iKdeQiwaO6map1lUaZLmG0xIUyb/HwCVFLT7gNbj8JZ9RmpvMCZ4fB98ZUMRfNmp/ft8uy/xD1RLA==", + "dependencies": { + "@babel/core": "7.9.0", + "@svgr/webpack": "4.3.3", + "@typescript-eslint/eslint-plugin": "^2.10.0", + "@typescript-eslint/parser": "^2.10.0", + "babel-eslint": "10.1.0", + "babel-jest": "^24.9.0", + "babel-loader": "8.1.0", + "babel-plugin-named-asset-import": "^0.3.6", + "babel-preset-react-app": "^9.1.2", + "camelcase": "^5.3.1", + "case-sensitive-paths-webpack-plugin": "2.3.0", + "css-loader": "3.4.2", + "dotenv": "8.2.0", + "dotenv-expand": "5.1.0", + "eslint": "^6.6.0", + "eslint-config-react-app": "^5.2.1", + "eslint-loader": "3.0.3", + "eslint-plugin-flowtype": "4.6.0", + "eslint-plugin-import": "2.20.1", + "eslint-plugin-jsx-a11y": "6.2.3", + "eslint-plugin-react": "7.19.0", + "eslint-plugin-react-hooks": "^1.6.1", + "file-loader": "4.3.0", + "fs-extra": "^8.1.0", + "html-webpack-plugin": "4.0.0-beta.11", + "identity-obj-proxy": "3.0.0", + "jest": "24.9.0", + "jest-environment-jsdom-fourteen": "1.0.1", + "jest-resolve": "24.9.0", + "jest-watch-typeahead": "0.4.2", + "mini-css-extract-plugin": "0.9.0", + "optimize-css-assets-webpack-plugin": "5.0.3", + "pnp-webpack-plugin": "1.6.4", + "postcss-flexbugs-fixes": "4.1.0", + "postcss-loader": "3.0.0", + "postcss-normalize": "8.0.1", + "postcss-preset-env": "6.7.0", + "postcss-safe-parser": "4.0.1", + "react-app-polyfill": "^1.0.6", + "react-dev-utils": "^10.2.1", + "resolve": "1.15.0", + "resolve-url-loader": "3.1.1", + "sass-loader": "8.0.2", + "semver": "6.3.0", + "style-loader": "0.23.1", + "terser-webpack-plugin": "2.3.8", + "ts-pnp": "1.1.6", + "url-loader": "2.3.0", + "webpack": "4.42.0", + "webpack-dev-server": "3.11.0", + "webpack-manifest-plugin": "2.2.0", + "workbox-webpack-plugin": "4.3.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=8.10" + }, + "optionalDependencies": { + "fsevents": "2.1.2" + }, + "peerDependencies": { + "typescript": "^3.2.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/react-scripts/node_modules/@babel/core/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/react-scripts/node_modules/resolve": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-text-mask": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-text-mask/-/react-text-mask-5.5.0.tgz", + "integrity": "sha512-SLJlJQxa0uonMXsnXRpv5abIepGmHz77ylQcra0GNd7Jtk4Wj2Mtp85uGQHv1avba2uI8ZvRpIEQPpJKsqRGYw==", + "dependencies": { + "prop-types": "^15.5.6" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-toastify": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.0.tgz", + "integrity": "sha512-gmxAFTKV0VVN7s5UEcZdC+bBR1EQtsqXgHJWQgB0G5J8m3Mdxz1Pacm7BJ1nTHkMMNfupruKCu7wUwAq46Z1jg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-trello": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/react-trello/-/react-trello-2.2.11.tgz", + "integrity": "sha512-Zm7qrPAcFTysUXK6wpa1KhTt7fTadIZgNGYj+blDWGnppFYW7GMi1p+S6J8P9quhzKnIJTSZXVzHA1f4sREIVQ==", + "dependencies": { + "autosize": "^4.0.2", + "classnames": "^2.2.6", + "immutability-helper": "^2.8.1", + "lodash": "^4.17.11", + "prop-types": "^15.7.2", + "react-popopo": "^2.1.9", + "react-redux": "^5.0.7", + "redux": "^4.0.0", + "redux-actions": "^2.6.1", + "redux-logger": "^3.0.6", + "trello-smooth-dnd": "1.0.0", + "uuid": "^3.3.2" + }, + "peerDependencies": { + "lodash": ">= 4.17.11", + "react": "*", + "react-dom": "*", + "react-redux": ">= 5.0.7", + "redux": ">= 4.0.0", + "redux-actions": ">= 2.6.1", + "redux-logger": ">= 3.0.6", + "styled-components": ">= 4.0.3" + } + }, + "node_modules/react-trello/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-trello/node_modules/react-redux": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz", + "integrity": "sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.1.0", + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0-0 || ^16.0.0-0", + "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/react-trello/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "dependencies": { + "lodash": "^4.0.1" + } + }, + "node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dependencies": { + "util.promisify": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/recharts": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.6.tgz", + "integrity": "sha512-D+7j9WI+D0NHauah3fKHuNNcRK8bOypPW7os1DERinogGBGaHI7i6tQKJ0aUF3JXyBZ63dyfKIW2WTOPJDxJ8w==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "dependencies": { + "minimatch": "3.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reduce-reducers": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/reduce-reducers/-/reduce-reducers-0.4.3.tgz", + "integrity": "sha512-+CNMnI8QhgVMtAt54uQs3kUxC3Sybpa7Y63HR14uGLgI9/QR5ggHvpxwhGGe3wmx5V91YwqQIblN9k5lspAmGw==" + }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-actions": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/redux-actions/-/redux-actions-2.6.5.tgz", + "integrity": "sha512-pFhEcWFTYNk7DhQgxMGnbsB1H2glqhQJRQrtPb96kD3hWiZRzXHwwmFPswg6V2MjraXRXWNmuP9P84tvdLAJmw==", + "dependencies": { + "invariant": "^2.2.4", + "just-curry-it": "^3.1.0", + "loose-envify": "^1.4.0", + "reduce-reducers": "^0.4.3", + "to-camel-case": "^1.0.0" + } + }, + "node_modules/redux-logger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", + "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==", + "dependencies": { + "deep-diff": "^0.3.5" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-parser": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.10.tgz", + "integrity": "sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" + }, + "node_modules/renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "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" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated" + }, + "node_modules/resolve-url-loader": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz", + "integrity": "sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ==", + "dependencies": { + "adjust-sourcemap-loader": "2.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.21", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/resolve-url-loader/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/resolve-url-loader/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/resolve-url-loader/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-url-loader/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/resolve-url-loader/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/resolve-url-loader/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-url-loader/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha512-eEjL8FdkdsxApd0yWVZgBGzfCQiT8yqSc2H1p4jpZpQdtz7ohETiDMoje5PlM8I9WgkqkreVxFUKYOiJdVWDXw==", + "dependencies": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + } + }, + "node_modules/rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha512-W6V2fix7nCLUYX1v6eGPrBOZlc03/faqzP4sUxMAJMBMOPYhfV/RyLegTufn5gJKaOITyi+gvf0LXDZ9NzkHnQ==" + }, + "node_modules/rework/node_modules/convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg==" + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==" + }, + "node_modules/rifm": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rifm/-/rifm-0.7.0.tgz", + "integrity": "sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ==", + "dependencies": { + "@babel/runtime": "^7.3.1" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "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" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sanitize.css": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz", + "integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg==" + }, + "node_modules/sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "dependencies": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass-loader/node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-loader/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sass-loader/node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + }, + "node_modules/saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dependencies": { + "xmlchars": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==", + "peer": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serialize-javascript/node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==", + "dependencies": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallow-clone/node_modules/kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==", + "dependencies": { + "is-buffer": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallow-clone/node_modules/lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dependencies": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "dependencies": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + } + }, + "node_modules/sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "dependencies": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/sockjs-client/node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "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" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "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" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.1.tgz", + "integrity": "sha512-w+daCzXN89PseTL99MkA+fxJEcU3wfaE/ah0i0lnOlpG1CYLJ2ZjzEry68YBKfLs4JfoTShrTEsJkAZuNZ/stw==", + "dependencies": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha512-Qka42GGrS8Mm3SZ+7cH8UXiIWI867/b/Z/feQSpQx/rbfB8UGknGEZVaUQMOUVj+soY6NpWAxily63HI1OckVQ==", + "dependencies": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", + "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", + "dependencies": { + "babel-extract-comments": "^1.0.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "dependencies": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/style-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/styled-components": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", + "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/styled-components/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/styled-components/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/svgo/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/svgo/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/svgo/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dependencies": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", + "integrity": "sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w==", + "dependencies": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.6.12", + "webpack-sources": "^1.4.3" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", + "dependencies": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 8.3" + } + }, + "node_modules/terser-webpack-plugin/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dependencies": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/test-exclude/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/test-exclude/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/test-exclude/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/test-exclude/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/test-exclude/node_modules/read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dependencies": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-mask-addons": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/text-mask-addons/-/text-mask-addons-3.8.0.tgz", + "integrity": "sha512-VSZSdc/tKn4zGxgpJ+uNBzoW1t472AoAFIlbw1K7hSNXz0DfSBYDJNRxLqgxOfWw1BY2z6DQpm7g0sYZn5qLpg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==" + }, + "node_modules/to-camel-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", + "integrity": "sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==", + "dependencies": { + "to-space-case": "^1.0.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==" + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", + "dependencies": { + "to-no-case": "^1.0.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/trello-smooth-dnd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trello-smooth-dnd/-/trello-smooth-dnd-1.0.0.tgz", + "integrity": "sha512-KgYEwmxX08Dl4OmioEv24LSnlNp9jNv8lwTQlUMbMm6B+VuwyQuuuoyu4wlsRweiMCCC6sZXpCCGkmAni/vCaQ==" + }, + "node_modules/ts-pnp": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz", + "integrity": "sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ==", + "engines": { + "node": ">=6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated" + }, + "node_modules/url": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", + "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.11.2" + } + }, + "node_modules/url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dependencies": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "node_modules/url/node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/use-debounce": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-7.0.1.tgz", + "integrity": "sha512-fOrzIw2wstbAJuv8PC9Vg4XgwyTLEOdq4y/Z3IhVl8DAE4svRcgyEUvrEXu+BMNgMoc3YND6qLT61kkgEKXh7Q==", + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/use-sound": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/use-sound/-/use-sound-2.2.0.tgz", + "integrity": "sha512-Sa524UuX1piuCHomP/STSjRVhZK6FaXC9qOjfwms2uWp9EErUl+g5ioju/ECfThtexNgcceNunuUtrEr7707Uw==", + "dependencies": { + "howler": "^2.1.3" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/use-strict": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/use-strict/-/use-strict-1.0.1.tgz", + "integrity": "sha512-IeiWvvEXfW5ltKVMkxq6FvNf2LojMKvB2OCeja6+ct24S1XOmQw2dGr2JyndwACWAGJva9B7yPHwAmeA9QCqAQ==" + }, + "node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dependencies": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/vest-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/vest-utils/-/vest-utils-0.0.5.tgz", + "integrity": "sha512-uA5NW62n/EdmIOiEHZUQesUSzRnzJgh4R36hZ1omdil0wwYgv7Qobb5omnl1Z03ymopadDtbGXNRxnNFIlwTjQ==" + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dependencies": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "optional": true, + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/watchpack-chokidar2/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "optional": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "optional": true, + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "optional": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "optional": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/webpack": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz", + "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==", + "dependencies": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dependencies": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "dependencies": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 6.11.5" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dependencies": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-log/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", + "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", + "dependencies": { + "fs-extra": "^7.0.0", + "lodash": ">=3.5 <5", + "object.entries": "^1.1.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.11.5" + }, + "peerDependencies": { + "webpack": "2 || 3 || 4" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dependencies": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/webpack/node_modules/terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dependencies": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/webpack/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webrtc-adapter": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.1.tgz", + "integrity": "sha512-1AQO+d4ElfVSXyzNVTOewgGT/tAomwwztX/6e3totvyyzXPvXIIuUUjAmyZGbKBKbZOXauuJooZm3g6IuFuiNQ==", + "peer": true, + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha512-oBx6ZM1Gs5q2jwZuSN/Qxyy/fbgomV8+vqsmipaPKB/74hjHlKuM07jNmRhn4qa2AdUwsgxrltq+gaPsHgcl0Q==", + "dependencies": { + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-background-sync": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz", + "integrity": "sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz", + "integrity": "sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-build": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-4.3.1.tgz", + "integrity": "sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw==", + "dependencies": { + "@babel/runtime": "^7.3.4", + "@hapi/joi": "^15.0.0", + "common-tags": "^1.8.0", + "fs-extra": "^4.0.2", + "glob": "^7.1.3", + "lodash.template": "^4.4.0", + "pretty-bytes": "^5.1.0", + "stringify-object": "^3.3.0", + "strip-comments": "^1.0.2", + "workbox-background-sync": "^4.3.1", + "workbox-broadcast-update": "^4.3.1", + "workbox-cacheable-response": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-expiration": "^4.3.1", + "workbox-google-analytics": "^4.3.1", + "workbox-navigation-preload": "^4.3.1", + "workbox-precaching": "^4.3.1", + "workbox-range-requests": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1", + "workbox-streams": "^4.3.1", + "workbox-sw": "^4.3.1", + "workbox-window": "^4.3.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz", + "integrity": "sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-4.3.1.tgz", + "integrity": "sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg==" + }, + "node_modules/workbox-expiration": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-4.3.1.tgz", + "integrity": "sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-google-analytics": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz", + "integrity": "sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg==", + "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", + "dependencies": { + "workbox-background-sync": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz", + "integrity": "sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-precaching": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-4.3.1.tgz", + "integrity": "sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-range-requests": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz", + "integrity": "sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-routing": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-4.3.1.tgz", + "integrity": "sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-strategies": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-4.3.1.tgz", + "integrity": "sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-streams": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-4.3.1.tgz", + "integrity": "sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/workbox-sw": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-4.3.1.tgz", + "integrity": "sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w==" + }, + "node_modules/workbox-webpack-plugin": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-4.3.1.tgz", + "integrity": "sha512-gJ9jd8Mb8wHLbRz9ZvGN57IAmknOipD3W4XNE/Lk/4lqs5Htw4WOQgakQy/o/4CoXQlMCYldaqUg+EJ35l9MEQ==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "json-stable-stringify": "^1.0.1", + "workbox-build": "^4.3.1" + }, + "engines": { + "node": ">=4.0.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/workbox-window": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-4.3.1.tgz", + "integrity": "sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg==", + "dependencies": { + "workbox-core": "^4.3.1" + } + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "dependencies": { + "microevent.ts": "~0.1.1" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dependencies": { + "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/ws": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xregexp": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz", + "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==", + "dependencies": { + "@babel/runtime-corejs3": "^7.12.1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..1f56d7f --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,90 @@ +{ + "name": "frontend", + "version": "6.0.0", + "versionSystem": "6.0.0", + "nomeEmpresa": "Atendechat", + "private": true, + "dependencies": { + "@date-io/date-fns": "^2.14.0", + "@emotion/styled": "^11.10.6", + "@material-ui/core": "4.12.3", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.56", + "@material-ui/pickers": "^3.3.10", + "@material-ui/styles": "^4.11.5", + "@mui/material": "^5.10.13", + "@mui/x-date-pickers": "^6.0.1", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.0.4", + "@testing-library/user-event": "^12.1.7", + "axios": "^0.21.1", + "bootstrap": "^5.2.3", + "chart.js": "^3.9.1", + "chartjs-plugin-datalabels": "^2.1.0", + "context": "^4.0.0", + "date-fns": "^2.16.1", + "emoji-mart": "^3.0.0", + "formik": "^2.2.0", + "formik-material-ui": "^3.0.1", + "gn-api-sdk-node": "^3.0.2", + "i18next": "^19.8.2", + "i18next-browser-languagedetector": "^6.0.1", + "jsonwebtoken": "^9.0.2", + "markdown-to-jsx": "^7.1.0", + "material-ui-color": "^1.2.0", + "mic-recorder-to-mp3": "^2.2.2", + "moment": "^2.29.1", + "qrcode.react": "^1.0.0", + "query-string": "^7.0.0", + "react": "^17.0.2", + "react-big-calendar": "^1.8.7", + "react-bootstrap": "^2.7.0", + "react-chartjs-2": "^4.3.1", + "react-color": "^2.19.3", + "react-copy-to-clipboard": "^5.1.0", + "react-csv": "^2.2.2", + "react-currency-format": "^1.1.0", + "react-dom": "^17.0.2", + "react-icons": "^4.4.0", + "react-input-mask": "^2.0.4", + "react-modal-image": "^2.5.0", + "react-number-format": "^4.6.4", + "react-qr-code": "^2.0.7", + "react-query": "^3.39.3", + "react-router-dom": "^5.2.0", + "react-scripts": "3.4.3", + "react-text-mask": "^5.5.0", + "react-toastify": "9.0.0", + "react-trello": "^2.2.11", + "recharts": "^2.0.2", + "socket.io-client": "^4.7.5", + "styled-components": "^5.3.5", + "text-mask-addons": "^3.8.0", + "use-debounce": "^7.0.0", + "use-sound": "^2.0.1", + "uuid": "^8.3.2", + "yup": "^0.32.8" + }, + "scripts": { + "start": "NODE_OPTIONS=--openssl-legacy-provider react-scripts start", + "build": "NODE_OPTIONS=--openssl-legacy-provider GENERATE_SOURCEMAP=false react-scripts build", + "builddev": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/frontend/public/android-chrome-192x192.jpg b/frontend/public/android-chrome-192x192.jpg new file mode 100644 index 0000000..3b81d05 Binary files /dev/null and b/frontend/public/android-chrome-192x192.jpg differ diff --git a/frontend/public/apple-touch-icon.jpg b/frontend/public/apple-touch-icon.jpg new file mode 100644 index 0000000..3b81d05 Binary files /dev/null and b/frontend/public/apple-touch-icon.jpg differ diff --git a/frontend/public/favicon-16x16.jpg b/frontend/public/favicon-16x16.jpg new file mode 100644 index 0000000..3b81d05 Binary files /dev/null and b/frontend/public/favicon-16x16.jpg differ diff --git a/frontend/public/favicon-32x32.jpg b/frontend/public/favicon-32x32.jpg new file mode 100644 index 0000000..3b81d05 Binary files /dev/null and b/frontend/public/favicon-32x32.jpg differ diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000..a96f608 Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/public/index.html b/frontend/public/index.html new file mode 100644 index 0000000..ad3fa17 --- /dev/null +++ b/frontend/public/index.html @@ -0,0 +1,28 @@ + + + + Atendechat + + + + + + + + + + + + +
+ + diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json new file mode 100644 index 0000000..fa8141d --- /dev/null +++ b/frontend/public/manifest.json @@ -0,0 +1,20 @@ +{ + "short_name": "Atendechat", + "name": "Atendechat", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/frontend/public/mstile-150x150.jpg b/frontend/public/mstile-150x150.jpg new file mode 100644 index 0000000..3b81d05 Binary files /dev/null and b/frontend/public/mstile-150x150.jpg differ diff --git a/frontend/server.js b/frontend/server.js new file mode 100644 index 0000000..838a462 --- /dev/null +++ b/frontend/server.js @@ -0,0 +1,10 @@ +//simple express server to run frontend production build; +const express = require("express"); +const path = require("path"); +const app = express(); +app.use(express.static(path.join(__dirname, "build"))); +app.get("/*", function (req, res) { + res.sendFile(path.join(__dirname, "build", "index.html")); +}); +app.listen(3000); + diff --git a/frontend/src/App.js b/frontend/src/App.js new file mode 100644 index 0000000..fb3adc2 --- /dev/null +++ b/frontend/src/App.js @@ -0,0 +1,116 @@ +import React, { useState, useEffect } from "react"; + +import "react-toastify/dist/ReactToastify.css"; +import { QueryClient, QueryClientProvider } from "react-query"; + +import { ptBR } from "@material-ui/core/locale"; +import { createTheme, ThemeProvider } from "@material-ui/core/styles"; +import { useMediaQuery } from "@material-ui/core"; +import ColorModeContext from "./layout/themeContext"; +import { SocketContext, SocketManager } from './context/Socket/SocketContext'; + +import Routes from "./routes"; + +const queryClient = new QueryClient(); + +const App = () => { + const [locale, setLocale] = useState(); + + const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); + const preferredTheme = window.localStorage.getItem("preferredTheme"); + const [mode, setMode] = useState(preferredTheme ? preferredTheme : prefersDarkMode ? "dark" : "light"); + + const colorMode = React.useMemo( + () => ({ + toggleColorMode: () => { + setMode((prevMode) => (prevMode === "light" ? "dark" : "light")); + }, + }), + [] + ); + + const theme = createTheme( + { + scrollbarStyles: { + "&::-webkit-scrollbar": { + width: '8px', + height: '8px', + }, + "&::-webkit-scrollbar-thumb": { + boxShadow: 'inset 0 0 6px rgba(0, 0, 0, 0.3)', + backgroundColor: "#682EE3", + }, + }, + scrollbarStylesSoft: { + "&::-webkit-scrollbar": { + width: "8px", + }, + "&::-webkit-scrollbar-thumb": { + backgroundColor: mode === "light" ? "#F3F3F3" : "#333333", + }, + }, + palette: { + type: mode, + primary: { main: mode === "light" ? "#682EE3" : "#FFFFFF" }, + textPrimary: mode === "light" ? "#682EE3" : "#FFFFFF", + borderPrimary: mode === "light" ? "#682EE3" : "#FFFFFF", + dark: { main: mode === "light" ? "#333333" : "#F3F3F3" }, + light: { main: mode === "light" ? "#F3F3F3" : "#333333" }, + tabHeaderBackground: mode === "light" ? "#EEE" : "#666", + optionsBackground: mode === "light" ? "#fafafa" : "#333", + options: mode === "light" ? "#fafafa" : "#666", + fontecor: mode === "light" ? "#128c7e" : "#fff", + fancyBackground: mode === "light" ? "#fafafa" : "#333", + bordabox: mode === "light" ? "#eee" : "#333", + newmessagebox: mode === "light" ? "#eee" : "#333", + inputdigita: mode === "light" ? "#fff" : "#666", + contactdrawer: mode === "light" ? "#fff" : "#666", + announcements: mode === "light" ? "#ededed" : "#333", + login: mode === "light" ? "#fff" : "#1C1C1C", + announcementspopover: mode === "light" ? "#fff" : "#666", + chatlist: mode === "light" ? "#eee" : "#666", + boxlist: mode === "light" ? "#ededed" : "#666", + boxchatlist: mode === "light" ? "#ededed" : "#333", + total: mode === "light" ? "#fff" : "#222", + messageIcons: mode === "light" ? "grey" : "#F3F3F3", + inputBackground: mode === "light" ? "#FFFFFF" : "#333", + barraSuperior: mode === "light" ? "linear-gradient(to right, #682EE3, #682EE3 , #682EE3)" : "#666", + boxticket: mode === "light" ? "#EEE" : "#666", + campaigntab: mode === "light" ? "#ededed" : "#666", + mediainput: mode === "light" ? "#ededed" : "#1c1c1c", + }, + mode, + }, + locale + ); + + useEffect(() => { + const i18nlocale = localStorage.getItem("i18nextLng"); + const browserLocale = + i18nlocale.substring(0, 2) + i18nlocale.substring(3, 5); + + if (browserLocale === "ptBR") { + setLocale(ptBR); + } + }, []); + + useEffect(() => { + window.localStorage.setItem("preferredTheme", mode); + }, [mode]); + + + + return ( + + + + + + + + + + ); +}; + +export default App; diff --git a/frontend/src/assets/chat_notify.mp3 b/frontend/src/assets/chat_notify.mp3 new file mode 100644 index 0000000..1e5ab73 Binary files /dev/null and b/frontend/src/assets/chat_notify.mp3 differ diff --git a/frontend/src/assets/dialogflow.png b/frontend/src/assets/dialogflow.png new file mode 100644 index 0000000..2b765c5 Binary files /dev/null and b/frontend/src/assets/dialogflow.png differ diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png new file mode 100644 index 0000000..254ef9a Binary files /dev/null and b/frontend/src/assets/logo.png differ diff --git a/frontend/src/assets/n8n.png b/frontend/src/assets/n8n.png new file mode 100644 index 0000000..3acc578 Binary files /dev/null and b/frontend/src/assets/n8n.png differ diff --git a/frontend/src/assets/planilha.xlsx b/frontend/src/assets/planilha.xlsx new file mode 100644 index 0000000..2b6030f Binary files /dev/null and b/frontend/src/assets/planilha.xlsx differ diff --git a/frontend/src/assets/sound.mp3 b/frontend/src/assets/sound.mp3 new file mode 100644 index 0000000..bd770d8 Binary files /dev/null and b/frontend/src/assets/sound.mp3 differ diff --git a/frontend/src/assets/sound.ogg b/frontend/src/assets/sound.ogg new file mode 100644 index 0000000..5a7c0ab Binary files /dev/null and b/frontend/src/assets/sound.ogg differ diff --git a/frontend/src/assets/typebot.jpg b/frontend/src/assets/typebot.jpg new file mode 100644 index 0000000..99f8b28 Binary files /dev/null and b/frontend/src/assets/typebot.jpg differ diff --git a/frontend/src/assets/wa-background-dark.png b/frontend/src/assets/wa-background-dark.png new file mode 100644 index 0000000..892c3e0 Binary files /dev/null and b/frontend/src/assets/wa-background-dark.png differ diff --git a/frontend/src/assets/wa-background.png b/frontend/src/assets/wa-background.png new file mode 100644 index 0000000..b76c17b Binary files /dev/null and b/frontend/src/assets/wa-background.png differ diff --git a/frontend/src/assets/webhook.png b/frontend/src/assets/webhook.png new file mode 100644 index 0000000..ca83b4d Binary files /dev/null and b/frontend/src/assets/webhook.png differ diff --git a/frontend/src/components/AnnouncementModal/index.js b/frontend/src/components/AnnouncementModal/index.js new file mode 100644 index 0000000..49162cb --- /dev/null +++ b/frontend/src/components/AnnouncementModal/index.js @@ -0,0 +1,338 @@ +import React, { useState, useEffect, useRef } from "react"; + +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; +import { toast } from "react-toastify"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import AttachFileIcon from "@material-ui/icons/AttachFile"; +import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"; +import IconButton from "@material-ui/core/IconButton"; + +import { i18n } from "../../translate/i18n"; +import { head } from "lodash"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { + FormControl, + Grid, + InputLabel, + MenuItem, + Select, +} from "@material-ui/core"; +import ConfirmationModal from "../ConfirmationModal"; + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + flexWrap: "wrap", + }, + multFieldLine: { + display: "flex", + "& > *:not(:last-child)": { + marginRight: theme.spacing(1), + }, + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + colorAdorment: { + width: 20, + height: 20, + }, +})); + +const AnnouncementSchema = Yup.object().shape({ + title: Yup.string().required("Obrigatório"), + text: Yup.string().required("Obrigatório"), +}); + +const AnnouncementModal = ({ open, onClose, announcementId, reload }) => { + const classes = useStyles(); + + const initialState = { + title: "", + text: "", + priority: 3, + status: true, + }; + + const [confirmationOpen, setConfirmationOpen] = useState(false); + const [announcement, setAnnouncement] = useState(initialState); + const [attachment, setAttachment] = useState(null); + const attachmentFile = useRef(null); + + useEffect(() => { + try { + (async () => { + if (!announcementId) return; + + const { data } = await api.get(`/announcements/${announcementId}`); + setAnnouncement((prevState) => { + return { ...prevState, ...data }; + }); + })(); + } catch (err) { + toastError(err); + } + }, [announcementId, open]); + + const handleClose = () => { + setAnnouncement(initialState); + setAttachment(null); + onClose(); + }; + + const handleAttachmentFile = (e) => { + const file = head(e.target.files); + if (file) { + setAttachment(file); + } + }; + + const handleSaveAnnouncement = async (values) => { + const announcementData = { ...values }; + try { + if (announcementId) { + await api.put(`/announcements/${announcementId}`, announcementData); + if (attachment != null) { + const formData = new FormData(); + formData.append("file", attachment); + await api.post( + `/announcements/${announcementId}/media-upload`, + formData + ); + } + } else { + const { data } = await api.post("/announcements", announcementData); + if (attachment != null) { + const formData = new FormData(); + formData.append("file", attachment); + await api.post(`/announcements/${data.id}/media-upload`, formData); + } + } + toast.success(i18n.t("announcements.toasts.success")); + if (typeof reload == "function") { + reload(); + } + } catch (err) { + toastError(err); + } + handleClose(); + }; + + const deleteMedia = async () => { + if (attachment) { + setAttachment(null); + attachmentFile.current.value = null; + } + + if (announcement.mediaPath) { + await api.delete(`/announcements/${announcement.id}/media-upload`); + setAnnouncement((prev) => ({ + ...prev, + mediaPath: null, + })); + toast.success(i18n.t("announcements.toasts.deleted")); + if (typeof reload == "function") { + reload(); + } + } + }; + + return ( +
+ setConfirmationOpen(false)} + onConfirm={deleteMedia} + > + {i18n.t("announcements.confirmationModal.deleteMessage")} + + + + {announcementId + ? `${i18n.t("announcements.dialog.edit")}` + : `${i18n.t("announcements.dialog.add")}`} + +
+ handleAttachmentFile(e)} + /> +
+ { + setTimeout(() => { + handleSaveAnnouncement(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ touched, errors, isSubmitting, values }) => ( +
+ + + + + + + + + + + + {i18n.t("announcements.dialog.form.status")} + + + Ativo + Inativo + + + + + + + {i18n.t("announcements.dialog.form.priority")} + + + Alta + Média + Baixa + + + + {(announcement.mediaPath || attachment) && ( + + + setConfirmationOpen(true)} + color="secondary" + > + + + + )} + + + + {!attachment && !announcement.mediaPath && ( + + )} + + + +
+ )} +
+
+
+ ); +}; + +export default AnnouncementModal; diff --git a/frontend/src/components/AnnouncementsPopover/index.js b/frontend/src/components/AnnouncementsPopover/index.js new file mode 100644 index 0000000..b4af33d --- /dev/null +++ b/frontend/src/components/AnnouncementsPopover/index.js @@ -0,0 +1,329 @@ +import React, { useEffect, useReducer, useState, useContext } from "react"; +import { makeStyles } from "@material-ui/core/styles"; +import toastError from "../../errors/toastError"; +import Popover from "@material-ui/core/Popover"; +import AnnouncementIcon from "@material-ui/icons/Announcement"; +import Notifications from "@material-ui/icons/Notifications" + +import { + Avatar, + Badge, + IconButton, + List, + ListItem, + ListItemAvatar, + ListItemText, + Dialog, + Paper, + Typography, + DialogTitle, + DialogContent, + DialogActions, + Button, + DialogContentText, +} from "@material-ui/core"; +import api from "../../services/api"; +import { isArray } from "lodash"; +import moment from "moment"; +import { SocketContext } from "../../context/Socket/SocketContext"; + +const useStyles = makeStyles((theme) => ({ + mainPaper: { + flex: 1, + maxHeight: 3000, + maxWidth: 5000, + padding: theme.spacing(1), + overflowY: "scroll", + ...theme.scrollbarStyles, + }, +})); + +function AnnouncementDialog({ announcement, open, handleClose }) { + const getMediaPath = (filename) => { + return `${process.env.REACT_APP_BACKEND_URL}/public/${filename}`; + }; + return ( + handleClose()} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + {announcement.title} + + {announcement.mediaPath && ( +
+ )} + + {announcement.text} + +
+ + + +
+ ); +} + +const reducer = (state, action) => { + if (action.type === "LOAD_ANNOUNCEMENTS") { + const announcements = action.payload; + const newAnnouncements = []; + + if (isArray(announcements)) { + announcements.forEach((announcement) => { + const announcementIndex = state.findIndex( + (u) => u.id === announcement.id + ); + if (announcementIndex !== -1) { + state[announcementIndex] = announcement; + } else { + newAnnouncements.push(announcement); + } + }); + } + + return [...state, ...newAnnouncements]; + } + + if (action.type === "UPDATE_ANNOUNCEMENTS") { + const announcement = action.payload; + const announcementIndex = state.findIndex((u) => u.id === announcement.id); + + if (announcementIndex !== -1) { + state[announcementIndex] = announcement; + return [...state]; + } else { + return [announcement, ...state]; + } + } + + if (action.type === "DELETE_ANNOUNCEMENT") { + const announcementId = action.payload; + + const announcementIndex = state.findIndex((u) => u.id === announcementId); + if (announcementIndex !== -1) { + state.splice(announcementIndex, 1); + } + return [...state]; + } + + if (action.type === "RESET") { + return []; + } +}; + +export default function AnnouncementsPopover() { + const classes = useStyles(); + + const [loading, setLoading] = useState(false); + const [anchorEl, setAnchorEl] = useState(null); + const [pageNumber, setPageNumber] = useState(1); + const [hasMore, setHasMore] = useState(false); + const [searchParam] = useState(""); + const [announcements, dispatch] = useReducer(reducer, []); + const [invisible, setInvisible] = useState(false); + const [announcement, setAnnouncement] = useState({}); + const [showAnnouncementDialog, setShowAnnouncementDialog] = useState(false); + + const socketManager = useContext(SocketContext); + + useEffect(() => { + dispatch({ type: "RESET" }); + setPageNumber(1); + }, [searchParam]); + + useEffect(() => { + setLoading(true); + const delayDebounceFn = setTimeout(() => { + fetchAnnouncements(); + }, 500); + return () => clearTimeout(delayDebounceFn); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [searchParam, pageNumber]); + + useEffect(() => { + const companyId = localStorage.getItem("companyId"); + const socket = socketManager.getSocket(companyId); + + if (!socket) { + return () => {}; + } + + socket.on(`company-announcement`, (data) => { + if (data.action === "update" || data.action === "create") { + dispatch({ type: "UPDATE_ANNOUNCEMENTS", payload: data.record }); + setInvisible(false); + } + if (data.action === "delete") { + dispatch({ type: "DELETE_ANNOUNCEMENT", payload: +data.id }); + } + }); + return () => { + socket.disconnect(); + }; + }, [socketManager]); + + const fetchAnnouncements = async () => { + try { + const { data } = await api.get("/announcements/", { + params: { searchParam, pageNumber }, + }); + dispatch({ type: "LOAD_ANNOUNCEMENTS", payload: data.records }); + setHasMore(data.hasMore); + setLoading(false); + } catch (err) { + toastError(err); + } + }; + + const loadMore = () => { + setPageNumber((prevState) => prevState + 1); + }; + + const handleScroll = (e) => { + if (!hasMore || loading) return; + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + if (scrollHeight - (scrollTop + 100) < clientHeight) { + loadMore(); + } + }; + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + setInvisible(true); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const borderPriority = (priority) => { + if (priority === 1) { + return "4px solid #b81111"; + } + if (priority === 2) { + return "4px solid orange"; + } + if (priority === 3) { + return "4px solid grey"; + } + }; + + const getMediaPath = (filename) => { + return `${process.env.REACT_APP_BACKEND_URL}/public/${filename}`; + }; + + const handleShowAnnouncementDialog = (record) => { + setAnnouncement(record); + setShowAnnouncementDialog(true); + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + const id = open ? "simple-popover" : undefined; + + return ( +
+ setShowAnnouncementDialog(false)} + /> + + + + + + + + + {isArray(announcements) && + announcements.map((item, key) => ( + handleShowAnnouncementDialog(item)} + > + {item.mediaPath && ( + + + + )} + + + {moment(item.createdAt).format("DD/MM/YYYY")} + + + + {item.text} + + + } + /> + + ))} + {isArray(announcements) && announcements.length === 0 && ( + + )} + + + +
+ ); +} diff --git a/frontend/src/components/BackdropLoading/index.js b/frontend/src/components/BackdropLoading/index.js new file mode 100644 index 0000000..183b02b --- /dev/null +++ b/frontend/src/components/BackdropLoading/index.js @@ -0,0 +1,23 @@ +import React from "react"; + +import Backdrop from "@material-ui/core/Backdrop"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles(theme => ({ + backdrop: { + zIndex: theme.zIndex.drawer + 1, + color: "#fff", + }, +})); + +const BackdropLoading = () => { + const classes = useStyles(); + return ( + + + + ); +}; + +export default BackdropLoading; diff --git a/frontend/src/components/ButtonWithSpinner/index.js b/frontend/src/components/ButtonWithSpinner/index.js new file mode 100644 index 0000000..542c39c --- /dev/null +++ b/frontend/src/components/ButtonWithSpinner/index.js @@ -0,0 +1,35 @@ +import React from "react"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import { CircularProgress, Button } from "@material-ui/core"; + +const useStyles = makeStyles(theme => ({ + button: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, +})); + +const ButtonWithSpinner = ({ loading, children, ...rest }) => { + const classes = useStyles(); + + return ( + + ); +}; + +export default ButtonWithSpinner; diff --git a/frontend/src/components/CampaignModal/index.js b/frontend/src/components/CampaignModal/index.js new file mode 100644 index 0000000..afbc7d6 --- /dev/null +++ b/frontend/src/components/CampaignModal/index.js @@ -0,0 +1,771 @@ +import React, { useState, useEffect, useRef, useContext } from "react"; + +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; +import { toast } from "react-toastify"; +import { head } from "lodash"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Button from "@material-ui/core/Button"; +import IconButton from "@material-ui/core/IconButton"; +import TextField from "@material-ui/core/TextField"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import AttachFileIcon from "@material-ui/icons/AttachFile"; +import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"; + +import { i18n } from "../../translate/i18n"; +import moment from "moment"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { + Box, + FormControl, + Grid, + InputLabel, + MenuItem, + Select, + Tab, + Tabs, +} from "@material-ui/core"; +import { AuthContext } from "../../context/Auth/AuthContext"; +import ConfirmationModal from "../ConfirmationModal"; + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + flexWrap: "wrap", + backgroundColor: "#fff" + }, + + tabmsg: { + backgroundColor: theme.palette.campaigntab, + }, + + textField: { + marginRight: theme.spacing(1), + flex: 1, + }, + + extraAttr: { + display: "flex", + justifyContent: "center", + alignItems: "center", + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, +})); + +const CampaignSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "Too Short!") + .max(50, "Too Long!") + .required("Required"), +}); + +const CampaignModal = ({ + open, + onClose, + campaignId, + initialValues, + onSave, + resetPagination, +}) => { + const classes = useStyles(); + const isMounted = useRef(true); + const { user } = useContext(AuthContext); + const { companyId } = user; + const [file, setFile] = useState(null); + + const initialState = { + name: "", + message1: "", + message2: "", + message3: "", + message4: "", + message5: "", + confirmationMessage1: "", + confirmationMessage2: "", + confirmationMessage3: "", + confirmationMessage4: "", + confirmationMessage5: "", + status: "INATIVA", // INATIVA, PROGRAMADA, EM_ANDAMENTO, CANCELADA, FINALIZADA, + confirmation: false, + scheduledAt: "", + whatsappId: "", + contactListId: "", + tagListId: "Nenhuma", + companyId, + }; + + const [campaign, setCampaign] = useState(initialState); + const [whatsapps, setWhatsapps] = useState([]); + const [contactLists, setContactLists] = useState([]); + const [messageTab, setMessageTab] = useState(0); + const [attachment, setAttachment] = useState(null); + const [confirmationOpen, setConfirmationOpen] = useState(false); + const [campaignEditable, setCampaignEditable] = useState(true); + const attachmentFile = useRef(null); + const [tagLists, setTagLists] = useState([]); + + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + useEffect(() => { + (async () => { + try { + const { data } = await api.get("/files/", { + params: { companyId } + }); + + setFile(data.files); + } catch (err) { + toastError(err); + } + })(); + }, []); + + useEffect(() => { + if (isMounted.current) { + if (initialValues) { + setCampaign((prevState) => { + return { ...prevState, ...initialValues }; + }); + } + + api + .get(`/contact-lists/list`, { params: { companyId } }) + .then(({ data }) => setContactLists(data)); + + api + .get(`/whatsapp`, { params: { companyId, session: 0 } }) + .then(({ data }) => setWhatsapps(data)); + + api.get(`/tags`, { params: { companyId } }) + .then(({ data }) => { + const fetchedTags = data.tags; + // Perform any necessary data transformation here + const formattedTagLists = fetchedTags.map((tag) => ({ + id: tag.id, + name: tag.name, + })); + setTagLists(formattedTagLists); + }) + .catch((error) => { + console.error("Error retrieving tags:", error); + }); + + if (!campaignId) return; + + api.get(`/campaigns/${campaignId}`).then(({ data }) => { + setCampaign((prev) => { + let prevCampaignData = Object.assign({}, prev); + + Object.entries(data).forEach(([key, value]) => { + if (key === "scheduledAt" && value !== "" && value !== null) { + prevCampaignData[key] = moment(value).format("YYYY-MM-DDTHH:mm"); + } else { + prevCampaignData[key] = value === null ? "" : value; + } + }); + + return prevCampaignData; + }); + }); + } + }, [campaignId, open, initialValues, companyId]); + + useEffect(() => { + const now = moment(); + const scheduledAt = moment(campaign.scheduledAt); + const moreThenAnHour = + !Number.isNaN(scheduledAt.diff(now)) && scheduledAt.diff(now, "hour") > 1; + const isEditable = + campaign.status === "INATIVA" || + (campaign.status === "PROGRAMADA" && moreThenAnHour); + + setCampaignEditable(isEditable); + }, [campaign.status, campaign.scheduledAt]); + + const handleClose = () => { + onClose(); + setCampaign(initialState); + }; + + const handleAttachmentFile = (e) => { + const file = head(e.target.files); + if (file) { + setAttachment(file); + } + }; + + const handleSaveCampaign = async (values) => { + try { + const dataValues = {}; + Object.entries(values).forEach(([key, value]) => { + if (key === "scheduledAt" && value !== "" && value !== null) { + dataValues[key] = moment(value).format("YYYY-MM-DD HH:mm:ss"); + } else { + dataValues[key] = value === "" ? null : value; + } + }); + + if (campaignId) { + await api.put(`/campaigns/${campaignId}`, dataValues); + + if (attachment != null) { + const formData = new FormData(); + formData.append("file", attachment); + await api.post(`/campaigns/${campaignId}/media-upload`, formData); + } + handleClose(); + } else { + const { data } = await api.post("/campaigns", dataValues); + + if (attachment != null) { + const formData = new FormData(); + formData.append("file", attachment); + await api.post(`/campaigns/${data.id}/media-upload`, formData); + } + if (onSave) { + onSave(data); + } + handleClose(); + } + toast.success(i18n.t("campaigns.toasts.success")); + } catch (err) { + console.log(err); + toastError(err); + } + }; + + const deleteMedia = async () => { + if (attachment) { + setAttachment(null); + attachmentFile.current.value = null; + } + + if (campaign.mediaPath) { + await api.delete(`/campaigns/${campaign.id}/media-upload`); + setCampaign((prev) => ({ ...prev, mediaPath: null, mediaName: null })); + toast.success(i18n.t("campaigns.toasts.deleted")); + } + }; + + const renderMessageField = (identifier) => { + return ( + + ); + }; + + const renderConfirmationMessageField = (identifier) => { + return ( + + ); + }; + + const cancelCampaign = async () => { + try { + await api.post(`/campaigns/${campaign.id}/cancel`); + toast.success(i18n.t("campaigns.toasts.cancel")); + setCampaign((prev) => ({ ...prev, status: "CANCELADA" })); + resetPagination(); + } catch (err) { + toast.error(err.message); + } + }; + + const restartCampaign = async () => { + try { + await api.post(`/campaigns/${campaign.id}/restart`); + toast.success(i18n.t("campaigns.toasts.restart")); + setCampaign((prev) => ({ ...prev, status: "EM_ANDAMENTO" })); + resetPagination(); + } catch (err) { + toast.error(err.message); + } + }; + + return ( +
+ setConfirmationOpen(false)} + onConfirm={deleteMedia} + > + {i18n.t("campaigns.confirmationModal.deleteMessage")} + + + + {campaignEditable ? ( + <> + {campaignId + ? `${i18n.t("campaigns.dialog.update")}` + : `${i18n.t("campaigns.dialog.new")}`} + + ) : ( + <>{`${i18n.t("campaigns.dialog.readonly")}`} + )} + +
+ handleAttachmentFile(e)} + /> +
+ { + setTimeout(() => { + handleSaveCampaign(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ values, errors, touched, isSubmitting }) => ( +
+ + + + + + + + + {i18n.t("campaigns.dialog.form.confirmation")} + + + Desabilitada + Habilitada + + + + + + + {i18n.t("campaigns.dialog.form.contactList")} + + + Nenhuma + {contactLists && + contactLists.map((contactList) => ( + + {contactList.name} + + ))} + + + + + + + {i18n.t("campaigns.dialog.form.tagList")} + + + Nenhuma + {Array.isArray(tagLists) && + tagLists.map((tagList) => ( + + {tagList.name} + + ))} + + + + + + + {i18n.t("campaigns.dialog.form.whatsapp")} + + + Nenhuma + {whatsapps && + whatsapps.map((whatsapp) => ( + + {whatsapp.name} + + ))} + + + + + + + + + {i18n.t("campaigns.dialog.form.fileList")} + + {"Nenhum"} + {file.map(f => ( + + {f.name} + + ))} + + + + + setMessageTab(v)} + variant="fullWidth" + centered + style={{ + borderRadius: 2, + }} + > + + + + + + + + {messageTab === 0 && ( + <> + {values.confirmation ? ( + + + <>{renderMessageField("message1")} + + + <> + {renderConfirmationMessageField( + "confirmationMessage1" + )} + + + + ) : ( + <>{renderMessageField("message1")} + )} + + )} + {messageTab === 1 && ( + <> + {values.confirmation ? ( + + + <>{renderMessageField("message2")} + + + <> + {renderConfirmationMessageField( + "confirmationMessage2" + )} + + + + ) : ( + <>{renderMessageField("message2")} + )} + + )} + {messageTab === 2 && ( + <> + {values.confirmation ? ( + + + <>{renderMessageField("message3")} + + + <> + {renderConfirmationMessageField( + "confirmationMessage3" + )} + + + + ) : ( + <>{renderMessageField("message3")} + )} + + )} + {messageTab === 3 && ( + <> + {values.confirmation ? ( + + + <>{renderMessageField("message4")} + + + <> + {renderConfirmationMessageField( + "confirmationMessage4" + )} + + + + ) : ( + <>{renderMessageField("message4")} + )} + + )} + {messageTab === 4 && ( + <> + {values.confirmation ? ( + + + <>{renderMessageField("message5")} + + + <> + {renderConfirmationMessageField( + "confirmationMessage5" + )} + + + + ) : ( + <>{renderMessageField("message5")} + )} + + )} + + + {(campaign.mediaPath || attachment) && ( + + + {campaignEditable && ( + setConfirmationOpen(true)} + color="secondary" + > + + + )} + + )} + + + + {campaign.status === "CANCELADA" && ( + + )} + {campaign.status === "EM_ANDAMENTO" && ( + + )} + {!attachment && !campaign.mediaPath && campaignEditable && ( + + )} + + {(campaignEditable || campaign.status === "CANCELADA") && ( + + )} + +
+ )} +
+
+
+ ); +}; + +export default CampaignModal; diff --git a/frontend/src/components/Can/index.js b/frontend/src/components/Can/index.js new file mode 100644 index 0000000..3d80215 --- /dev/null +++ b/frontend/src/components/Can/index.js @@ -0,0 +1,39 @@ +import rules from "../../rules"; + +const check = (role, action, data) => { + const permissions = rules[role]; + if (!permissions) { + // role is not present in the rules + return false; + } + + const staticPermissions = permissions.static; + + if (staticPermissions && staticPermissions.includes(action)) { + // static rule not provided for action + return true; + } + + const dynamicPermissions = permissions.dynamic; + + if (dynamicPermissions) { + const permissionCondition = dynamicPermissions[action]; + if (!permissionCondition) { + // dynamic rule not provided for action + return false; + } + + return permissionCondition(data); + } + return false; +}; + +const Can = ({ role, perform, data, yes, no }) => + check(role, perform, data) ? yes() : no(); + +Can.defaultProps = { + yes: () => null, + no: () => null, +}; + +export { Can }; diff --git a/frontend/src/components/CheckoutPage/CheckoutPage.js b/frontend/src/components/CheckoutPage/CheckoutPage.js new file mode 100644 index 0000000..7e94b2c --- /dev/null +++ b/frontend/src/components/CheckoutPage/CheckoutPage.js @@ -0,0 +1,174 @@ +import React, { useContext, useState } from "react"; +import { + Stepper, + Step, + StepLabel, + Button, + Typography, + CircularProgress, +} from "@material-ui/core"; +import { Formik, Form } from "formik"; + +import AddressForm from "./Forms/AddressForm"; +import PaymentForm from "./Forms/PaymentForm"; +import ReviewOrder from "./ReviewOrder"; +import CheckoutSuccess from "./CheckoutSuccess"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { toast } from "react-toastify"; +import { AuthContext } from "../../context/Auth/AuthContext"; + + +import validationSchema from "./FormModel/validationSchema"; +import checkoutFormModel from "./FormModel/checkoutFormModel"; +import formInitialValues from "./FormModel/formInitialValues"; + +import useStyles from "./styles"; +import Invoices from "../../pages/Financeiro"; + + +export default function CheckoutPage(props) { + const steps = ["Dados", "Personalizar", "Revisar"]; + const { formId, formField } = checkoutFormModel; + + + + const classes = useStyles(); + const [activeStep, setActiveStep] = useState(1); + const [datePayment, setDatePayment] = useState(null); + const [invoiceId, setinvoiceId] = useState(props.Invoice.id); + const currentValidationSchema = validationSchema[activeStep]; + const isLastStep = activeStep === steps.length - 1; + const { user } = useContext(AuthContext); + +function _renderStepContent(step, setFieldValue, setActiveStep, values ) { + + switch (step) { + case 0: + return ; + case 1: + return ; + case 2: + return ; + default: + return
Not Found
; + } +} + + + async function _submitForm(values, actions) { + try { + const plan = JSON.parse(values.plan); + const newValues = { + firstName: values.firstName, + lastName: values.lastName, + address2: values.address2, + city: values.city, + state: values.state, + zipcode: values.zipcode, + country: values.country, + useAddressForPaymentDetails: values.useAddressForPaymentDetails, + nameOnCard: values.nameOnCard, + cardNumber: values.cardNumber, + cvv: values.cvv, + plan: values.plan, + price: plan.price, + users: plan.users, + connections: plan.connections, + invoiceId: invoiceId + } + + const { data } = await api.post("/subscription", newValues); + setDatePayment(data) + actions.setSubmitting(false); + setActiveStep(activeStep + 1); + toast.success("Assinatura realizada com sucesso!, aguardando a realização do pagamento"); + } catch (err) { + toastError(err); + } + } + + function _handleSubmit(values, actions) { + if (isLastStep) { + _submitForm(values, actions); + } else { + setActiveStep(activeStep + 1); + actions.setTouched({}); + actions.setSubmitting(false); + } + } + + function _handleBack() { + setActiveStep(activeStep - 1); + } + + return ( + + + Falta pouco! + + + {steps.map((label) => ( + + {label} + + ))} + + + {activeStep === steps.length ? ( + + ) : ( + + {({ isSubmitting, setFieldValue, values }) => ( +
+ {_renderStepContent(activeStep, setFieldValue, setActiveStep, values)} + +
+ {activeStep !== 1 && ( + + )} +
+ {activeStep !== 1 && ( + + )} + {isSubmitting && ( + + )} +
+
+
+ )} +
+ )} +
+
+ ); +} diff --git a/frontend/src/components/CheckoutPage/CheckoutSuccess/CheckoutSuccess.js b/frontend/src/components/CheckoutPage/CheckoutSuccess/CheckoutSuccess.js new file mode 100644 index 0000000..a2b9094 --- /dev/null +++ b/frontend/src/components/CheckoutPage/CheckoutSuccess/CheckoutSuccess.js @@ -0,0 +1,76 @@ +import React, { useState, useEffect, useContext } from 'react'; +import { useHistory } from "react-router-dom"; +import QRCode from 'react-qr-code'; +import { SuccessContent, Total } from './style'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; +import { FaCopy, FaCheckCircle } from 'react-icons/fa'; +import { SocketContext } from "../../../context/Socket/SocketContext"; +import { useDate } from "../../../hooks/useDate"; +import { toast } from "react-toastify"; + +function CheckoutSuccess(props) { + + const { pix } = props; + const [pixString,] = useState(pix.qrcode.qrcode); + const [copied, setCopied] = useState(false); + const history = useHistory(); + + const { dateToClient } = useDate(); + + const socketManager = useContext(SocketContext); + + useEffect(() => { + const companyId = localStorage.getItem("companyId"); + const socket = socketManager.getSocket(companyId); + + socket.on(`company-${companyId}-payment`, (data) => { + + if (data.action === "CONCLUIDA") { + toast.success(`Sua licença foi renovada até ${dateToClient(data.company.dueDate)}!`); + setTimeout(() => { + history.push("/"); + }, 4000); + } + }); + }, [history, socketManager]); + + const handleCopyQR = () => { + setTimeout(() => { + setCopied(false); + }, 1 * 1000); + setCopied(true); + }; + + return ( + + + TOTAL + R${pix.valor.original.toLocaleString('pt-br', { minimumFractionDigits: 2 })} + + + + + + + + Para finalizar, basta realizar o pagamento escaneando ou colando o + código Pix acima :) + + + + ); +} + +export default CheckoutSuccess; diff --git a/frontend/src/components/CheckoutPage/CheckoutSuccess/index.js b/frontend/src/components/CheckoutPage/CheckoutSuccess/index.js new file mode 100644 index 0000000..32bd128 --- /dev/null +++ b/frontend/src/components/CheckoutPage/CheckoutSuccess/index.js @@ -0,0 +1,2 @@ +import CheckoutSuccess from './CheckoutSuccess'; +export default CheckoutSuccess; diff --git a/frontend/src/components/CheckoutPage/CheckoutSuccess/style.js b/frontend/src/components/CheckoutPage/CheckoutSuccess/style.js new file mode 100644 index 0000000..f0bbdee --- /dev/null +++ b/frontend/src/components/CheckoutPage/CheckoutSuccess/style.js @@ -0,0 +1,117 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + footer { + margin-top: 30px; + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + + @media (max-width: 768px) { + flex-direction: column; + .checkout-buttons { + display: flex; + flex-direction: column-reverse; + width: 100%; + + button { + width: 100%; + margin-top: 16px; + margin-left: 0; + } + } + } + + button { + margin-left: 16px; + } + } +`; +export const Total = styled.div` + display: flex; + align-items: baseline; + + span { + color: #333; + font-weight: bold; + } + + strong { + color: #333; + font-size: 28px; + margin-left: 5px; + } + + @media (max-width: 768px) { + min-width: 100%; + justify-content: space-between; + } +`; + +export const SuccessContent = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + > h2 { + text-align: center; + } + + > svg { + margin-top: 16px; + } + + > span { + margin-top: 24px; + text-align: center; + } + + > p, + strong { + margin-top: 8px; + font-size: 9px; + color: #999; + } + + .copy-button { + font-size: 14px; + cursor: pointer; + text-align: center; + user-select: none; + min-height: 56px; + display: inline-flex; + -webkit-box-pack: center; + justify-content: center; + -webkit-box-align: center; + align-items: center; + background-color: #f9f9f9; + color: #000; + -webkit-appearance: none !important; + outline: none; + margin-top: 16px; + width: 256px; + font-weight: 600; + text-transform: uppercase; + border: none; + + > span { + margin-right: 8px; + } + } +`; + +export const CheckoutWrapper = styled.div` + width: 100%; + margin: 0 auto 0; + max-width: 1110px; + display: flex; + flex-direction: column; + -webkit-box-align: center; + align-items: center; + padding: 50px 95px; + background: #fff; + @media (max-width: 768px) { + padding: 16px 24px; +`; \ No newline at end of file diff --git a/frontend/src/components/CheckoutPage/FormModel/checkoutFormModel.js b/frontend/src/components/CheckoutPage/FormModel/checkoutFormModel.js new file mode 100644 index 0000000..f77ff4e --- /dev/null +++ b/frontend/src/components/CheckoutPage/FormModel/checkoutFormModel.js @@ -0,0 +1,73 @@ +export default { + formId: 'checkoutForm', + formField: { + firstName: { + name: 'firstName', + label: 'Nome completo*', + requiredErrorMsg: 'O nome completo é obrigatório' + }, + lastName: { + name: 'lastName', + label: 'Last name*', + requiredErrorMsg: 'Last name is required' + }, + address1: { + name: 'address2', + label: 'Endereço*', + requiredErrorMsg: 'O Endereço é obrigatório' + }, + + city: { + name: 'city', + label: 'Cidade*', + requiredErrorMsg: 'Cidade é obrigatória' + }, + state: { + name: 'state', + label: 'Estado*', + requiredErrorMsg: 'Cidade é obrigatória' + }, + zipcode: { + name: 'zipcode', + label: 'CEP*', + requiredErrorMsg: 'CEP é obrigatório', + invalidErrorMsg: 'Formato de CEP inválido' + }, + country: { + name: 'country', + label: 'País*', + requiredErrorMsg: 'País é obrigatório' + }, + useAddressForPaymentDetails: { + name: 'useAddressForPaymentDetails', + label: 'Use this address for payment details' + }, + invoiceId: { + name: 'invoiceId', + label: 'Use this invoiceId' + }, + nameOnCard: { + name: 'nameOnCard', + label: 'Name on card*', + requiredErrorMsg: 'Name on card is required' + }, + cardNumber: { + name: 'cardNumber', + label: 'Card number*', + requiredErrorMsg: 'Card number is required', + invalidErrorMsg: 'Card number is not valid (e.g. 4111111111111)' + }, + expiryDate: { + name: 'expiryDate', + label: 'Expiry date*', + requiredErrorMsg: 'Expiry date is required', + invalidErrorMsg: 'Expiry date is not valid' + }, + cvv: { + name: 'cvv', + label: 'CVV*', + requiredErrorMsg: 'CVV is required', + invalidErrorMsg: 'CVV is invalid (e.g. 357)' + } + } +}; diff --git a/frontend/src/components/CheckoutPage/FormModel/formInitialValues.js b/frontend/src/components/CheckoutPage/FormModel/formInitialValues.js new file mode 100644 index 0000000..a4d22e1 --- /dev/null +++ b/frontend/src/components/CheckoutPage/FormModel/formInitialValues.js @@ -0,0 +1,32 @@ +import checkoutFormModel from './checkoutFormModel'; +const { + formField: { + firstName, + lastName, + address1, + city, + state, + zipcode, + country, + useAddressForPaymentDetails, + nameOnCard, + cardNumber, + invoiceId, + cvv + } +} = checkoutFormModel; + +export default { + [firstName.name]: '', + [lastName.name]: '', + [address1.name]: '', + [city.name]: '', + [state.name]: '', + [zipcode.name]: '', + [country.name]: '', + [useAddressForPaymentDetails.name]: false, + [nameOnCard.name]: '', + [cardNumber.name]: '', + [invoiceId.name]: '', + [cvv.name]: '' +}; diff --git a/frontend/src/components/CheckoutPage/FormModel/validationSchema.js b/frontend/src/components/CheckoutPage/FormModel/validationSchema.js new file mode 100644 index 0000000..9c01534 --- /dev/null +++ b/frontend/src/components/CheckoutPage/FormModel/validationSchema.js @@ -0,0 +1,29 @@ +import * as Yup from 'yup'; +import checkoutFormModel from './checkoutFormModel'; +const { + formField: { + firstName, + address1, + city, + zipcode, + country, + } +} = checkoutFormModel; + + +export default [ + Yup.object().shape({ + [firstName.name]: Yup.string().required(`${firstName.requiredErrorMsg}`), + [address1.name]: Yup.string().required(`${address1.requiredErrorMsg}`), + [city.name]: Yup.string() + .nullable() + .required(`${city.requiredErrorMsg}`), + [zipcode.name]: Yup.string() + .required(`${zipcode.requiredErrorMsg}`), + + [country.name]: Yup.string() + .nullable() + .required(`${country.requiredErrorMsg}`) + }), + +]; diff --git a/frontend/src/components/CheckoutPage/Forms/AddressForm.js b/frontend/src/components/CheckoutPage/Forms/AddressForm.js new file mode 100644 index 0000000..a5afc16 --- /dev/null +++ b/frontend/src/components/CheckoutPage/Forms/AddressForm.js @@ -0,0 +1,134 @@ +import React, { useContext, useEffect, useState } from "react"; +import { Grid, Typography } from "@material-ui/core"; +import { InputField, SelectField } from "../../FormFields"; +import { AuthContext } from "../../../context/Auth/AuthContext"; + +const countries = [ + { + value: "BR", + label: "Brasil", + }, + { + value: "usa", + label: "United States", + }, +]; + +export default function AddressForm(props) { + + const { user } = useContext(AuthContext); + const [billingName, setBillingName] = useState(user.company.billingName); + const [addressZipCode, setAddressZipCode] = useState(user.company.addressZipCode); + const [addressStreet, setAddressStreet] = useState(user.company.addressStreet); + const [addressState, setAddressState] = useState(user.company.addressState); + const [addressCity, setAddressCity] = useState(user.company.addressCity); + const [addressDistrict, setAddressDistrict] = useState(user.company.addressDistrict); + + const { + formField: { + firstName, + address1, + city, + state, + zipcode, + country, + }, + setFieldValue + } = props; + useEffect(() => { + setFieldValue("firstName", billingName) + setFieldValue("zipcode", addressZipCode) + setFieldValue("address2", addressStreet) + setFieldValue("state", addressState) + setFieldValue("city", addressCity) + setFieldValue("country", addressDistrict) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( + + + Vamos precisar de algumas informações + + + + + { + setBillingName(e.target.value) + setFieldValue("firstName", e.target.value) + }} + /> + + + { + setAddressDistrict(e.target.value) + setFieldValue("country", e.target.value) + } + } + /> + + + + { + setAddressZipCode(e.target.value) + setFieldValue("zipcode", e.target.value) + }} + /> + + + { + setAddressStreet(e.target.value) + setFieldValue("address2", e.target.value) + + }} + /> + + + + { + setAddressState(e.target.value) + setFieldValue("state", e.target.value) + + }} + /> + + + { + setAddressCity(e.target.value) + setFieldValue("city", e.target.value) + }} + /> + + + + + ); +} diff --git a/frontend/src/components/CheckoutPage/Forms/PaymentForm.js b/frontend/src/components/CheckoutPage/Forms/PaymentForm.js new file mode 100644 index 0000000..4a8a6f3 --- /dev/null +++ b/frontend/src/components/CheckoutPage/Forms/PaymentForm.js @@ -0,0 +1,237 @@ +import React, { useState, useEffect, useReducer } from 'react'; +import Button from '@material-ui/core/Button'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader from '@material-ui/core/CardHeader'; +import Grid from '@material-ui/core/Grid'; +import StarIcon from '@material-ui/icons/StarBorder'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +import IconButton from '@material-ui/core/IconButton'; +import MinimizeIcon from '@material-ui/icons/Minimize'; +import AddIcon from '@material-ui/icons/Add'; + +import usePlans from "../../../hooks/usePlans"; +import useCompanies from "../../../hooks/useCompanies"; + +const useStyles = makeStyles((theme) => ({ + '@global': { + ul: { + margin: 0, + padding: 0, + listStyle: 'none', + }, + }, + margin: { + margin: theme.spacing(1), + }, + + + cardHeader: { + backgroundColor: + theme.palette.type === 'light' ? theme.palette.grey[200] : theme.palette.grey[700], + }, + cardPricing: { + display: 'flex', + justifyContent: 'center', + alignItems: 'baseline', + marginBottom: theme.spacing(2), + }, + footer: { + borderTop: `1px solid ${theme.palette.divider}`, + marginTop: theme.spacing(8), + paddingTop: theme.spacing(3), + paddingBottom: theme.spacing(3), + [theme.breakpoints.up('sm')]: { + paddingTop: theme.spacing(6), + paddingBottom: theme.spacing(6), + }, + }, + + customCard: { + display: "flex", + marginTop: "16px", + alignItems: "center", + flexDirection: "column", + }, + custom: { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + } +})); + + +export default function Pricing(props) { + const { + setFieldValue, + setActiveStep, + activeStep, + } = props; + + const handleChangeAdd = (event, newValue) => { + if (newValue < 3) return + + const newPrice = 11.00; + + setUsersPlans(newValue); + setCustomValuePlans(customValuePlans + newPrice); + } + + const handleChangeMin = (event, newValue) => { + if (newValue < 3) return + + const newPrice = 11; + + setUsersPlans(newValue); + setCustomValuePlans(customValuePlans - newPrice); + } + + const handleChangeConnectionsAdd = (event, newValue) => { + if (newValue < 3) return + const newPrice = 20.00; + setConnectionsPlans(newValue); + setCustomValuePlans(customValuePlans + newPrice); + } + + const handleChangeConnectionsMin = (event, newValue) => { + if (newValue < 3) return + const newPrice = 20; + setConnectionsPlans(newValue); + setCustomValuePlans(customValuePlans - newPrice); + } + + const { list, finder } = usePlans(); + const { find } = useCompanies(); + + const classes = useStyles(); + const [usersPlans, setUsersPlans] = React.useState(3); + const [companiesPlans, setCompaniesPlans] = useState(0); + const [connectionsPlans, setConnectionsPlans] = React.useState(3); + const [storagePlans, setStoragePlans] = React.useState([]); + const [customValuePlans, setCustomValuePlans] = React.useState(49.00); + const [loading, setLoading] = React.useState(false); + const companyId = localStorage.getItem("companyId"); + + useEffect(() => { + async function fetchData() { + await loadCompanies(); + } + fetchData(); + }, []) + + const loadCompanies = async () => { + setLoading(true); + try { + const companiesList = await find(companyId); + setCompaniesPlans(companiesList.planId); + await loadPlans(companiesList.planId); + } catch (e) { + console.log(e); + // toast.error("Não foi possível carregar a lista de registros"); + } + setLoading(false); + }; + const loadPlans = async (companiesPlans) => { + setLoading(true); + try { + const plansCompanies = await finder(companiesPlans); + const plans = [] + + //plansCompanies.forEach((plan) => { + plans.push({ + title: plansCompanies.name, + planId: plansCompanies.id, + price: plansCompanies.value, + description: [ + `${plansCompanies.users} Usuários`, + `${plansCompanies.connections} Conexão`, + `${plansCompanies.queues} Filas` + ], + users: plansCompanies.users, + connections: plansCompanies.connections, + queues: plansCompanies.queues, + buttonText: 'SELECIONAR', + buttonVariant: 'outlined', + }) + + // setStoragePlans(data); + //}); + setStoragePlans(plans); + } catch (e) { + console.log(e); + // toast.error("Não foi possível carregar a lista de registros"); + } + setLoading(false); + }; + + + const tiers = storagePlans + return ( + + + {tiers.map((tier) => ( + // Enterprise card is full width at sm breakpoint + + + : null} + className={classes.cardHeader} + /> + +
+ + { + + + R${tier.price.toLocaleString('pt-br', { minimumFractionDigits: 2 })} + + } + + + /mês + +
+
    + {tier.description.map((line) => ( + + {line} + + ))} +
+
+ + + +
+
+ ))} +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/CheckoutPage/ReviewOrder/PaymentDetails.js b/frontend/src/components/CheckoutPage/ReviewOrder/PaymentDetails.js new file mode 100644 index 0000000..8f7fa76 --- /dev/null +++ b/frontend/src/components/CheckoutPage/ReviewOrder/PaymentDetails.js @@ -0,0 +1,61 @@ +import React, {useContext} from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import useStyles from './styles'; +import { AuthContext } from "../../../context/Auth/AuthContext"; + +function PaymentDetails(props) { + const { formValues } = props; + const classes = useStyles(); + const { firstName, address2, city, zipcode, state, country, plan } = formValues; + const { user } = useContext(AuthContext); + + + const newPlan = JSON.parse(plan); + const { price } = newPlan; + + return ( + + + Informação de pagamento + + + + + Email: + + + {user.company.email} + + + + + Nome: + + + {firstName} + + + + + Endereço: + + + + {address2}, {city} - {state} {zipcode} {country} + + + + + + Total: + + + R${price.toLocaleString('pt-br', {minimumFractionDigits: 2})} + + + + + ); +} + +export default PaymentDetails; diff --git a/frontend/src/components/CheckoutPage/ReviewOrder/ProductDetails.js b/frontend/src/components/CheckoutPage/ReviewOrder/ProductDetails.js new file mode 100644 index 0000000..6d9da8d --- /dev/null +++ b/frontend/src/components/CheckoutPage/ReviewOrder/ProductDetails.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { Typography, List, ListItem, ListItemText } from '@material-ui/core'; +import useStyles from './styles'; + +const products = [ + { name: 'Product 1', desc: 'A nice thing', price: '$9.99' }, + { name: 'Product 2', desc: 'Another thing', price: '$3.45' }, + { name: 'Product 3', desc: 'Something else', price: '$6.51' }, + { name: 'Product 4', desc: 'Best thing of all', price: '$14.11' }, + { name: 'Shipping', desc: '', price: 'Free' } +]; + +function ProductDetails() { + const classes = useStyles(); + return ( + + {products.map(product => ( + + + {product.price} + + ))} + + + + $34.06 + + + + ); +} + +export default ProductDetails; diff --git a/frontend/src/components/CheckoutPage/ReviewOrder/ReviewOrder.js b/frontend/src/components/CheckoutPage/ReviewOrder/ReviewOrder.js new file mode 100644 index 0000000..4ffb132 --- /dev/null +++ b/frontend/src/components/CheckoutPage/ReviewOrder/ReviewOrder.js @@ -0,0 +1,19 @@ +import React from 'react'; +import { useFormikContext } from 'formik'; +import { Typography, Grid } from '@material-ui/core'; +import ShippingDetails from './ShippingDetails'; +import PaymentDetails from './PaymentDetails'; + +export default function ReviewOrder() { + const { values: formValues } = useFormikContext(); + return ( + + + Resumo da assinatura + + + + + + ); +} diff --git a/frontend/src/components/CheckoutPage/ReviewOrder/ShippingDetails.js b/frontend/src/components/CheckoutPage/ReviewOrder/ShippingDetails.js new file mode 100644 index 0000000..78c9de0 --- /dev/null +++ b/frontend/src/components/CheckoutPage/ReviewOrder/ShippingDetails.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import useStyles from './styles'; + +function PaymentDetails(props) { + const { formValues } = props; + const classes = useStyles(); + const { plan } = formValues; + + const newPlan = JSON.parse(plan); + const { users, connections, price } = newPlan; + return ( + + + Detalhes do plano + + Usuários: {users} + Whatsapps: {connections} + Cobrança: Mensal + Total: R${price.toLocaleString('pt-br', {minimumFractionDigits: 2})} + + ); +} + +export default PaymentDetails; diff --git a/frontend/src/components/CheckoutPage/ReviewOrder/index.js b/frontend/src/components/CheckoutPage/ReviewOrder/index.js new file mode 100644 index 0000000..4a3afc8 --- /dev/null +++ b/frontend/src/components/CheckoutPage/ReviewOrder/index.js @@ -0,0 +1,2 @@ +import ReviewOrder from './ReviewOrder'; +export default ReviewOrder; diff --git a/frontend/src/components/CheckoutPage/ReviewOrder/styles.js b/frontend/src/components/CheckoutPage/ReviewOrder/styles.js new file mode 100644 index 0000000..626176a --- /dev/null +++ b/frontend/src/components/CheckoutPage/ReviewOrder/styles.js @@ -0,0 +1,12 @@ +import { makeStyles } from '@material-ui/core/styles'; +export default makeStyles(theme => ({ + listItem: { + padding: theme.spacing(1, 0) + }, + total: { + fontWeight: '700' + }, + title: { + marginTop: theme.spacing(2) + } +})); diff --git a/frontend/src/components/CheckoutPage/index.js b/frontend/src/components/CheckoutPage/index.js new file mode 100644 index 0000000..8728a02 --- /dev/null +++ b/frontend/src/components/CheckoutPage/index.js @@ -0,0 +1,2 @@ +import CheckoutPage from './CheckoutPage'; +export default CheckoutPage; diff --git a/frontend/src/components/CheckoutPage/styles.js b/frontend/src/components/CheckoutPage/styles.js new file mode 100644 index 0000000..963c4d2 --- /dev/null +++ b/frontend/src/components/CheckoutPage/styles.js @@ -0,0 +1,23 @@ +import { makeStyles } from '@material-ui/core/styles'; +export default makeStyles(theme => ({ + stepper: { + padding: theme.spacing(3, 0, 5) + }, + buttons: { + display: 'flex', + justifyContent: 'flex-end' + }, + button: { + marginTop: theme.spacing(3), + marginLeft: theme.spacing(1) + }, + wrapper: { + margin: theme.spacing(1), + position: 'relative' + }, + buttonProgress: { + position: 'absolute', + top: '50%', + left: '50%' + } +})); diff --git a/frontend/src/components/ColorPicker/index.js b/frontend/src/components/ColorPicker/index.js new file mode 100644 index 0000000..7eb9d0a --- /dev/null +++ b/frontend/src/components/ColorPicker/index.js @@ -0,0 +1,85 @@ +import { Dialog } from "@material-ui/core"; +import React, { useState } from "react"; + +import { BlockPicker } from "react-color"; + +const ColorPicker = ({ onChange, currentColor, handleClose, open }) => { + const [selectedColor, setSelectedColor] = useState(currentColor); + + const handleChange = color => { + setSelectedColor(color.hex); + handleClose(); + }; + + const colors = [ + "#B80000", + "#DB3E00", + "#FCCB00", + "#008B02", + "#006B76", + "#1273DE", + "#004DCF", + "#5300EB", + "#EB9694", + "#FAD0C3", + "#FEF3BD", + "#C1E1C5", + "#BEDADC", + "#C4DEF6", + "#BED3F3", + "#D4C4FB", + "#4D4D4D", + "#999999", + "#F44E3B", + "#FE9200", + "#FCDC00", + "#DBDF00", + "#A4DD00", + "#68CCCA", + "#73D8FF", + "#AEA1FF", + "#FDA1FF", + "#333333", + "#808080", + "#cccccc", + "#D33115", + "#E27300", + "#FCC400", + "#B0BC00", + "#68BC00", + "#16A5A5", + "#009CE0", + "#7B64FF", + "#FA28FF", + "#666666", + "#B3B3B3", + "#9F0500", + "#C45100", + "#FB9E00", + "#808900", + "#194D33", + "#0C797D", + "#0062B1", + "#653294", + "#AB149E", + ]; + + return ( + + onChange(color.hex)} + /> + + ); +}; + +export default ColorPicker; diff --git a/frontend/src/components/CompaniesManager/index.js b/frontend/src/components/CompaniesManager/index.js new file mode 100644 index 0000000..f8866e3 --- /dev/null +++ b/frontend/src/components/CompaniesManager/index.js @@ -0,0 +1,627 @@ +import React, { useState, useEffect } from "react"; +import { + makeStyles, + Paper, + Grid, + FormControl, + InputLabel, + MenuItem, + TextField, + Table, + TableHead, + TableBody, + TableCell, + TableRow, + IconButton, + Select, +} from "@material-ui/core"; +import { Formik, Form, Field } from "formik"; +import ButtonWithSpinner from "../ButtonWithSpinner"; +import ConfirmationModal from "../ConfirmationModal"; + +import { Edit as EditIcon } from "@material-ui/icons"; + +import { toast } from "react-toastify"; +import useCompanies from "../../hooks/useCompanies"; +import usePlans from "../../hooks/usePlans"; +import ModalUsers from "../ModalUsers"; +import api from "../../services/api"; +import { head, isArray, has } from "lodash"; +import { useDate } from "../../hooks/useDate"; + +import moment from "moment"; + +const useStyles = makeStyles((theme) => ({ + root: { + width: "100%", + }, + mainPaper: { + width: "100%", + flex: 1, + padding: theme.spacing(2), + }, + fullWidth: { + width: "100%", + }, + tableContainer: { + width: "100%", + overflowX: "scroll", + ...theme.scrollbarStyles, + }, + textfield: { + width: "100%", + }, + textRight: { + textAlign: "right", + }, + row: { + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), + }, + control: { + paddingRight: theme.spacing(1), + paddingLeft: theme.spacing(1), + }, + buttonContainer: { + textAlign: "right", + padding: theme.spacing(1), + }, +})); + +export function CompanyForm(props) { + const { onSubmit, onDelete, onCancel, initialValue, loading } = props; + const classes = useStyles(); + const [plans, setPlans] = useState([]); + const [modalUser, setModalUser] = useState(false); + const [firstUser, setFirstUser] = useState({}); + + const [record, setRecord] = useState({ + name: "", + email: "", + phone: "", + planId: "", + status: true, + campaignsEnabled: false, + dueDate: "", + recurrence: "", + ...initialValue, + }); + + const { list: listPlans } = usePlans(); + + useEffect(() => { + async function fetchData() { + const list = await listPlans(); + setPlans(list); + } + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + setRecord((prev) => { + if (moment(initialValue).isValid()) { + initialValue.dueDate = moment(initialValue.dueDate).format( + "YYYY-MM-DD" + ); + } + return { + ...prev, + ...initialValue, + }; + }); + }, [initialValue]); + + const handleSubmit = async (data) => { + if (data.dueDate === "" || moment(data.dueDate).isValid() === false) { + data.dueDate = null; + } + onSubmit(data); + setRecord({ ...initialValue, dueDate: "" }); + }; + + const handleOpenModalUsers = async () => { + try { + const { data } = await api.get("/users/list", { + params: { + companyId: initialValue.id, + }, + }); + if (isArray(data) && data.length) { + setFirstUser(head(data)); + } + setModalUser(true); + } catch (e) { + toast.error(e); + } + }; + + const handleCloseModalUsers = () => { + setFirstUser({}); + setModalUser(false); + }; + + const incrementDueDate = () => { + const data = { ...record }; + if (data.dueDate !== "" && data.dueDate !== null) { + switch (data.recurrence) { + case "MENSAL": + data.dueDate = moment(data.dueDate) + .add(1, "month") + .format("YYYY-MM-DD"); + break; + case "BIMESTRAL": + data.dueDate = moment(data.dueDate) + .add(2, "month") + .format("YYYY-MM-DD"); + break; + case "TRIMESTRAL": + data.dueDate = moment(data.dueDate) + .add(3, "month") + .format("YYYY-MM-DD"); + break; + case "SEMESTRAL": + data.dueDate = moment(data.dueDate) + .add(6, "month") + .format("YYYY-MM-DD"); + break; + case "ANUAL": + data.dueDate = moment(data.dueDate) + .add(12, "month") + .format("YYYY-MM-DD"); + break; + default: + break; + } + } + setRecord(data); + }; + + return ( + <> + + + setTimeout(() => { + handleSubmit(values); + resetForm(); + }, 500) + } + > + {(values, setValues) => ( +
+ + + + + + + + + + + + + Plano + + {plans.map((plan, key) => ( + + {plan.name} + + ))} + + + + + + Status + + Sim + Não + + + + + + Campanhas + + Habilitadas + Desabilitadas + + + + + + + + + + + + Recorrência + + + Mensal + {/*Bimestral*/} + {/*Trimestral*/} + {/*Semestral*/} + {/*Anual*/} + + + + + + + onCancel()} + variant="contained" + > + Limpar + + + {record.id !== undefined ? ( + <> + + onDelete(record)} + variant="contained" + color="secondary" + > + Excluir + + + + incrementDueDate()} + variant="contained" + color="primary" + > + + Vencimento + + + + handleOpenModalUsers()} + variant="contained" + color="primary" + > + Usuário + + + + ) : null} + + + Salvar + + + + + +
+ )} +
+ + ); +} + +export function CompaniesManagerGrid(props) { + const { records, onSelect } = props; + const classes = useStyles(); + const { dateToClient } = useDate(); + + const renderStatus = (row) => { + return row.status === false ? "Não" : "Sim"; + }; + + const renderPlan = (row) => { + return row.planId !== null ? row.plan.name : "-"; + }; + + const renderCampaignsStatus = (row) => { + if ( + has(row, "settings") && + isArray(row.settings) && + row.settings.length > 0 + ) { + const setting = row.settings.find((s) => s.key === "campaignsEnabled"); + if (setting) { + return setting.value === "true" ? "Habilitadas" : "Desabilitadas"; + } + } + return "Desabilitadas"; + }; + + const rowStyle = (record) => { + if (moment(record.dueDate).isValid()) { + const now = moment(); + const dueDate = moment(record.dueDate); + const diff = dueDate.diff(now, "days"); + if (diff === 5) { + return { backgroundColor: "#fffead" }; + } + if (diff >= -3 && diff <= 4) { + return { backgroundColor: "#f7cc8f" }; + } + if (diff === -4) { + return { backgroundColor: "#fa8c8c" }; + } + } + return {}; + }; + + return ( + + + + + + # + + Nome + E-mail + Telefone + Plano + Campanhas + Status + Criada Em + Vencimento + + + + {records.map((row, key) => ( + + + onSelect(row)} aria-label="delete"> + + + + {row.name || "-"} + {row.email || "-"} + {row.phone || "-"} + {renderPlan(row)} + {renderCampaignsStatus(row)} + {renderStatus(row)} + {dateToClient(row.createdAt)} + + {dateToClient(row.dueDate)} +
+ {row.recurrence} +
+
+ ))} +
+
+
+ ); +} + +export default function CompaniesManager() { + const classes = useStyles(); + const { list, save, update, remove } = useCompanies(); + + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + const [loading, setLoading] = useState(false); + const [records, setRecords] = useState([]); + const [record, setRecord] = useState({ + name: "", + email: "", + phone: "", + planId: "", + status: true, + campaignsEnabled: false, + dueDate: "", + recurrence: "", + }); + + useEffect(() => { + loadPlans(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const loadPlans = async () => { + setLoading(true); + try { + const companyList = await list(); + setRecords(companyList); + } catch (e) { + toast.error("Não foi possível carregar a lista de registros"); + } + setLoading(false); + }; + + const handleSubmit = async (data) => { + setLoading(true); + try { + if (data.id !== undefined) { + await update(data); + } else { + await save(data); + } + await loadPlans(); + handleCancel(); + toast.success("Operação realizada com sucesso!"); + } catch (e) { + toast.error( + "Não foi possível realizar a operação. Verifique se já existe uma empresa com o mesmo nome ou se os campos foram preenchidos corretamente" + ); + } + setLoading(false); + }; + + const handleDelete = async () => { + setLoading(true); + try { + await remove(record.id); + await loadPlans(); + handleCancel(); + toast.success("Operação realizada com sucesso!"); + } catch (e) { + toast.error("Não foi possível realizar a operação"); + } + setLoading(false); + }; + + const handleOpenDeleteDialog = () => { + setShowConfirmDialog(true); + }; + + const handleCancel = () => { + setRecord((prev) => ({ + ...prev, + name: "", + email: "", + phone: "", + planId: "", + status: true, + campaignsEnabled: false, + dueDate: "", + recurrence: "", + })); + }; + + const handleSelect = (data) => { + let campaignsEnabled = false; + + const setting = data.settings.find( + (s) => s.key.indexOf("campaignsEnabled") > -1 + ); + if (setting) { + campaignsEnabled = + setting.value === "true" || setting.value === "enabled"; + } + + setRecord((prev) => ({ + ...prev, + id: data.id, + name: data.name || "", + phone: data.phone || "", + email: data.email || "", + planId: data.planId || "", + status: data.status === false ? false : true, + campaignsEnabled, + dueDate: data.dueDate || "", + recurrence: data.recurrence || "", + })); + }; + + return ( + + + + + + + + + + setShowConfirmDialog(false)} + onConfirm={() => handleDelete()} + > + Deseja realmente excluir esse registro? + + + ); +} diff --git a/frontend/src/components/ConfirmationModal/index.js b/frontend/src/components/ConfirmationModal/index.js new file mode 100644 index 0000000..ce340f2 --- /dev/null +++ b/frontend/src/components/ConfirmationModal/index.js @@ -0,0 +1,45 @@ +import React from "react"; +import Button from "@material-ui/core/Button"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import Typography from "@material-ui/core/Typography"; + +import { i18n } from "../../translate/i18n"; + +const ConfirmationModal = ({ title, children, open, onClose, onConfirm }) => { + return ( + onClose(false)} + aria-labelledby="confirm-dialog" + > + {title} + + {children} + + + + + + + ); +}; + +export default ConfirmationModal; diff --git a/frontend/src/components/ContactDrawer/index.js b/frontend/src/components/ContactDrawer/index.js new file mode 100644 index 0000000..912035e --- /dev/null +++ b/frontend/src/components/ContactDrawer/index.js @@ -0,0 +1,199 @@ +import React, { useEffect, useState } from "react"; + +import { makeStyles } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import IconButton from "@material-ui/core/IconButton"; +import CloseIcon from "@material-ui/icons/Close"; +import Drawer from "@material-ui/core/Drawer"; +import Link from "@material-ui/core/Link"; +import InputLabel from "@material-ui/core/InputLabel"; +import Avatar from "@material-ui/core/Avatar"; +import Button from "@material-ui/core/Button"; +import Paper from "@material-ui/core/Paper"; +import CreateIcon from '@material-ui/icons/Create'; + +import { i18n } from "../../translate/i18n"; + +import ContactDrawerSkeleton from "../ContactDrawerSkeleton"; +import MarkdownWrapper from "../MarkdownWrapper"; +import { CardHeader } from "@material-ui/core"; +import { ContactForm } from "../ContactForm"; +import ContactModal from "../ContactModal"; +import { ContactNotes } from "../ContactNotes"; + +const drawerWidth = 320; + +const useStyles = makeStyles(theme => ({ + drawer: { + width: drawerWidth, + flexShrink: 0, + }, + drawerPaper: { + width: drawerWidth, + display: "flex", + borderTop: "1px solid rgba(0, 0, 0, 0.12)", + borderRight: "1px solid rgba(0, 0, 0, 0.12)", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + borderTopRightRadius: 4, + borderBottomRightRadius: 4, + }, + header: { + display: "flex", + borderBottom: "1px solid rgba(0, 0, 0, 0.12)", + backgroundColor: theme.palette.contactdrawer, //DARK MODE PLW DESIGN// + alignItems: "center", + padding: theme.spacing(0, 1), + minHeight: "73px", + justifyContent: "flex-start", + }, + content: { + display: "flex", + backgroundColor: theme.palette.contactdrawer, //DARK MODE PLW DESIGN// + flexDirection: "column", + padding: "8px 0px 8px 8px", + height: "100%", + overflowY: "scroll", + ...theme.scrollbarStyles, + }, + + contactAvatar: { + margin: 15, + width: 100, + height: 100, + }, + + contactHeader: { + display: "flex", + padding: 8, + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + "& > *": { + margin: 4, + }, + }, + + contactDetails: { + marginTop: 8, + padding: 8, + display: "flex", + flexDirection: "column", + }, + contactExtraInfo: { + marginTop: 4, + padding: 6, + }, +})); + +const ContactDrawer = ({ open, handleDrawerClose, contact, ticket, loading }) => { + const classes = useStyles(); + + const [modalOpen, setModalOpen] = useState(false); + const [openForm, setOpenForm] = useState(false); + + useEffect(() => { + setOpenForm(false); + }, [open, contact]); + + return ( + <> + +
+ + + + + {i18n.t("contactDrawer.header")} + +
+ {loading ? ( + + ) : ( +
+ + {}} + style={{ cursor: "pointer", width: '100%' }} + titleTypographyProps={{ noWrap: true }} + subheaderTypographyProps={{ noWrap: true }} + avatar={} + title={ + <> + setOpenForm(true)}> + {contact.name} + + + + } + subheader={ + <> + + {contact.number} + + + {contact.email} + + + } + /> + + {(contact.id && openForm) && setOpenForm(false)} />} + + + + {i18n.t("ticketOptionsMenu.appointmentsModal.title")} + + + + + setModalOpen(false)} + contactId={contact.id} + > + + {i18n.t("contactDrawer.extraInfo")} + + {contact?.extraInfo?.map(info => ( + + {info.name} + + {info.value} + + + ))} + +
+ )} +
+ + ); +}; + +export default ContactDrawer; diff --git a/frontend/src/components/ContactDrawerSkeleton/index.js b/frontend/src/components/ContactDrawerSkeleton/index.js new file mode 100644 index 0000000..14b9027 --- /dev/null +++ b/frontend/src/components/ContactDrawerSkeleton/index.js @@ -0,0 +1,50 @@ +import React from "react"; +import Skeleton from "@material-ui/lab/Skeleton"; +import Typography from "@material-ui/core/Typography"; +import Paper from "@material-ui/core/Paper"; +import { i18n } from "../../translate/i18n"; +import { Grid } from "@material-ui/core"; + +const ContactDrawerSkeleton = ({ classes }) => { + return ( +
+ + + + + + + + + + + + + + + {i18n.t("contactDrawer.extraInfo")} + + + + + + + + + + + + + + +
+ ); +}; + +export default ContactDrawerSkeleton; diff --git a/frontend/src/components/ContactForm/index.js b/frontend/src/components/ContactForm/index.js new file mode 100644 index 0000000..efdc98a --- /dev/null +++ b/frontend/src/components/ContactForm/index.js @@ -0,0 +1,187 @@ +import React, { useState, useEffect } from "react"; + +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; +import { toast } from "react-toastify"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +import { i18n } from "../../translate/i18n"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { Grid } from "@material-ui/core"; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + flexWrap: "wrap", + }, + textField: { + marginRight: theme.spacing(1), + flex: 1, + }, + + extraAttr: { + display: "flex", + justifyContent: "center", + alignItems: "center", + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, + + textCenter: { + backgroundColor: 'red' + } +})); + +const ContactSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "Too Short!") + .max(50, "Too Long!") + .required("Required"), + number: Yup.string().min(8, "Too Short!").max(50, "Too Long!"), + email: Yup.string().email("Invalid email"), +}); + +export function ContactForm ({ initialContact, onSave, onCancel }) { + const classes = useStyles(); + + const [contact, setContact] = useState(initialContact); + + useEffect(() => { + setContact(initialContact); + }, [initialContact]); + + const handleSaveContact = async values => { + try { + if (contact.id) { + await api.put(`/contacts/${contact.id}`, values); + } else { + const { data } = await api.post("/contacts", values); + if (onSave) { + onSave(data); + } + } + toast.success(i18n.t("contactModal.success")); + } catch (err) { + toastError(err); + } + }; + + return ( + { + setTimeout(() => { + handleSaveContact(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ values, errors, touched, isSubmitting }) => ( +
+ + {/* + + {i18n.t("contactModal.form.mainInfo")} + + */} + + + + + + + + + + + + + + + + + + + + +
+ )} +
+ ) +} diff --git a/frontend/src/components/ContactListDialog/index.js b/frontend/src/components/ContactListDialog/index.js new file mode 100644 index 0000000..de6ac3b --- /dev/null +++ b/frontend/src/components/ContactListDialog/index.js @@ -0,0 +1,181 @@ +import React, { useState, useEffect } from "react"; + +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; +import { toast } from "react-toastify"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +import { i18n } from "../../translate/i18n"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + flexWrap: "wrap", + }, + multFieldLine: { + display: "flex", + "& > *:not(:last-child)": { + marginRight: theme.spacing(1), + }, + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, +})); + +const ContactListSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "Too Short!") + .max(50, "Too Long!") + .required("Required"), +}); + +const ContactListModal = ({ open, onClose, contactListId }) => { + const classes = useStyles(); + + const initialState = { + name: "", + }; + + const [contactList, setContactList] = useState(initialState); + + useEffect(() => { + const fetchContactList = async () => { + if (!contactListId) return; + try { + const { data } = await api.get(`/contact-lists/${contactListId}`); + setContactList((prevState) => { + return { ...prevState, ...data }; + }); + } catch (err) { + toastError(err); + } + }; + + fetchContactList(); + }, [contactListId, open]); + + const handleClose = () => { + onClose(); + setContactList(initialState); + }; + + const handleSaveContactList = async (values) => { + const contactListData = { ...values }; + try { + if (contactListId) { + await api.put(`/contact-lists/${contactListId}`, contactListData); + } else { + await api.post("/contact-lists", contactListData); + } + toast.success(i18n.t("contactList.dialog")); + } catch (err) { + toastError(err); + } + handleClose(); + }; + + return ( +
+ + + {contactListId + ? `${i18n.t("contactLists.dialog.edit")}` + : `${i18n.t("contactLists.dialog.add")}`} + + { + setTimeout(() => { + handleSaveContactList(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ touched, errors, isSubmitting }) => ( +
+ +
+ +
+
+ + + + +
+ )} +
+
+
+ ); +}; + +export default ContactListModal; diff --git a/frontend/src/components/ContactListItemModal/index.js b/frontend/src/components/ContactListItemModal/index.js new file mode 100644 index 0000000..52d3881 --- /dev/null +++ b/frontend/src/components/ContactListItemModal/index.js @@ -0,0 +1,242 @@ +import React, { useState, useEffect, useRef, useContext } from "react"; + +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; +import { toast } from "react-toastify"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import Typography from "@material-ui/core/Typography"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +import { i18n } from "../../translate/i18n"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { useParams } from "react-router-dom"; +import { AuthContext } from "../../context/Auth/AuthContext"; + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + flexWrap: "wrap", + }, + textField: { + marginRight: theme.spacing(1), + flex: 1, + }, + + extraAttr: { + display: "flex", + justifyContent: "center", + alignItems: "center", + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, +})); + +const ContactSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "Too Short!") + .max(50, "Too Long!") + .required("Required"), + number: Yup.string().min(8, "Too Short!").max(50, "Too Long!"), + email: Yup.string().email("Invalid email"), +}); + +const ContactListItemModal = ({ + open, + onClose, + contactId, + initialValues, + onSave, +}) => { + const classes = useStyles(); + const isMounted = useRef(true); + + const { + user: { companyId }, + } = useContext(AuthContext); + const { contactListId } = useParams(); + + const initialState = { + name: "", + number: "", + email: "", + }; + + const [contact, setContact] = useState(initialState); + + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + useEffect(() => { + const fetchContact = async () => { + if (initialValues) { + setContact((prevState) => { + return { ...prevState, ...initialValues }; + }); + } + + if (!contactId) return; + + try { + const { data } = await api.get(`/contact-list-items/${contactId}`); + if (isMounted.current) { + setContact(data); + } + } catch (err) { + toastError(err); + } + }; + + fetchContact(); + }, [contactId, open, initialValues]); + + const handleClose = () => { + onClose(); + setContact(initialState); + }; + + const handleSaveContact = async (values) => { + try { + if (contactId) { + await api.put(`/contact-list-items/${contactId}`, { + ...values, + companyId, + contactListId, + }); + handleClose(); + } else { + const { data } = await api.post("/contact-list-items", { + ...values, + companyId, + contactListId, + }); + if (onSave) { + onSave(data); + } + handleClose(); + } + toast.success(i18n.t("contactModal.success")); + } catch (err) { + toastError(err); + } + }; + + return ( +
+ + + {contactId + ? `${i18n.t("contactModal.title.edit")}` + : `${i18n.t("contactModal.title.add")}`} + + { + setTimeout(() => { + handleSaveContact(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ values, errors, touched, isSubmitting }) => ( +
+ + + {i18n.t("contactModal.form.mainInfo")} + + + +
+ +
+
+ + + + +
+ )} +
+
+
+ ); +}; + +export default ContactListItemModal; diff --git a/frontend/src/components/ContactListTable/index.js b/frontend/src/components/ContactListTable/index.js new file mode 100644 index 0000000..24f354d --- /dev/null +++ b/frontend/src/components/ContactListTable/index.js @@ -0,0 +1,103 @@ +import React, { useState, useEffect } from "react"; +import PropTypes from "prop-types"; +import { + Table, + TableHead, + TableBody, + TableCell, + TableRow, + IconButton, +} from "@material-ui/core"; +import { + Edit as EditIcon, + DeleteOutline as DeleteOutlineIcon, + People as PeopleIcon, +} from "@material-ui/icons"; + +import TableRowSkeleton from "../../components/TableRowSkeleton"; + +function ContactListsTable(props) { + const { + contactLists, + showLoading, + editContactList, + deleteContactList, + readOnly, + } = props; + const [loading, setLoading] = useState(true); + const [rows, setRows] = useState([]); + + useEffect(() => { + if (Array.isArray(contactLists)) { + setRows(contactLists); + } + if (showLoading !== undefined) { + setLoading(showLoading); + } + }, [contactLists, showLoading]); + + const handleEdit = (contactList) => { + editContactList(contactList); + }; + + const handleDelete = (contactList) => { + deleteContactList(contactList); + }; + + const renderRows = () => { + return rows.map((contactList) => { + return ( + + {contactList.name} + + {!readOnly ? ( + + + + + + handleEdit(contactList)}> + + + + handleDelete(contactList)} + > + + + + ) : null} + + ); + }); + }; + + return ( + + + + Nome + Contatos + {!readOnly ? Ações : null} + + + + {loading ? ( + + ) : ( + renderRows() + )} + +
+ ); +} + +ContactListsTable.propTypes = { + contactLists: PropTypes.array.isRequired, + showLoading: PropTypes.bool, + editContactList: PropTypes.func.isRequired, + deleteContactList: PropTypes.func.isRequired, +}; + +export default ContactListsTable; diff --git a/frontend/src/components/ContactModal/index.js b/frontend/src/components/ContactModal/index.js new file mode 100644 index 0000000..3b9c40b --- /dev/null +++ b/frontend/src/components/ContactModal/index.js @@ -0,0 +1,284 @@ +import React, { useState, useEffect, useRef } from "react"; + +import * as Yup from "yup"; +import { Formik, FieldArray, Form, Field } from "formik"; +import { toast } from "react-toastify"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green } from "@material-ui/core/colors"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import Typography from "@material-ui/core/Typography"; +import IconButton from "@material-ui/core/IconButton"; +import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +import { i18n } from "../../translate/i18n"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + flexWrap: "wrap", + }, + textField: { + marginRight: theme.spacing(1), + flex: 1, + }, + + extraAttr: { + display: "flex", + justifyContent: "center", + alignItems: "center", + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, +})); + +const ContactSchema = Yup.object().shape({ + name: Yup.string() + .min(2, "Too Short!") + .max(50, "Too Long!") + .required("Required"), + number: Yup.string().min(8, "Too Short!").max(50, "Too Long!"), + email: Yup.string().email("Invalid email"), +}); + +const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { + const classes = useStyles(); + const isMounted = useRef(true); + + const initialState = { + name: "", + number: "", + email: "", + }; + + const [contact, setContact] = useState(initialState); + + useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + useEffect(() => { + const fetchContact = async () => { + if (initialValues) { + setContact(prevState => { + return { ...prevState, ...initialValues }; + }); + } + + if (!contactId) return; + + try { + const { data } = await api.get(`/contacts/${contactId}`); + if (isMounted.current) { + console.log(data) + setContact(data); + } + } catch (err) { + toastError(err); + } + }; + + fetchContact(); + }, [contactId, open, initialValues]); + + const handleClose = () => { + onClose(); + setContact(initialState); + }; + + const handleSaveContact = async values => { + try { + if (contactId) { + await api.put(`/contacts/${contactId}`, values); + handleClose(); + } else { + const { data } = await api.post("/contacts", values); + if (onSave) { + onSave(data); + } + handleClose(); + } + toast.success(i18n.t("contactModal.success")); + } catch (err) { + toastError(err); + } + }; + + return ( +
+ + + {contactId + ? `${i18n.t("contactModal.title.edit")}` + : `${i18n.t("contactModal.title.add")}`} + + { + setTimeout(() => { + handleSaveContact(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ values, errors, touched, isSubmitting }) => ( +
+ + + {i18n.t("contactModal.form.mainInfo")} + + + +
+ +
+ + {i18n.t("contactModal.form.whatsapp")} {contact?.whatsapp ? contact?.whatsapp.name : ""} + + + {i18n.t("contactModal.form.extraInfo")} + + + + {({ push, remove }) => ( + <> + {values.extraInfo && + values.extraInfo.length > 0 && + values.extraInfo.map((info, index) => ( +
+ + + remove(index)} + > + + +
+ ))} +
+ +
+ + )} +
+
+ + + + +
+ )} +
+
+
+ ); +}; + +export default ContactModal; diff --git a/frontend/src/components/ContactNotes/index.js b/frontend/src/components/ContactNotes/index.js new file mode 100644 index 0000000..a7db83c --- /dev/null +++ b/frontend/src/components/ContactNotes/index.js @@ -0,0 +1,204 @@ +import React, { useState, useEffect } from 'react'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import List from '@material-ui/core/List'; +import { makeStyles } from '@material-ui/core/styles'; +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; + +import ContactNotesDialogListItem from '../ContactNotesDialogListItem'; +import ConfirmationModal from '../ConfirmationModal'; + +import { toast } from "react-toastify"; + +import { i18n } from "../../translate/i18n"; + +import ButtonWithSpinner from '../ButtonWithSpinner'; + +import useTicketNotes from '../../hooks/useTicketNotes'; +import { Grid } from '@material-ui/core'; + +const useStyles = makeStyles((theme) => ({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: '350px', + }, + }, + list: { + width: '100%', + maxWidth: '350px', + maxHeight: '200px', + backgroundColor: theme.palette.background.paper, + overflow: 'auto' + }, + inline: { + width: '100%' + } +})); + +const NoteSchema = Yup.object().shape({ + note: Yup.string() + .min(2, "Too Short!") + .required("Required") +}); +export function ContactNotes ({ ticket }) { + const { id: ticketId, contactId } = ticket + const classes = useStyles() + const [newNote, setNewNote] = useState({ note: "" }); + const [loading, setLoading] = useState(false) + const [showOnDeleteDialog, setShowOnDeleteDialog] = useState(false) + const [selectedNote, setSelectedNote] = useState({}) + const [notes, setNotes] = useState([]) + const { saveNote, deleteNote, listNotes } = useTicketNotes() + + useEffect(() => { + async function openAndFetchData () { + handleResetState() + await loadNotes() + } + openAndFetchData() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const handleResetState = () => { + setNewNote({ note: "" }) + setLoading(false) + } + + const handleChangeComment = (e) => { + setNewNote({ note: e.target.value }) + } + + const handleSave = async values => { + setLoading(true) + try { + await saveNote({ + ...values, + ticketId, + contactId + }) + await loadNotes() + setNewNote({ note: '' }) + toast.success('Observação adicionada com sucesso!') + } catch (e) { + toast.error(e) + } + setLoading(false) + } + + const handleOpenDialogDelete = (item) => { + setSelectedNote(item) + setShowOnDeleteDialog(true) + } + + const handleDelete = async () => { + setLoading(true) + try { + await deleteNote(selectedNote.id) + await loadNotes() + setSelectedNote({}) + toast.success('Observação excluída com sucesso!') + } catch (e) { + toast.error(e) + } + setLoading(false) + } + + const loadNotes = async () => { + setLoading(true) + try { + const notes = await listNotes({ ticketId, contactId }) + setNotes(notes) + } catch (e) { + toast.error(e) + } + setLoading(false) + } + + const renderNoteList = () => { + return notes.map((note) => { + return + }) + } + + return ( + <> + + Deseja realmente excluir este registro? + + { + setTimeout(() => { + handleSave(values); + actions.setSubmitting(false); + }, 400); + }} + > + + {({ touched, errors, setErrors }) => ( +
+ + + + + { notes.length > 0 && ( + + + { renderNoteList() } + + + ) } + + + + + + + + Salvar + + + + + +
+ )} +
+ + ); +} \ No newline at end of file diff --git a/frontend/src/components/ContactNotesDialog/index.js b/frontend/src/components/ContactNotesDialog/index.js new file mode 100644 index 0000000..d511256 --- /dev/null +++ b/frontend/src/components/ContactNotesDialog/index.js @@ -0,0 +1,206 @@ +import React, { useState, useEffect } from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import TextField from '@material-ui/core/TextField'; +import List from '@material-ui/core/List'; +import { makeStyles } from '@material-ui/core/styles'; +import * as Yup from "yup"; +import { Formik, Form, Field } from "formik"; + +import ContactNotesDialogListItem from '../ContactNotesDialogListItem'; +import ConfirmationModal from '../ConfirmationModal'; + +import { toast } from "react-toastify"; + +import { i18n } from "../../translate/i18n"; + +import ButtonWithSpinner from '../ButtonWithSpinner'; + +import useTicketNotes from '../../hooks/useTicketNotes'; + +const useStyles = makeStyles((theme) => ({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: '350px', + }, + }, + list: { + width: '100%', + maxWidth: '350px', + maxHeight: '200px', + backgroundColor: theme.palette.background.paper, + }, + inline: { + width: '100%' + } +})); + +const NoteSchema = Yup.object().shape({ + note: Yup.string() + .min(2, "Too Short!") + .required("Required") +}); + +export default function ContactNotesDialog ({ modalOpen, onClose, ticket }) { + const { id: ticketId, contactId } = ticket + const classes = useStyles() + const [open, setOpen] = useState(false); + const [newNote, setNewNote] = useState({ note: "" }); + const [loading, setLoading] = useState(false) + const [showOnDeleteDialog, setShowOnDeleteDialog] = useState(false) + const [selectedNote, setSelectedNote] = useState({}) + const [notes, setNotes] = useState([]) + const { saveNote, deleteNote, listNotes } = useTicketNotes() + + useEffect(() => { + async function openAndFetchData () { + if (modalOpen) { + setOpen(true) + handleResetState() + await loadNotes() + } + } + openAndFetchData() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [modalOpen]) + + const handleResetState = () => { + setNewNote({ note: "" }) + setLoading(false) + } + + const handleChangeComment = (e) => { + setNewNote({ note: e.target.value }) + } + + const handleClose = () => { + setOpen(false); + onClose() + }; + + const handleSave = async values => { + setLoading(true) + try { + await saveNote({ + ...values, + ticketId, + contactId + }) + await loadNotes() + setNewNote({ note: '' }) + toast.success('Observação adicionada com sucesso!') + } catch (e) { + toast.error(e) + } + setLoading(false) + } + + const handleOpenDialogDelete = (item) => { + setSelectedNote(item) + setShowOnDeleteDialog(true) + } + + const handleDelete = async () => { + setLoading(true) + try { + await deleteNote(selectedNote.id) + await loadNotes() + setSelectedNote({}) + toast.success('Observação excluída com sucesso!') + } catch (e) { + toast.error(e) + } + setLoading(false) + } + + const loadNotes = async () => { + setLoading(true) + try { + const notes = await listNotes({ ticketId, contactId }) + setNotes(notes) + } catch (e) { + toast.error(e) + } + setLoading(false) + } + + const renderNoteList = () => { + return notes.map((note) => { + return + }) + } + + return ( + <> + + Deseja realmente excluir este registro? + + + + { i18n.t("ticketOptionsMenu.appointmentsModal.title") } + + { + setTimeout(() => { + handleSave(values); + actions.setSubmitting(false); + }, 400); + }} + > + + {({ touched, errors }) => ( +
+ + + + + { renderNoteList() } + + + + + + Salvar + + +
+ )} +
+
+ + ); +} \ No newline at end of file diff --git a/frontend/src/components/ContactNotesDialogListItem/index.js b/frontend/src/components/ContactNotesDialogListItem/index.js new file mode 100644 index 0000000..2b45f71 --- /dev/null +++ b/frontend/src/components/ContactNotesDialogListItem/index.js @@ -0,0 +1,64 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import IconButton from '@material-ui/core/IconButton'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import Avatar from '@material-ui/core/Avatar'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; +import DeleteIcon from '@material-ui/icons/Delete'; +import moment from 'moment'; + +const useStyles = makeStyles((theme) => ({ + inline: { + width: '100%' + } +})); + +export default function ContactNotesDialogListItem (props) { + const { note, deleteItem } = props; + const classes = useStyles(); + + const handleDelete = (item) => { + deleteItem(item); + } + + return ( + + + + + + + {note.note} + + + } + secondary={ + <> + {note.user.name}, {moment(note.createdAt).format('DD/MM/YY HH:mm')} + + } + /> + + handleDelete(note)} edge="end" aria-label="delete"> + + + + + ) +} + +ContactNotesDialogListItem.propTypes = { + note: PropTypes.object.isRequired, + deleteItem: PropTypes.func.isRequired +} \ No newline at end of file diff --git a/frontend/src/components/ContactTag/index.js b/frontend/src/components/ContactTag/index.js new file mode 100644 index 0000000..b6c838d --- /dev/null +++ b/frontend/src/components/ContactTag/index.js @@ -0,0 +1,26 @@ +import { makeStyles } from "@material-ui/styles"; +import React from "react"; + +const useStyles = makeStyles(theme => ({ + tag: { + padding: "1px 5px", + borderRadius: "3px", + fontSize: "0.8em", + fontWeight: "bold", + color: "#FFF", + marginRight: "5px", + whiteSpace: "nowrap" + } +})); + +const ContactTag = ({ tag }) => { + const classes = useStyles(); + + return ( +
+ {tag.name.toUpperCase()} +
+ ) +} + +export default ContactTag; \ No newline at end of file diff --git a/frontend/src/components/CurrencyInput/index.js b/frontend/src/components/CurrencyInput/index.js new file mode 100644 index 0000000..4cdddc9 --- /dev/null +++ b/frontend/src/components/CurrencyInput/index.js @@ -0,0 +1,50 @@ +import React from 'react' +import PropTypes from 'prop-types' +import MaskedInput from 'react-text-mask' +import createNumberMask from 'text-mask-addons/dist/createNumberMask' + +const defaultMaskOptions = { + prefix: 'R$', + suffix: '', + includeThousandsSeparator: true, + thousandsSeparatorSymbol: '.', + allowDecimal: true, + decimalSymbol: ',', + decimalLimit: 2, // how many digits allowed after the decimal + integerLimit: 7, // limit length of integer numbers + allowNegative: false, + allowLeadingZeroes: false, +} + +const CurrencyInput = ({ maskOptions, ...inputProps }) => { + const currencyMask = createNumberMask({ + ...defaultMaskOptions, + ...maskOptions, + }) + + return +} + +CurrencyInput.defaultProps = { + inputMode: 'numeric', + maskOptions: {}, +} + +CurrencyInput.propTypes = { + inputmode: PropTypes.string, + maskOptions: PropTypes.shape({ + prefix: PropTypes.string, + suffix: PropTypes.string, + includeThousandsSeparator: PropTypes.bool, + thousandsSeparatorSymbol: PropTypes.string, + allowDecimal: PropTypes.bool, + decimalSymbol: PropTypes.string, + decimalLimit: PropTypes.string, + requireDecimal: PropTypes.bool, + allowNegative: PropTypes.bool, + allowLeadingZeroes: PropTypes.bool, + integerLimit: PropTypes.number, + }), +} + +export default CurrencyInput diff --git a/frontend/src/components/DarkMode/index.js b/frontend/src/components/DarkMode/index.js new file mode 100644 index 0000000..014be13 --- /dev/null +++ b/frontend/src/components/DarkMode/index.js @@ -0,0 +1,70 @@ +import React, { useState } from "react"; + +import { makeStyles } from "@material-ui/core/styles"; +import { CssBaseline, IconButton } from "@material-ui/core"; +import Brightness4Icon from "@material-ui/icons/Brightness4"; +import Brightness7Icon from "@material-ui/icons/Brightness7"; + +const useStyles = makeStyles((theme) => ({ + icons: { + color: "#fff", + }, + switch: { + color: "#fff", + }, + visible: { + display: "none", + }, + btnHeader: { + color: "#fff", + }, +})); + +const DarkMode = (props) => { + const classes = useStyles(); + + const [theme, setTheme] = useState("light"); + + const themeToggle = () => { + theme === "light" ? setTheme("dark") : setTheme("light"); + }; + + const handleClick = () => { + props.themeToggle(); + themeToggle(); + }; + + return ( + <> + {theme === "light" ? ( + <> + + + + + + ) : ( + <> + + + + + + )} + + ); +}; + +export default DarkMode; diff --git a/frontend/src/components/Dashboard/CardCounter.js b/frontend/src/components/Dashboard/CardCounter.js new file mode 100644 index 0000000..c8978a6 --- /dev/null +++ b/frontend/src/components/Dashboard/CardCounter.js @@ -0,0 +1,53 @@ +import React from "react"; + +import { Avatar, Card, CardHeader, Typography } from "@material-ui/core"; +import Skeleton from "@material-ui/lab/Skeleton"; + +import { makeStyles } from "@material-ui/core/styles"; +import { grey } from '@material-ui/core/colors'; + +const useStyles = makeStyles(theme => ({ + cardAvatar: { + fontSize: '55px', + color: grey[500], + backgroundColor: '#ffffff', + width: theme.spacing(7), + height: theme.spacing(7) + }, + cardTitle: { + fontSize: '18px', + color: theme.palette.text.primary + }, + cardSubtitle: { + color: grey[600], + fontSize: '14px' + } +})); + +export default function CardCounter(props) { + const { icon, title, value, loading } = props + const classes = useStyles(); + return ( !loading ? + + + {icon} + + } + title={ + + { title } + + } + subheader={ + + { value } + + } + /> + + : + ) + +} \ No newline at end of file diff --git a/frontend/src/components/Dashboard/TableAttendantsStatus.js b/frontend/src/components/Dashboard/TableAttendantsStatus.js new file mode 100644 index 0000000..ed748e4 --- /dev/null +++ b/frontend/src/components/Dashboard/TableAttendantsStatus.js @@ -0,0 +1,104 @@ +import React from "react"; + +import Paper from "@material-ui/core/Paper"; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Skeleton from "@material-ui/lab/Skeleton"; + +import { makeStyles } from "@material-ui/core/styles"; +import { green, red } from '@material-ui/core/colors'; + +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import ErrorIcon from '@material-ui/icons/Error'; +import moment from 'moment'; + +import Rating from '@material-ui/lab/Rating'; + +const useStyles = makeStyles(theme => ({ + on: { + color: green[600], + fontSize: '20px' + }, + off: { + color: red[600], + fontSize: '20px' + }, + pointer: { + cursor: "pointer" + } +})); + +export function RatingBox ({ rating }) { + const ratingTrunc = rating === null ? 0 : Math.trunc(rating); + return +} + +export default function TableAttendantsStatus(props) { + const { loading, attendants } = props + const classes = useStyles(); + + function renderList () { + return attendants.map((a, k) => ( + + {a.name} + + + + {formatTime(a.avgSupportTime, 2)} + + { a.online ? + + : + } + + + )) + } + + function formatTime(minutes){ + return moment().startOf('day').add(minutes, 'minutes').format('HH[h] mm[m]'); + } + + return ( !loading ? + + + + + Nome + Avaliações + T.M. de Atendimento + Status (Atual) + + + + { renderList() } + {/* + Nome 4 + 10 + 10 minutos + + + + + + Nome 5 + 10 + 10 minutos + + + + */} + +
+
+ : + ) +} \ No newline at end of file diff --git a/frontend/src/components/Dialog/index.js b/frontend/src/components/Dialog/index.js new file mode 100644 index 0000000..23bcb28 --- /dev/null +++ b/frontend/src/components/Dialog/index.js @@ -0,0 +1,34 @@ +import React, { useState, useEffect } from 'react'; +import CoreDialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +function Dialog ({ title, modalOpen, onClose, children }) { + const [open, setOpen] = useState(false); + + useEffect(() => { + setOpen(modalOpen) + }, [modalOpen]) + + const handleClose = () => { + setOpen(false); + onClose() + }; + + return ( + <> + + + {title} + + {children} + + + ); +} + +export default Dialog; \ No newline at end of file diff --git a/frontend/src/components/FileModal/index.js b/frontend/src/components/FileModal/index.js new file mode 100644 index 0000000..87b7404 --- /dev/null +++ b/frontend/src/components/FileModal/index.js @@ -0,0 +1,350 @@ +import React, { useState, useEffect, useContext } from "react"; + +import * as Yup from "yup"; +import { + Formik, + Form, + Field, + FieldArray +} from "formik"; +import { toast } from "react-toastify"; + +import { + Box, + Button, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Divider, + Grid, + makeStyles, + TextField +} from "@material-ui/core"; +import IconButton from "@material-ui/core/IconButton"; +import Typography from "@material-ui/core/Typography"; +import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"; +import AttachFileIcon from "@material-ui/icons/AttachFile"; + +import { green } from "@material-ui/core/colors"; + +import { i18n } from "../../translate/i18n"; + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { AuthContext } from "../../context/Auth/AuthContext"; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + flexWrap: "wrap", + gap: 4 + }, + multFieldLine: { + display: "flex", + "& > *:not(:last-child)": { + marginRight: theme.spacing(1), + }, + }, + textField: { + marginRight: theme.spacing(1), + flex: 1, + }, + + extraAttr: { + display: "flex", + justifyContent: "center", + alignItems: "center", + }, + + btnWrapper: { + position: "relative", + }, + + buttonProgress: { + color: green[500], + position: "absolute", + top: "50%", + left: "50%", + marginTop: -12, + marginLeft: -12, + }, + formControl: { + margin: theme.spacing(1), + minWidth: 2000, + }, + colorAdorment: { + width: 20, + height: 20, + }, +})); + +const FileListSchema = Yup.object().shape({ + name: Yup.string() + .min(3, "nome muito curto") + .required("Obrigatório"), + message: Yup.string() + .required("Obrigatório") +}); + +const FilesModal = ({ open, onClose, fileListId, reload }) => { + const classes = useStyles(); + const { user } = useContext(AuthContext); + const [ files, setFiles ] = useState([]); + const [selectedFileNames, setSelectedFileNames] = useState([]); + + + const initialState = { + name: "", + message: "", + options: [{ name: "", path:"", mediaType:"" }], + }; + + const [fileList, setFileList] = useState(initialState); + + useEffect(() => { + try { + (async () => { + if (!fileListId) return; + + const { data } = await api.get(`/files/${fileListId}`); + setFileList(data); + })() + } catch (err) { + toastError(err); + } + }, [fileListId, open]); + + const handleClose = () => { + setFileList(initialState); + setFiles([]); + onClose(); + }; + + const handleSaveFileList = async (values) => { + + const uploadFiles = async (options, filesOptions, id) => { + const formData = new FormData(); + formData.append("fileId", id); + formData.append("typeArch", "fileList") + filesOptions.forEach((fileOption, index) => { + if (fileOption.file) { + formData.append("files", fileOption.file); + formData.append("mediaType", fileOption.file.type) + formData.append("name", options[index].name); + formData.append("id", options[index].id); + } + }); + + try { + const { data } = await api.post(`/files/uploadList/${id}`, formData); + setFiles([]); + return data; + } catch (err) { + toastError(err); + } + return null; + } + + const fileData = { ...values, userId: user.id }; + + try { + if (fileListId) { + const { data } = await api.put(`/files/${fileListId}`, fileData) + if (data.options.length > 0) + + uploadFiles(data.options, values.options, fileListId) + } else { + const { data } = await api.post("/files", fileData); + if (data.options.length > 0) + uploadFiles(data.options, values.options, data.id) + } + toast.success(i18n.t("fileModal.success")); + if (typeof reload == 'function') { + reload(); + } + } catch (err) { + toastError(err); + } + handleClose(); + }; + + return ( +
+ + + {(fileListId ? `${i18n.t("fileModal.title.edit")}` : `${i18n.t("fileModal.title.add")}`)} + + { + setTimeout(() => { + handleSaveFileList(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ touched, errors, isSubmitting, values }) => ( +
+ +
+ +
+
+
+ +
+ + {i18n.t("fileModal.form.fileOptions")} + + + + {({ push, remove }) => ( + <> + {values.options && + values.options.length > 0 && + values.options.map((info, index) => ( +
+ + + + + + { + const selectedFile = e.target.files[0]; + const updatedOptions = [...values.options]; + updatedOptions[index].file = selectedFile; + + setFiles('options', updatedOptions); + + // Atualize a lista selectedFileNames para o campo específico + const updatedFileNames = [...selectedFileNames]; + updatedFileNames[index] = selectedFile ? selectedFile.name : ''; + setSelectedFileNames(updatedFileNames); + }} + style={{ display: 'none' }} + name={`options[${index}].file`} + id={`file-upload-${index}`} + /> + + remove(index)} + > + + + + + {info.path? info.path : selectedFileNames[index]} + + +
+ + ))} +
+ +
+ + )} +
+
+ + + + +
+ )} +
+
+
+ ); +}; + +export default FilesModal; \ No newline at end of file diff --git a/frontend/src/components/FormFields/CheckboxField.js b/frontend/src/components/FormFields/CheckboxField.js new file mode 100644 index 0000000..c7688ff --- /dev/null +++ b/frontend/src/components/FormFields/CheckboxField.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { at } from 'lodash'; +import { useField } from 'formik'; +import { + Checkbox, + FormControl, + FormControlLabel, + FormHelperText +} from '@material-ui/core'; + +export default function CheckboxField(props) { + const { label, ...rest } = props; + const [field, meta, helper] = useField(props); + const { setValue } = helper; + + function _renderHelperText() { + const [touched, error] = at(meta, 'touched', 'error'); + if (touched && error) { + return {error}; + } + } + + function _onChange(e) { + setValue(e.target.checked); + } + + return ( + + } + label={label} + /> + {_renderHelperText()} + + ); +} diff --git a/frontend/src/components/FormFields/DatePickerField.js b/frontend/src/components/FormFields/DatePickerField.js new file mode 100644 index 0000000..503396e --- /dev/null +++ b/frontend/src/components/FormFields/DatePickerField.js @@ -0,0 +1,54 @@ +import React, { useState, useEffect } from 'react'; +import { useField } from 'formik'; +import Grid from '@material-ui/core/Grid'; +import { + MuiPickersUtilsProvider, + KeyboardDatePicker +} from '@material-ui/pickers'; +import DateFnsUtils from '@date-io/date-fns'; + +export default function DatePickerField(props) { + const [field, meta, helper] = useField(props); + const { touched, error } = meta; + const { setValue } = helper; + const isError = touched && error && true; + const { value } = field; + const [selectedDate, setSelectedDate] = useState(null); + + useEffect(() => { + if (value) { + const date = new Date(value); + setSelectedDate(date); + } + }, [value]); + + function _onChange(date) { + if (date) { + setSelectedDate(date); + try { + const ISODateString = date.toISOString(); + setValue(ISODateString); + } catch (error) { + setValue(date); + } + } else { + setValue(date); + } + } + + return ( + + + + + + ); +} diff --git a/frontend/src/components/FormFields/InputField.js b/frontend/src/components/FormFields/InputField.js new file mode 100644 index 0000000..ecc68b2 --- /dev/null +++ b/frontend/src/components/FormFields/InputField.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { at } from 'lodash'; +import { useField } from 'formik'; +import { TextField } from '@material-ui/core'; + +export default function InputField(props) { + const { errorText, ...rest } = props; + const [field, meta] = useField(props); + + function _renderHelperText() { + const [touched, error] = at(meta, 'touched', 'error'); + if (touched && error) { + return error; + } + } + + return ( + + ); +} diff --git a/frontend/src/components/FormFields/SelectField.js b/frontend/src/components/FormFields/SelectField.js new file mode 100644 index 0000000..35ca7e2 --- /dev/null +++ b/frontend/src/components/FormFields/SelectField.js @@ -0,0 +1,48 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { at } from 'lodash'; +import { useField } from 'formik'; +import { + InputLabel, + FormControl, + Select, + MenuItem, + FormHelperText +} from '@material-ui/core'; + +function SelectField(props) { + const { label, data, ...rest } = props; + const [field, meta] = useField(props); + const { value: selectedValue } = field; + const [touched, error] = at(meta, 'touched', 'error'); + const isError = touched && error && true; + function _renderHelperText() { + if (isError) { + return {error}; + } + } + + return ( + + {label} + + {_renderHelperText()} + + ); +} + +SelectField.defaultProps = { + data: [] +}; + +SelectField.propTypes = { + data: PropTypes.array.isRequired +}; + +export default SelectField; diff --git a/frontend/src/components/FormFields/index.js b/frontend/src/components/FormFields/index.js new file mode 100644 index 0000000..9fa1775 --- /dev/null +++ b/frontend/src/components/FormFields/index.js @@ -0,0 +1,5 @@ +import InputField from './InputField'; +import CheckboxField from './CheckboxField'; +import SelectField from './SelectField'; +import DatePickerField from './DatePickerField'; +export { InputField, CheckboxField, SelectField, DatePickerField }; diff --git a/frontend/src/components/HelpsManager/index.js b/frontend/src/components/HelpsManager/index.js new file mode 100644 index 0000000..5e7c2ac --- /dev/null +++ b/frontend/src/components/HelpsManager/index.js @@ -0,0 +1,290 @@ +import React, { useState, useEffect } from "react"; +import { + makeStyles, + Paper, + Grid, + TextField, + Table, + TableHead, + TableBody, + TableCell, + TableRow, + IconButton +} from "@material-ui/core"; +import { Formik, Form, Field } from 'formik'; +import ButtonWithSpinner from "../ButtonWithSpinner"; +import ConfirmationModal from "../ConfirmationModal"; + +import { Edit as EditIcon } from "@material-ui/icons"; + +import { toast } from "react-toastify"; +import useHelps from "../../hooks/useHelps"; + + +const useStyles = makeStyles(theme => ({ + root: { + width: '100%' + }, + mainPaper: { + width: '100%', + flex: 1, + padding: theme.spacing(2) + }, + fullWidth: { + width: '100%' + }, + tableContainer: { + width: '100%', + overflowX: "scroll", + ...theme.scrollbarStyles + }, + textfield: { + width: '100%' + }, + textRight: { + textAlign: 'right' + }, + row: { + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2) + }, + control: { + paddingRight: theme.spacing(1), + paddingLeft: theme.spacing(1) + }, + buttonContainer: { + textAlign: 'right', + padding: theme.spacing(1) + } +})); + +export function HelpManagerForm (props) { + const { onSubmit, onDelete, onCancel, initialValue, loading } = props; + const classes = useStyles() + + const [record, setRecord] = useState(initialValue); + + useEffect(() => { + setRecord(initialValue) + }, [initialValue]) + + const handleSubmit = async(data) => { + onSubmit(data) + } + + return ( + + setTimeout(() => { + handleSubmit(values) + resetForm() + }, 500) + } + > + {(values) => ( +
+ + + + + + + + + + + + onCancel()} variant="contained"> + Limpar + + + { record.id !== undefined ? ( + + onDelete(record)} variant="contained" color="secondary"> + Excluir + + + ) : null} + + + Salvar + + + +
+ )} +
+ ) +} + +export function HelpsManagerGrid (props) { + const { records, onSelect } = props + const classes = useStyles() + + return ( + + + + + # + Título + Descrição + Vídeo + + + + {records.map((row) => ( + + + onSelect(row)} aria-label="delete"> + + + + {row.title || '-'} + {row.description || '-'} + {row.video || '-'} + + ))} + +
+
+ ) +} + +export default function HelpsManager () { + const classes = useStyles() + const { list, save, update, remove } = useHelps() + + const [showConfirmDialog, setShowConfirmDialog] = useState(false) + const [loading, setLoading] = useState(false) + const [records, setRecords] = useState([]) + const [record, setRecord] = useState({ + title: '', + description: '', + video: '' + }) + + useEffect(() => { + async function fetchData () { + await loadHelps() + } + fetchData() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const loadHelps = async () => { + setLoading(true) + try { + const helpList = await list() + setRecords(helpList) + } catch (e) { + toast.error('Não foi possível carregar a lista de registros') + } + setLoading(false) + } + + const handleSubmit = async (data) => { + setLoading(true) + try { + if (data.id !== undefined) { + await update(data) + } else { + await save(data) + } + await loadHelps() + handleCancel() + toast.success('Operação realizada com sucesso!') + } catch (e) { + toast.error('Não foi possível realizar a operação. Verifique se já existe uma helpo com o mesmo nome ou se os campos foram preenchidos corretamente') + } + setLoading(false) + } + + const handleDelete = async () => { + setLoading(true) + try { + await remove(record.id) + await loadHelps() + handleCancel() + toast.success('Operação realizada com sucesso!') + } catch (e) { + toast.error('Não foi possível realizar a operação') + } + setLoading(false) + } + + const handleOpenDeleteDialog = () => { + setShowConfirmDialog(true) + } + + const handleCancel = () => { + setRecord({ + title: '', + description: '', + video: '' + }) + } + + const handleSelect = (data) => { + setRecord({ + id: data.id, + title: data.title || '', + description: data.description || '', + video: data.video || '' + }) + } + + return ( + + + + + + + + + + setShowConfirmDialog(false)} + onConfirm={() => handleDelete()} + > + Deseja realmente excluir esse registro? + + + ) +} \ No newline at end of file diff --git a/frontend/src/components/LocationPreview/index.js b/frontend/src/components/LocationPreview/index.js new file mode 100644 index 0000000..c7f7b5e --- /dev/null +++ b/frontend/src/components/LocationPreview/index.js @@ -0,0 +1,50 @@ +import React, { useEffect } from 'react'; +import toastError from "../../errors/toastError"; + +import { Button, Divider, Typography} from "@material-ui/core"; + +const LocationPreview = ({ image, link, description }) => { + useEffect(() => {}, [image, link, description]); + + const handleLocation = async() => { + try { + window.open(link); + } catch (err) { + toastError(err); + } + } + + return ( + <> +
+
+
+ loc +
+ { description && ( +
+ +
') }}>
+
+
+ )} +
+
+ + +
+
+
+ + ); + +}; + +export default LocationPreview; \ No newline at end of file diff --git a/frontend/src/components/MainContainer/index.js b/frontend/src/components/MainContainer/index.js new file mode 100644 index 0000000..24b7b9a --- /dev/null +++ b/frontend/src/components/MainContainer/index.js @@ -0,0 +1,31 @@ +import React from "react"; + +import { makeStyles } from "@material-ui/core/styles"; +import Container from "@material-ui/core/Container"; + +const useStyles = makeStyles(theme => ({ + mainContainer: { + flex: 1, + padding: theme.spacing(2), + height: `calc(100% - 48px)`, + }, + + contentWrapper: { + height: "100%", + overflowY: "hidden", + display: "flex", + flexDirection: "column", + }, +})); + +const MainContainer = ({ children }) => { + const classes = useStyles(); + + return ( + +
{children}
+
+ ); +}; + +export default MainContainer; diff --git a/frontend/src/components/MainHeader/index.js b/frontend/src/components/MainHeader/index.js new file mode 100644 index 0000000..46fa8ab --- /dev/null +++ b/frontend/src/components/MainHeader/index.js @@ -0,0 +1,19 @@ +import React from "react"; + +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles(theme => ({ + contactsHeader: { + display: "flex", + alignItems: "center", + padding: "0px 6px 6px 6px", + }, +})); + +const MainHeader = ({ children }) => { + const classes = useStyles(); + + return
{children}
; +}; + +export default MainHeader; diff --git a/frontend/src/components/MainHeaderButtonsWrapper/index.js b/frontend/src/components/MainHeaderButtonsWrapper/index.js new file mode 100644 index 0000000..ed5887c --- /dev/null +++ b/frontend/src/components/MainHeaderButtonsWrapper/index.js @@ -0,0 +1,21 @@ +import React from "react"; + +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles(theme => ({ + MainHeaderButtonsWrapper: { + flex: "none", + marginLeft: "auto", + "& > *": { + margin: theme.spacing(1), + }, + }, +})); + +const MainHeaderButtonsWrapper = ({ children }) => { + const classes = useStyles(); + + return
{children}
; +}; + +export default MainHeaderButtonsWrapper; diff --git a/frontend/src/components/MarkdownWrapper/index.js b/frontend/src/components/MarkdownWrapper/index.js new file mode 100644 index 0000000..64764b2 --- /dev/null +++ b/frontend/src/components/MarkdownWrapper/index.js @@ -0,0 +1,186 @@ +import React from "react"; +import Markdown from "markdown-to-jsx"; + +const elements = [ + "a", + "abbr", + "address", + "area", + "article", + "aside", + "audio", + "b", + "base", + "bdi", + "bdo", + "big", + "blockquote", + "body", + "br", + "button", + "canvas", + "caption", + "cite", + "code", + "col", + "colgroup", + "data", + "datalist", + "dd", + "del", + "details", + "dfn", + "dialog", + "div", + "dl", + "dt", + "em", + "embed", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "head", + "header", + "hgroup", + "hr", + "html", + "i", + "iframe", + "img", + "input", + "ins", + "kbd", + "keygen", + "label", + "legend", + "li", + "link", + "main", + "map", + "mark", + "marquee", + "menu", + "menuitem", + "meta", + "meter", + "nav", + "noscript", + "object", + "ol", + "optgroup", + "option", + "output", + "p", + "param", + "picture", + "pre", + "progress", + "q", + "rp", + "rt", + "ruby", + "s", + "samp", + "script", + "section", + "select", + "small", + "source", + "span", + "strong", + "style", + "sub", + "summary", + "sup", + "table", + "tbody", + "td", + "textarea", + "tfoot", + "th", + "thead", + "time", + "title", + "tr", + "track", + "u", + "ul", + "var", + "video", + "wbr", + + // SVG + "circle", + "clipPath", + "defs", + "ellipse", + "foreignObject", + "g", + "image", + "line", + "linearGradient", + "marker", + "mask", + "path", + "pattern", + "polygon", + "polyline", + "radialGradient", + "rect", + "stop", + "svg", + "text", + "tspan", +]; + +const allowedElements = ["a", "b", "strong", "em", "u", "code", "del"]; + +const CustomLink = ({ children, ...props }) => ( + + {children} + +); + +const MarkdownWrapper = ({ children }) => { + const boldRegex = /\*(.*?)\*/g; + const tildaRegex = /~(.*?)~/g; + + if (children && boldRegex.test(children)) { + children = children.replace(boldRegex, "**$1**"); + } + if (children && tildaRegex.test(children)) { + children = children.replace(tildaRegex, "~~$1~~"); + } + + const options = React.useMemo(() => { + const markdownOptions = { + disableParsingRawHTML: true, + forceInline: true, + overrides: { + a: { component: CustomLink }, + }, + }; + + elements.forEach(element => { + if (!allowedElements.includes(element)) { + markdownOptions.overrides[element] = el => el.children || null; + } + }); + + return markdownOptions; + }, []); + + if (!children) return null; + + return {children}; +}; + +export default MarkdownWrapper; diff --git a/frontend/src/components/MessageInput/RecordingTimer.js b/frontend/src/components/MessageInput/RecordingTimer.js new file mode 100644 index 0000000..108cf50 --- /dev/null +++ b/frontend/src/components/MessageInput/RecordingTimer.js @@ -0,0 +1,48 @@ +import React, { useState, useEffect } from "react"; +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles(theme => ({ + timerBox: { + display: "flex", + marginLeft: 10, + marginRight: 10, + alignItems: "center", + }, +})); + +const RecordingTimer = () => { + const classes = useStyles(); + const initialState = { + minutes: 0, + seconds: 0, + }; + const [timer, setTimer] = useState(initialState); + + useEffect(() => { + const interval = setInterval( + () => + setTimer(prevState => { + if (prevState.seconds === 59) { + return { ...prevState, minutes: prevState.minutes + 1, seconds: 0 }; + } + return { ...prevState, seconds: prevState.seconds + 1 }; + }), + 1000 + ); + return () => { + clearInterval(interval); + }; + }, []); + + const addZero = n => { + return n < 10 ? "0" + n : n; + }; + + return ( +
+ {`${addZero(timer.minutes)}:${addZero(timer.seconds)}`} +
+ ); +}; + +export default RecordingTimer; diff --git a/frontend/src/components/MessageInput/index.js b/frontend/src/components/MessageInput/index.js new file mode 100644 index 0000000..874b332 --- /dev/null +++ b/frontend/src/components/MessageInput/index.js @@ -0,0 +1,513 @@ +import React, { useState, useEffect, useContext, useRef } from "react"; +import "emoji-mart/css/emoji-mart.css"; +import { useParams } from "react-router-dom"; +import { Picker } from "emoji-mart"; +import MicRecorder from "mic-recorder-to-mp3"; +import clsx from "clsx"; + +import { makeStyles } from "@material-ui/core/styles"; +import Paper from "@material-ui/core/Paper"; +import InputBase from "@material-ui/core/InputBase"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import { green } from "@material-ui/core/colors"; +import AttachFileIcon from "@material-ui/icons/AttachFile"; +import IconButton from "@material-ui/core/IconButton"; +import MoodIcon from "@material-ui/icons/Mood"; +import SendIcon from "@material-ui/icons/Send"; +import CancelIcon from "@material-ui/icons/Cancel"; +import ClearIcon from "@material-ui/icons/Clear"; +import MicIcon from "@material-ui/icons/Mic"; +import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline"; +import HighlightOffIcon from "@material-ui/icons/HighlightOff"; +import { FormControlLabel, Switch } from "@material-ui/core"; + +import { i18n } from "../../translate/i18n"; +import api from "../../services/api"; +import RecordingTimer from "./RecordingTimer"; +import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext"; +import { AuthContext } from "../../context/Auth/AuthContext"; +import { useLocalStorage } from "../../hooks/useLocalStorage"; +import toastError from "../../errors/toastError"; + +const Mp3Recorder = new MicRecorder({ bitRate: 128 }); + +const useStyles = makeStyles(theme => ({ + mainWrapper: { + backgroundColor: theme.palette.bordabox, //DARK MODE PLW DESIGN// + display: "flex", + flexDirection: "column", + alignItems: "center", + borderTop: "1px solid rgba(0, 0, 0, 0.12)", + }, + + newMessageBox: { + background: "#eee", + width: "100%", + display: "flex", + padding: "7px", + alignItems: "center", + }, + + messageInputWrapper: { + padding: 6, + marginRight: 7, + background: "#fff", + display: "flex", + borderRadius: 20, + flex: 1, + }, + + messageInput: { + paddingLeft: 10, + flex: 1, + border: "none", + }, + + sendMessageIcons: { + color: "grey", + }, + + uploadInput: { + display: "none", + }, + + viewMediaInputWrapper: { + display: "flex", + padding: "10px 13px", + position: "relative", + justifyContent: "space-between", + alignItems: "center", + backgroundColor: "#eee", + borderTop: "1px solid rgba(0, 0, 0, 0.12)", + }, + + emojiBox: { + position: "absolute", + bottom: 63, + width: 40, + borderTop: "1px solid #e8e8e8", + }, + + circleLoading: { + color: green[500], + opacity: "70%", + position: "absolute", + top: "20%", + left: "50%", + marginLeft: -12, + }, + + audioLoading: { + color: green[500], + opacity: "70%", + }, + + recorderWrapper: { + display: "flex", + alignItems: "center", + alignContent: "middle", + }, + + cancelAudioIcon: { + color: "red", + }, + + sendAudioIcon: { + color: "green", + }, + + replyginMsgWrapper: { + display: "flex", + width: "100%", + alignItems: "center", + justifyContent: "center", + paddingTop: 8, + paddingLeft: 73, + paddingRight: 7, + }, + + replyginMsgContainer: { + flex: 1, + marginRight: 5, + overflowY: "hidden", + backgroundColor: "rgba(0, 0, 0, 0.05)", + borderRadius: "7.5px", + display: "flex", + position: "relative", + }, + + replyginMsgBody: { + padding: 10, + height: "auto", + display: "block", + whiteSpace: "pre-wrap", + overflow: "hidden", + }, + + replyginContactMsgSideColor: { + flex: "none", + width: "4px", + backgroundColor: "#35cd96", + }, + + replyginSelfMsgSideColor: { + flex: "none", + width: "4px", + backgroundColor: "#6bcbef", + }, + + messageContactName: { + display: "flex", + color: "#6bcbef", + fontWeight: 500, + }, +})); + +const MessageInput = ({ ticketStatus }) => { + const classes = useStyles(); + const { ticketId } = useParams(); + + const [medias, setMedias] = useState([]); + const [inputMessage, setInputMessage] = useState(""); + const [showEmoji, setShowEmoji] = useState(false); + const [loading, setLoading] = useState(false); + const [recording, setRecording] = useState(false); + const inputRef = useRef(); + const { setReplyingMessage, replyingMessage } = useContext( + ReplyMessageContext + ); + const { user } = useContext(AuthContext); + + const [signMessage, setSignMessage] = useLocalStorage("signOption", true); + + useEffect(() => { + inputRef.current.focus(); + }, [replyingMessage]); + + useEffect(() => { + inputRef.current.focus(); + return () => { + setInputMessage(""); + setShowEmoji(false); + setMedias([]); + setReplyingMessage(null); + }; + }, [ticketId, setReplyingMessage]); + + const handleChangeInput = e => { + setInputMessage(e.target.value); + }; + + const handleAddEmoji = e => { + let emoji = e.native; + setInputMessage(prevState => prevState + emoji); + }; + + const handleChangeMedias = e => { + if (!e.target.files) { + return; + } + + const selectedMedias = Array.from(e.target.files); + setMedias(selectedMedias); + }; + + const handleInputPaste = e => { + if (e.clipboardData.files[0]) { + setMedias([e.clipboardData.files[0]]); + } + }; + + const handleUploadMedia = async e => { + setLoading(true); + e.preventDefault(); + + const formData = new FormData(); + formData.append("fromMe", true); + medias.forEach(media => { + formData.append("medias", media); + formData.append("body", media.name); + }); + + try { + await api.post(`/messages/${ticketId}`, formData); + } catch (err) { + toastError(err); + } + + setLoading(false); + setMedias([]); + }; + + const handleSendMessage = async () => { + if (inputMessage.trim() === "") return; + setLoading(true); + + const message = { + read: 1, + fromMe: true, + mediaUrl: "", + body: signMessage + ? `*${user?.name}:*\n${inputMessage.trim()}` + : inputMessage.trim(), + quotedMsg: replyingMessage, + }; + try { + await api.post(`/messages/${ticketId}`, message); + } catch (err) { + toastError(err); + } + + setInputMessage(""); + setShowEmoji(false); + setLoading(false); + setReplyingMessage(null); + }; + + const handleStartRecording = async () => { + setLoading(true); + try { + await navigator.mediaDevices.getUserMedia({ audio: true }); + await Mp3Recorder.start(); + setRecording(true); + setLoading(false); + } catch (err) { + toastError(err); + setLoading(false); + } + }; + + const handleUploadAudio = async () => { + setLoading(true); + try { + const [, blob] = await Mp3Recorder.stop().getMp3(); + if (blob.size < 10000) { + setLoading(false); + setRecording(false); + return; + } + + const formData = new FormData(); + const filename = `${new Date().getTime()}.mp3`; + formData.append("medias", blob, filename); + formData.append("body", filename); + formData.append("fromMe", true); + + await api.post(`/messages/${ticketId}`, formData); + } catch (err) { + toastError(err); + } + + setRecording(false); + setLoading(false); + }; + + const handleCancelAudio = async () => { + try { + await Mp3Recorder.stop().getMp3(); + setRecording(false); + } catch (err) { + toastError(err); + } + }; + + const renderReplyingMessage = message => { + return ( +
+
+ +
+ {!message.fromMe && ( + + {message.contact?.name} + + )} + {message.body} +
+
+ setReplyingMessage(null)} + > + + +
+ ); + }; + + if (medias.length > 0) + return ( + + setMedias([])} + > + + + + {loading ? ( +
+ +
+ ) : ( + + {medias[0]?.name} + {/* */} + + )} + + + +
+ ); + else { + return ( + + {replyingMessage && renderReplyingMessage(replyingMessage)} +
+ setShowEmoji(prevState => !prevState)} + > + + + {showEmoji ? ( +
+ +
+ ) : null} + + + + { + setSignMessage(e.target.checked); + }} + name="showAllTickets" + color="primary" + /> + } + /> +
+ { + input && input.focus(); + input && (inputRef.current = input); + }} + className={classes.messageInput} + placeholder={ + ticketStatus === "open" + ? i18n.t("messagesInput.placeholderOpen") + : i18n.t("messagesInput.placeholderClosed") + } + multiline + maxRows={5} + value={inputMessage} + onChange={handleChangeInput} + disabled={recording || loading || ticketStatus !== "open"} + onPaste={e => { + ticketStatus === "open" && handleInputPaste(e); + }} + onKeyPress={e => { + if (loading || e.shiftKey) return; + else if (e.key === "Enter") { + handleSendMessage(); + } + }} + /> +
+ {inputMessage ? ( + + + + ) : recording ? ( +
+ + + + {loading ? ( +
+ +
+ ) : ( + + )} + + + + +
+ ) : ( + + + + )} +
+
+ ); + } +}; + +export default MessageInput; \ No newline at end of file diff --git a/frontend/src/components/MessageInputCustom/RecordingTimer.js b/frontend/src/components/MessageInputCustom/RecordingTimer.js new file mode 100644 index 0000000..108cf50 --- /dev/null +++ b/frontend/src/components/MessageInputCustom/RecordingTimer.js @@ -0,0 +1,48 @@ +import React, { useState, useEffect } from "react"; +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles(theme => ({ + timerBox: { + display: "flex", + marginLeft: 10, + marginRight: 10, + alignItems: "center", + }, +})); + +const RecordingTimer = () => { + const classes = useStyles(); + const initialState = { + minutes: 0, + seconds: 0, + }; + const [timer, setTimer] = useState(initialState); + + useEffect(() => { + const interval = setInterval( + () => + setTimer(prevState => { + if (prevState.seconds === 59) { + return { ...prevState, minutes: prevState.minutes + 1, seconds: 0 }; + } + return { ...prevState, seconds: prevState.seconds + 1 }; + }), + 1000 + ); + return () => { + clearInterval(interval); + }; + }, []); + + const addZero = n => { + return n < 10 ? "0" + n : n; + }; + + return ( +
+ {`${addZero(timer.minutes)}:${addZero(timer.seconds)}`} +
+ ); +}; + +export default RecordingTimer; diff --git a/frontend/src/components/MessageInputCustom/index.js b/frontend/src/components/MessageInputCustom/index.js new file mode 100644 index 0000000..deb144f --- /dev/null +++ b/frontend/src/components/MessageInputCustom/index.js @@ -0,0 +1,773 @@ +import React, { useState, useEffect, useContext, useRef } from "react"; +import withWidth, { isWidthUp } from "@material-ui/core/withWidth"; +import "emoji-mart/css/emoji-mart.css"; +import { Picker } from "emoji-mart"; +import MicRecorder from "mic-recorder-to-mp3"; +import clsx from "clsx"; +import { isNil } from "lodash"; + +import { makeStyles } from "@material-ui/core/styles"; +import Paper from "@material-ui/core/Paper"; +import InputBase from "@material-ui/core/InputBase"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import { green } from "@material-ui/core/colors"; +import AttachFileIcon from "@material-ui/icons/AttachFile"; +import IconButton from "@material-ui/core/IconButton"; +import MoodIcon from "@material-ui/icons/Mood"; +import SendIcon from "@material-ui/icons/Send"; +import CancelIcon from "@material-ui/icons/Cancel"; +import ClearIcon from "@material-ui/icons/Clear"; +import MicIcon from "@material-ui/icons/Mic"; +import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline"; +import HighlightOffIcon from "@material-ui/icons/HighlightOff"; +import { FormControlLabel, Switch } from "@material-ui/core"; +import Autocomplete from "@material-ui/lab/Autocomplete"; +import { isString, isEmpty, isObject, has } from "lodash"; + +import { i18n } from "../../translate/i18n"; +import api from "../../services/api"; +import axios from "axios"; + +import RecordingTimer from "./RecordingTimer"; +import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext"; +import { AuthContext } from "../../context/Auth/AuthContext"; +import { useLocalStorage } from "../../hooks/useLocalStorage"; +import toastError from "../../errors/toastError"; + +import useQuickMessages from "../../hooks/useQuickMessages"; + +const Mp3Recorder = new MicRecorder({ bitRate: 128 }); + +const useStyles = makeStyles((theme) => ({ + mainWrapper: { + backgroundColor: theme.palette.bordabox, //DARK MODE PLW DESIGN// + display: "flex", + flexDirection: "column", + alignItems: "center", + borderTop: "1px solid rgba(0, 0, 0, 0.12)", + }, + + newMessageBox: { + backgroundColor: theme.palette.newmessagebox, //DARK MODE PLW DESIGN// + width: "100%", + display: "flex", + padding: "7px", + alignItems: "center", + }, + + messageInputWrapper: { + padding: 6, + marginRight: 7, + backgroundColor: theme.palette.inputdigita, //DARK MODE PLW DESIGN// + display: "flex", + borderRadius: 20, + flex: 1, + }, + + messageInput: { + paddingLeft: 10, + flex: 1, + border: "none", + }, + + sendMessageIcons: { + color: "grey", + }, + + uploadInput: { + display: "none", + }, + + viewMediaInputWrapper: { + display: "flex", + padding: "10px 13px", + position: "relative", + justifyContent: "space-between", + alignItems: "center", + backgroundColor: "#eee", + borderTop: "1px solid rgba(0, 0, 0, 0.12)", + }, + + emojiBox: { + position: "absolute", + bottom: 63, + width: 40, + borderTop: "1px solid #e8e8e8", + }, + + circleLoading: { + color: green[500], + opacity: "70%", + position: "absolute", + top: "20%", + left: "50%", + marginLeft: -12, + }, + + audioLoading: { + color: green[500], + opacity: "70%", + }, + + recorderWrapper: { + display: "flex", + alignItems: "center", + alignContent: "middle", + }, + + cancelAudioIcon: { + color: "red", + }, + + sendAudioIcon: { + color: "green", + }, + + replyginMsgWrapper: { + display: "flex", + width: "100%", + alignItems: "center", + justifyContent: "center", + paddingTop: 8, + paddingLeft: 73, + paddingRight: 7, + }, + + replyginMsgContainer: { + flex: 1, + marginRight: 5, + overflowY: "hidden", + backgroundColor: "rgba(0, 0, 0, 0.05)", + borderRadius: "7.5px", + display: "flex", + position: "relative", + }, + + replyginMsgBody: { + padding: 10, + height: "auto", + display: "block", + whiteSpace: "pre-wrap", + overflow: "hidden", + }, + + replyginContactMsgSideColor: { + flex: "none", + width: "4px", + backgroundColor: "#35cd96", + }, + + replyginSelfMsgSideColor: { + flex: "none", + width: "4px", + backgroundColor: "#6bcbef", + }, + + messageContactName: { + display: "flex", + color: "#6bcbef", + fontWeight: 500, + }, +})); + +const EmojiOptions = (props) => { + const { disabled, showEmoji, setShowEmoji, handleAddEmoji } = props; + const classes = useStyles(); + return ( + <> + setShowEmoji((prevState) => !prevState)} + > + + + {showEmoji ? ( +
+ +
+ ) : null} + + ); +}; + +const SignSwitch = (props) => { + const { width, setSignMessage, signMessage } = props; + if (isWidthUp("md", width)) { + return ( + { + setSignMessage(e.target.checked); + }} + name="showAllTickets" + color="primary" + /> + } + /> + ); + } + return null; +}; + +const FileInput = (props) => { + const { handleChangeMedias, disableOption } = props; + const classes = useStyles(); + return ( + <> + + + + ); +}; + +const ActionButtons = (props) => { + const { + inputMessage, + loading, + recording, + ticketStatus, + handleSendMessage, + handleCancelAudio, + handleUploadAudio, + handleStartRecording, + } = props; + const classes = useStyles(); + if (inputMessage) { + return ( + + + + ); + } else if (recording) { + return ( +
+ + + + {loading ? ( +
+ +
+ ) : ( + + )} + + + + +
+ ); + } else { + return ( + + + + ); + } +}; + +const CustomInput = (props) => { + const { + loading, + inputRef, + ticketStatus, + inputMessage, + setInputMessage, + handleSendMessage, + handleInputPaste, + disableOption, + handleQuickAnswersClick, + } = props; + const classes = useStyles(); + const [quickMessages, setQuickMessages] = useState([]); + const [options, setOptions] = useState([]); + const [popupOpen, setPopupOpen] = useState(false); + + const { user } = useContext(AuthContext); + + const { list: listQuickMessages } = useQuickMessages(); + + useEffect(() => { + async function fetchData() { + const companyId = localStorage.getItem("companyId"); + const messages = await listQuickMessages({ companyId, userId: user.id }); + const options = messages.map((m) => { + let truncatedMessage = m.message; + if (isString(truncatedMessage) && truncatedMessage.length > 35) { + truncatedMessage = m.message.substring(0, 35) + "..."; + } + return { + value: m.message, + label: `/${m.shortcode} - ${truncatedMessage}`, + mediaPath: m.mediaPath, + }; + }); + setQuickMessages(options); + } + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if ( + isString(inputMessage) && + !isEmpty(inputMessage) && + inputMessage.length > 1 + ) { + const firstWord = inputMessage.charAt(0); + setPopupOpen(firstWord.indexOf("/") > -1); + + const filteredOptions = quickMessages.filter( + (m) => m.label.indexOf(inputMessage) > -1 + ); + setOptions(filteredOptions); + } else { + setPopupOpen(false); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputMessage]); + + const onKeyPress = (e) => { + if (loading || e.shiftKey) return; + else if (e.key === "Enter") { + handleSendMessage(); + } + }; + + const onPaste = (e) => { + if (ticketStatus === "open") { + handleInputPaste(e); + } + }; + + const renderPlaceholder = () => { + if (ticketStatus === "open") { + return i18n.t("messagesInput.placeholderOpen"); + } + return i18n.t("messagesInput.placeholderClosed"); + }; + + + const setInputRef = (input) => { + if (input) { + input.focus(); + inputRef.current = input; + } + }; + + return ( +
+ { + if (isObject(option)) { + return option.label; + } else { + return option; + } + }} + onChange={(event, opt) => { + + if (isObject(opt) && has(opt, "value") && isNil(opt.mediaPath)) { + setInputMessage(opt.value); + setTimeout(() => { + inputRef.current.scrollTop = inputRef.current.scrollHeight; + }, 200); + } else if (isObject(opt) && has(opt, "value") && !isNil(opt.mediaPath)) { + handleQuickAnswersClick(opt); + + setTimeout(() => { + inputRef.current.scrollTop = inputRef.current.scrollHeight; + }, 200); + } + }} + onInputChange={(event, opt, reason) => { + if (reason === "input") { + setInputMessage(event.target.value); + } + }} + onPaste={onPaste} + onKeyPress={onKeyPress} + style={{ width: "100%" }} + renderInput={(params) => { + const { InputLabelProps, InputProps, ...rest } = params; + return ( + + ); + }} + /> +
+ ); +}; + +const MessageInputCustom = (props) => { + const { ticketStatus, ticketId } = props; + const classes = useStyles(); + + const [medias, setMedias] = useState([]); + const [inputMessage, setInputMessage] = useState(""); + const [showEmoji, setShowEmoji] = useState(false); + const [loading, setLoading] = useState(false); + const [recording, setRecording] = useState(false); + const inputRef = useRef(); + const { setReplyingMessage, replyingMessage } = + useContext(ReplyMessageContext); + const { user } = useContext(AuthContext); + + const [signMessage, setSignMessage] = useLocalStorage("signOption", true); + + useEffect(() => { + inputRef.current.focus(); + }, [replyingMessage]); + + useEffect(() => { + inputRef.current.focus(); + return () => { + setInputMessage(""); + setShowEmoji(false); + setMedias([]); + setReplyingMessage(null); + }; + }, [ticketId, setReplyingMessage]); + + // const handleChangeInput = e => { + // if (isObject(e) && has(e, 'value')) { + // setInputMessage(e.value); + // } else { + // setInputMessage(e.target.value) + // } + // }; + + const handleAddEmoji = (e) => { + let emoji = e.native; + setInputMessage((prevState) => prevState + emoji); + }; + + const handleChangeMedias = (e) => { + if (!e.target.files) { + return; + } + + const selectedMedias = Array.from(e.target.files); + setMedias(selectedMedias); + }; + + const handleInputPaste = (e) => { + if (e.clipboardData.files[0]) { + setMedias([e.clipboardData.files[0]]); + } + }; + + const handleUploadQuickMessageMedia = async (blob, message) => { + setLoading(true); + try { + const extension = blob.type.split("/")[1]; + + const formData = new FormData(); + const filename = `${new Date().getTime()}.${extension}`; + formData.append("medias", blob, filename); + formData.append("body", message); + formData.append("fromMe", true); + + await api.post(`/messages/${ticketId}`, formData); + } catch (err) { + toastError(err); + setLoading(false); + } + setLoading(false); + }; + + const handleQuickAnswersClick = async (value) => { + if (value.mediaPath) { + try { + const { data } = await axios.get(value.mediaPath, { + responseType: "blob", + }); + + handleUploadQuickMessageMedia(data, value.value); + setInputMessage(""); + return; + // handleChangeMedias(response) + } catch (err) { + toastError(err); + } + } + + setInputMessage(""); + setInputMessage(value.value); + }; + + const handleUploadMedia = async (e) => { + setLoading(true); + e.preventDefault(); + + const formData = new FormData(); + formData.append("fromMe", true); + medias.forEach((media) => { + formData.append("medias", media); + formData.append("body", media.name); + }); + + try { + await api.post(`/messages/${ticketId}`, formData); + } catch (err) { + toastError(err); + } + + setLoading(false); + setMedias([]); + }; + + const handleSendMessage = async () => { + if (inputMessage.trim() === "") return; + setLoading(true); + + const message = { + read: 1, + fromMe: true, + mediaUrl: "", + body: signMessage + ? `*${user?.name}:*\n${inputMessage.trim()}` + : inputMessage.trim(), + quotedMsg: replyingMessage, + }; + try { + await api.post(`/messages/${ticketId}`, message); + } catch (err) { + toastError(err); + } + + setInputMessage(""); + setShowEmoji(false); + setLoading(false); + setReplyingMessage(null); + }; + + const handleStartRecording = async () => { + setLoading(true); + try { + await navigator.mediaDevices.getUserMedia({ audio: true }); + await Mp3Recorder.start(); + setRecording(true); + setLoading(false); + } catch (err) { + toastError(err); + setLoading(false); + } + }; + + const handleUploadAudio = async () => { + setLoading(true); + try { + const [, blob] = await Mp3Recorder.stop().getMp3(); + if (blob.size < 10000) { + setLoading(false); + setRecording(false); + return; + } + + const formData = new FormData(); + const filename = `audio-record-site-${new Date().getTime()}.mp3`; + formData.append("medias", blob, filename); + formData.append("body", filename); + formData.append("fromMe", true); + + await api.post(`/messages/${ticketId}`, formData); + } catch (err) { + toastError(err); + } + + setRecording(false); + setLoading(false); + }; + + const handleCancelAudio = async () => { + try { + await Mp3Recorder.stop().getMp3(); + setRecording(false); + } catch (err) { + toastError(err); + } + }; + + const disableOption = () => { + return loading || recording || ticketStatus !== "open"; + }; + + const renderReplyingMessage = (message) => { + return ( +
+
+ +
+ {!message.fromMe && ( + + {message.contact?.name} + + )} + {message.body} +
+
+ setReplyingMessage(null)} + > + + +
+ ); + }; + + if (medias.length > 0) + return ( + + setMedias([])} + > + + + + {loading ? ( +
+ +
+ ) : ( + + {medias[0]?.name} + {/* */} + + )} + + + +
+ ); + else { + return ( + + {replyingMessage && renderReplyingMessage(replyingMessage)} +
+ + + + + + + + + +
+
+ ); + } +}; + +export default withWidth()(MessageInputCustom); diff --git a/frontend/src/components/MessageOptionsMenu/index.js b/frontend/src/components/MessageOptionsMenu/index.js new file mode 100644 index 0000000..83a0b6e --- /dev/null +++ b/frontend/src/components/MessageOptionsMenu/index.js @@ -0,0 +1,71 @@ +import React, { useState, useContext } from "react"; + +import MenuItem from "@material-ui/core/MenuItem"; + +import { i18n } from "../../translate/i18n"; +import api from "../../services/api"; +import ConfirmationModal from "../ConfirmationModal"; +import { Menu } from "@material-ui/core"; +import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext"; +import toastError from "../../errors/toastError"; + +const MessageOptionsMenu = ({ message, menuOpen, handleClose, anchorEl }) => { + const { setReplyingMessage } = useContext(ReplyMessageContext); + const [confirmationOpen, setConfirmationOpen] = useState(false); + + const handleDeleteMessage = async () => { + try { + await api.delete(`/messages/${message.id}`); + } catch (err) { + toastError(err); + } + }; + + const hanldeReplyMessage = () => { + setReplyingMessage(message); + handleClose(); + }; + + const handleOpenConfirmationModal = e => { + setConfirmationOpen(true); + handleClose(); + }; + + return ( + <> + + {i18n.t("messageOptionsMenu.confirmationModal.message")} + + + {message.fromMe && ( + + {i18n.t("messageOptionsMenu.delete")} + + )} + + {i18n.t("messageOptionsMenu.reply")} + + + + ); +}; + +export default MessageOptionsMenu; diff --git a/frontend/src/components/MessageVariablesPicker/index.js b/frontend/src/components/MessageVariablesPicker/index.js new file mode 100644 index 0000000..6d6ece2 --- /dev/null +++ b/frontend/src/components/MessageVariablesPicker/index.js @@ -0,0 +1,66 @@ +import React from "react"; +import { Chip, makeStyles } from "@material-ui/core"; +import { i18n } from "../../translate/i18n"; +import OutlinedDiv from "../OutlinedDiv"; + +const useStyles = makeStyles(theme => ({ + chip: { + margin: theme.spacing(0.5), + cursor: "pointer" + } +})); + +const MessageVariablesPicker = ({ onClick, disabled }) => { + const classes = useStyles(); + + const handleClick = (e, value) => { + e.preventDefault(); + if (disabled) return; + onClick(value); + }; + + const msgVars = [ + { + name: i18n.t("messageVariablesPicker.vars.contactFirstName"), + value: "{{firstName}}" + }, + { + name: i18n.t("messageVariablesPicker.vars.contactName"), + value: "{{name}} " + }, + { + name: i18n.t("messageVariablesPicker.vars.greeting"), + value: "{{ms}} " + }, + { + name: i18n.t("messageVariablesPicker.vars.protocolNumber"), + value: "{{protocol}} " + }, + { + name: i18n.t("messageVariablesPicker.vars.hour"), + value: "{{hora}} " + }, + ]; + + return ( + + {msgVars.map(msgVar => ( + handleClick(e, msgVar.value)} + label={msgVar.name} + size="small" + className={classes.chip} + color="primary" + /> + ))} + + ); +}; + +export default MessageVariablesPicker; \ No newline at end of file diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js new file mode 100644 index 0000000..3431a25 --- /dev/null +++ b/frontend/src/components/MessagesList/index.js @@ -0,0 +1,830 @@ +import React, { useState, useEffect, useReducer, useRef, useContext } from "react"; + +import { isSameDay, parseISO, format } from "date-fns"; +import clsx from "clsx"; + +import { green } from "@material-ui/core/colors"; +import { + Button, + CircularProgress, + Divider, + IconButton, + makeStyles, +} from "@material-ui/core"; + +import { + AccessTime, + Block, + Done, + DoneAll, + ExpandMore, + GetApp, +} from "@material-ui/icons"; + +import MarkdownWrapper from "../MarkdownWrapper"; +import ModalImageCors from "../ModalImageCors"; +import MessageOptionsMenu from "../MessageOptionsMenu"; +import whatsBackground from "../../assets/wa-background.png"; +import LocationPreview from "../LocationPreview"; + +import whatsBackgroundDark from "../../assets/wa-background-dark.png"; //DARK MODE PLW DESIGN// + +import api from "../../services/api"; +import toastError from "../../errors/toastError"; +import { SocketContext } from "../../context/Socket/SocketContext"; + +const useStyles = makeStyles((theme) => ({ + messagesListWrapper: { + overflow: "hidden", + position: "relative", + display: "flex", + flexDirection: "column", + flexGrow: 1, + width: "100%", + minWidth: 300, + minHeight: 200, + }, + + messagesList: { + backgroundImage: theme.mode === 'light' ? `url(${whatsBackground})` : `url(${whatsBackgroundDark})`, //DARK MODE PLW DESIGN// + display: "flex", + flexDirection: "column", + flexGrow: 1, + padding: "20px 20px 20px 20px", + overflowY: "scroll", + ...theme.scrollbarStyles, + }, + + circleLoading: { + color: green[500], + position: "absolute", + opacity: "70%", + top: 0, + left: "50%", + marginTop: 12, + }, + + messageLeft: { + marginRight: 20, + marginTop: 2, + minWidth: 100, + maxWidth: 600, + height: "auto", + display: "block", + position: "relative", + "&:hover #messageActionsButton": { + display: "flex", + position: "absolute", + top: 0, + right: 0, + }, + + whiteSpace: "pre-wrap", + backgroundColor: "#ffffff", + color: "#303030", + alignSelf: "flex-start", + borderTopLeftRadius: 0, + borderTopRightRadius: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + paddingLeft: 5, + paddingRight: 5, + paddingTop: 5, + paddingBottom: 0, + boxShadow: "0 1px 1px #b3b3b3", + }, + + quotedContainerLeft: { + margin: "-3px -80px 6px -6px", + overflow: "hidden", + backgroundColor: "#f0f0f0", + borderRadius: "7.5px", + display: "flex", + position: "relative", + }, + + quotedMsg: { + padding: 10, + maxWidth: 300, + height: "auto", + display: "block", + whiteSpace: "pre-wrap", + overflow: "hidden", + }, + + quotedSideColorLeft: { + flex: "none", + width: "4px", + backgroundColor: "#6bcbef", + }, + + messageRight: { + marginLeft: 20, + marginTop: 2, + minWidth: 100, + maxWidth: 600, + height: "auto", + display: "block", + position: "relative", + "&:hover #messageActionsButton": { + display: "flex", + position: "absolute", + top: 0, + right: 0, + }, + + whiteSpace: "pre-wrap", + backgroundColor: "#dcf8c6", + color: "#303030", + alignSelf: "flex-end", + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 0, + paddingLeft: 5, + paddingRight: 5, + paddingTop: 5, + paddingBottom: 0, + boxShadow: "0 1px 1px #b3b3b3", + }, + + quotedContainerRight: { + margin: "-3px -80px 6px -6px", + overflowY: "hidden", + backgroundColor: "#cfe9ba", + borderRadius: "7.5px", + display: "flex", + position: "relative", + }, + + quotedMsgRight: { + padding: 10, + maxWidth: 300, + height: "auto", + whiteSpace: "pre-wrap", + }, + + quotedSideColorRight: { + flex: "none", + width: "4px", + backgroundColor: "#35cd96", + }, + + messageActionsButton: { + display: "none", + position: "relative", + color: "#999", + zIndex: 1, + backgroundColor: "inherit", + opacity: "90%", + "&:hover, &.Mui-focusVisible": { backgroundColor: "inherit" }, + }, + + messageContactName: { + display: "flex", + color: "#6bcbef", + fontWeight: 500, + }, + + textContentItem: { + overflowWrap: "break-word", + padding: "3px 80px 6px 6px", + }, + + textContentItemEdited: { + overflowWrap: "break-word", + padding: "3px 120px 6px 6px", + }, + + textContentItemDeleted: { + fontStyle: "italic", + color: "rgba(0, 0, 0, 0.36)", + overflowWrap: "break-word", + padding: "3px 80px 6px 6px", + }, + + messageMedia: { + objectFit: "cover", + width: 250, + height: 200, + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderBottomLeftRadius: 8, + borderBottomRightRadius: 8, + }, + + timestamp: { + fontSize: 11, + position: "absolute", + bottom: 0, + right: 5, + color: "#999", + }, + + dailyTimestamp: { + alignItems: "center", + textAlign: "center", + alignSelf: "center", + width: "110px", + backgroundColor: "#e1f3fb", + margin: "10px", + borderRadius: "10px", + boxShadow: "0 1px 1px #b3b3b3", + }, + + dailyTimestampText: { + color: "#808888", + padding: 8, + alignSelf: "center", + marginLeft: "0px", + }, + + ackIcons: { + fontSize: 18, + verticalAlign: "middle", + marginLeft: 4, + }, + + deletedIcon: { + fontSize: 18, + verticalAlign: "middle", + marginRight: 4, + }, + + ackDoneAllIcon: { + color: green[500], + fontSize: 18, + verticalAlign: "middle", + marginLeft: 4, + }, + + downloadMedia: { + display: "flex", + alignItems: "center", + justifyContent: "center", + backgroundColor: "inherit", + padding: 10, + }, +})); + +const reducer = (state, action) => { + if (action.type === "LOAD_MESSAGES") { + const messages = action.payload; + const newMessages = []; + + messages.forEach((message) => { + const messageIndex = state.findIndex((m) => m.id === message.id); + if (messageIndex !== -1) { + state[messageIndex] = message; + } else { + newMessages.push(message); + } + }); + + return [...newMessages, ...state]; + } + + if (action.type === "ADD_MESSAGE") { + const newMessage = action.payload; + const messageIndex = state.findIndex((m) => m.id === newMessage.id); + + if (messageIndex !== -1) { + state[messageIndex] = newMessage; + } else { + state.push(newMessage); + } + + return [...state]; + } + + if (action.type === "UPDATE_MESSAGE") { + const messageToUpdate = action.payload; + const messageIndex = state.findIndex((m) => m.id === messageToUpdate.id); + + if (messageIndex !== -1) { + state[messageIndex] = messageToUpdate; + } + + return [...state]; + } + + if (action.type === "RESET") { + return []; + } +}; + +const MessagesList = ({ ticket, ticketId, isGroup }) => { + const classes = useStyles(); + + const [messagesList, dispatch] = useReducer(reducer, []); + const [pageNumber, setPageNumber] = useState(1); + const [hasMore, setHasMore] = useState(false); + const [loading, setLoading] = useState(false); + const lastMessageRef = useRef(); + + const [selectedMessage, setSelectedMessage] = useState({}); + const [anchorEl, setAnchorEl] = useState(null); + const messageOptionsMenuOpen = Boolean(anchorEl); + const currentTicketId = useRef(ticketId); + + const socketManager = useContext(SocketContext); + + useEffect(() => { + dispatch({ type: "RESET" }); + setPageNumber(1); + + currentTicketId.current = ticketId; + }, [ticketId]); + + useEffect(() => { + setLoading(true); + const delayDebounceFn = setTimeout(() => { + const fetchMessages = async () => { + if (ticketId === undefined) return; + try { + const { data } = await api.get("/messages/" + ticketId, { + params: { pageNumber }, + }); + + if (currentTicketId.current === ticketId) { + dispatch({ type: "LOAD_MESSAGES", payload: data.messages }); + setHasMore(data.hasMore); + setLoading(false); + } + + if (pageNumber === 1 && data.messages.length > 1) { + scrollToBottom(); + } + } catch (err) { + setLoading(false); + toastError(err); + } + }; + fetchMessages(); + }, 500); + return () => { + clearTimeout(delayDebounceFn); + }; + }, [pageNumber, ticketId]); + + useEffect(() => { + const companyId = localStorage.getItem("companyId"); + const socket = socketManager.getSocket(companyId); + + socket.on("ready", () => socket.emit("joinChatBox", `${ticket.id}`)); + + socket.on(`company-${companyId}-appMessage`, (data) => { + if (data.action === "create" && data.message.ticketId === currentTicketId.current) { + dispatch({ type: "ADD_MESSAGE", payload: data.message }); + scrollToBottom(); + } + + if (data.action === "update" && data.message.ticketId === currentTicketId.current) { + dispatch({ type: "UPDATE_MESSAGE", payload: data.message }); + } + }); + + return () => { + socket.disconnect(); + }; + }, [ticketId, ticket, socketManager]); + + const loadMore = () => { + setPageNumber((prevPageNumber) => prevPageNumber + 1); + }; + + const scrollToBottom = () => { + if (lastMessageRef.current) { + lastMessageRef.current.scrollIntoView({}); + } + }; + + const handleScroll = (e) => { + if (!hasMore) return; + const { scrollTop } = e.currentTarget; + + if (scrollTop === 0) { + document.getElementById("messagesList").scrollTop = 1; + } + + if (loading) { + return; + } + + if (scrollTop < 50) { + loadMore(); + } + }; + + const handleOpenMessageOptionsMenu = (e, message) => { + setAnchorEl(e.currentTarget); + setSelectedMessage(message); + }; + + const handleCloseMessageOptionsMenu = (e) => { + setAnchorEl(null); + }; + + const checkMessageMedia = (message) => { + if (message.mediaType === "locationMessage" && message.body.split('|').length >= 2) { + let locationParts = message.body.split('|') + let imageLocation = locationParts[0] + let linkLocation = locationParts[1] + + let descriptionLocation = null + + if (locationParts.length > 2) + descriptionLocation = message.body.split('|')[2] + + return + } + /* else if (message.mediaType === "vcard") { + let array = message.body.split("\n"); + let obj = []; + let contact = ""; + for (let index = 0; index < array.length; index++) { + const v = array[index]; + let values = v.split(":"); + for (let ind = 0; ind < values.length; ind++) { + if (values[ind].indexOf("+") !== -1) { + obj.push({ number: values[ind] }); + } + if (values[ind].indexOf("FN") !== -1) { + contact = values[ind + 1]; + } + } + } + return + } */ + /*else if (message.mediaType === "multi_vcard") { + console.log("multi_vcard") + console.log(message) + + if(message.body !== null && message.body !== "") { + let newBody = JSON.parse(message.body) + return ( + <> + { + newBody.map(v => ( + + )) + } + + ) + } else return (<>) + }*/ + else if (message.mediaType === "image") { + return ; + } else if (message.mediaType === "audio") { + return ( + + ); + } else if (message.mediaType === "video") { + return ( +