diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0531f23 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +.parcel-cache +.pnpm-store +pnpm-lock.yaml +dist \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..a8e1809 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,32 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + settings: { + react: { + version: "detect" + } + }, + extends: [ + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "standard", + "prettier" + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaFeatures: { + jsx: true + }, + ecmaVersion: 12, + sourceType: "module" + }, + plugins: ["react", "@typescript-eslint"], + rules: { + quotes: ["error", "double"], + "no-void": ["off"], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error"] + } +} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..6186e58 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +node_modules/.bin/lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0531f23 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.parcel-cache +.pnpm-store +pnpm-lock.yaml +dist \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..9fcf27e --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + ...require("prettier-config-standard"), + singleQuote: false +} diff --git a/README.md b/README.md index 969f448..cab20fa 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,27 @@ git clone -b dev https://github.com/guesant/ava-pro.git cd ava-pro ``` +#### Development with docker-compose (recommended) + +We recommend you to use docker-compose to develop the extension (~~due security reasons lol~~ [[1]](https://thehackernews.com/2021/10/popular-npm-package-hijacked-to-publish.html)). + +```sh +docker-compose up dev # -> packages/webextension/dist/dev +docker-compose up build # -> packages/webextension/dist/prod +``` + +#### Development with your system's nodejs + +```sh +npm i -g pnpm +pnpm install +``` + +```sh +pnpm run dev # -> packages/webextension/dist/dev +pnpm run build # -> packages/webextension/dist/prod +``` + ## License [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..05a0fb2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + dev: + image: cl00e9ment/node.js-builder + restart: always + container_name: ava-pro-dev + command: [sh, -c, 'su node -c "pnpm install && pnpm run dev"'] + volumes: + - .:/code + working_dir: /code + ports: + - 1234:1234 + + build: + image: cl00e9ment/node.js-builder + container_name: ava-pro-build + command: [sh, -c, 'su node -c "pnpm install && pnpm run build"'] + volumes: + - .:/code + working_dir: /code diff --git a/package.json b/package.json new file mode 100644 index 0000000..e66a24c --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "root", + "version": "0.0.2", + "private": true, + "scripts": { + "format": "prettier --write .", + "lint": "eslint --fix --ext js,ts,tsx,jsx packages", + "dev": "pnpm run --filter @ava-pro/webextension start", + "build": "pnpm run clear:cache; pnpm run --filter @ava-pro/webextension build", + "clear:cache": "find . -type d -name .parcel-cache -exec rm -rf {} \\+", + "prepare": "husky install" + }, + "lint-staged": { + "**/*": "prettier --write --ignore-unknown" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.4.0", + "@typescript-eslint/parser": "^5.4.0", + "eslint": "^7.12.1", + "eslint-config-prettier": "^8.3.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-react": "^7.27.1", + "husky": "^7.0.4", + "lint-staged": "^12.1.2", + "prettier": "^2.4.1", + "prettier-config-standard": "^4.0.0" + } +} diff --git a/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Conversations.ts b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Conversations.ts new file mode 100644 index 0000000..cf2e1d8 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Conversations.ts @@ -0,0 +1,18 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { ajaxService } from "../../../../ajaxService" +import { IMessageAreaConversations } from "../../../../Typings/IMessageAreaConversations" + +type IConversationsOptions = { + userid: number +} & Record + +export const Conversations = + (crawlerFetch: ICrawlerFetch) => + ({ ...options }: IConversationsOptions) => + ajaxService(crawlerFetch)( + "core_message_data_for_messagearea_conversations", + { + limitfrom: 0, + ...options + } + ) diff --git a/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Messages.ts b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Messages.ts new file mode 100644 index 0000000..8272bc8 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Messages.ts @@ -0,0 +1,15 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { ajaxService } from "../../../../ajaxService" +import { IMessageArea } from "../../../../Typings/IMessageArea" + +type IMessagesOptions = { + otheruserid: number + currentuserid: number +} & Record + +export const Messages = + (crawlerFetch: ICrawlerFetch) => (options: IMessagesOptions) => + ajaxService(crawlerFetch)( + "core_message_data_for_messagearea_messages", + { newest: true, ...options } + ) diff --git a/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Search.ts b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Search.ts new file mode 100644 index 0000000..f3bbe2c --- /dev/null +++ b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Search.ts @@ -0,0 +1,15 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { ajaxService } from "../../../../ajaxService" +import { IMessageAreaSearchContacts } from "../../../../Typings/IMessageAreaSearchContacts" + +type ISearchOptions = { + userid: number + search: string +} & Record + +export const Search = + (crawlerFetch: ICrawlerFetch) => (options: ISearchOptions) => + ajaxService(crawlerFetch)( + "core_message_data_for_messagearea_search_users", + { ...options } + ) diff --git a/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/MarkAllMessagesAsRead.ts b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/MarkAllMessagesAsRead.ts new file mode 100644 index 0000000..9b2a737 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/MarkAllMessagesAsRead.ts @@ -0,0 +1,15 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { ajaxService } from "../../../ajaxService" +import { IMessageArea } from "../../../Typings/IMessageArea" + +type IMarkAllMessagesAsReadOptions = { + useridto: number + useridfrom: number +} & Record + +export const MarkAllMessagesAsRead = + (crawlerFetch: ICrawlerFetch) => (options: IMarkAllMessagesAsReadOptions) => + ajaxService(crawlerFetch)( + "core_message_mark_all_messages_as_read", + { ...options } + ) diff --git a/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/SendInstantMessages.ts b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/SendInstantMessages.ts new file mode 100644 index 0000000..f563388 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/AjaxServices/CoreMessage/SendInstantMessages.ts @@ -0,0 +1,19 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { ajaxService } from "../../../ajaxService" + +type ISendInstantMessagesOptions = { + text: string + touserid: number +} & Record + +export const SendInstantMessages = + (crawlerFetch: ICrawlerFetch) => + ({ text, ...options }: ISendInstantMessagesOptions) => + ajaxService(crawlerFetch)("core_message_send_instant_messages", { + messages: [ + { + text: text.trim(), + ...options + } + ] + }) diff --git a/packages/crawlers/lib/Scrappers/CheckLogin/CheckLogin.ts b/packages/crawlers/lib/Scrappers/CheckLogin/CheckLogin.ts new file mode 100644 index 0000000..40abbf3 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/CheckLogin/CheckLogin.ts @@ -0,0 +1,10 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { routes } from "../../routes" +import { getLoginState } from "../getLoginState" + +export const CheckLogin = (crawlerFetch: ICrawlerFetch) => async () => { + const page = await crawlerFetch({ url: routes.login() }).then((res) => + res.text() + ) + return getLoginState(page) +} diff --git a/packages/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceHome/GetMoodleInstanceHome.ts b/packages/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceHome/GetMoodleInstanceHome.ts new file mode 100644 index 0000000..2d63881 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceHome/GetMoodleInstanceHome.ts @@ -0,0 +1,6 @@ +export const getMoodleInstanceHome = (document: Document) => { + const homeAnchor = document.querySelector( + "#nav-drawer a[data-key='home'], .homelink a" + ) + return homeAnchor?.href +} diff --git a/packages/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceName/GetMoodleInstanceName.ts b/packages/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceName/GetMoodleInstanceName.ts new file mode 100644 index 0000000..9161284 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceName/GetMoodleInstanceName.ts @@ -0,0 +1,4 @@ +export const getMoodleInstanceName = (document: Document) => { + const siteName = document.querySelector(".site-name") + return siteName?.textContent ?? document.title ?? document.location.href +} diff --git a/packages/crawlers/lib/Scrappers/DocumentScrappers/IsMoodleInstance/IsMoodleInstance.ts b/packages/crawlers/lib/Scrappers/DocumentScrappers/IsMoodleInstance/IsMoodleInstance.ts new file mode 100644 index 0000000..cfb4d8a --- /dev/null +++ b/packages/crawlers/lib/Scrappers/DocumentScrappers/IsMoodleInstance/IsMoodleInstance.ts @@ -0,0 +1,8 @@ +export const isMoodleInstance = (document: Document) => { + const metaKeywords = document.querySelector('meta[name="keywords"]') + + const keywords = + metaKeywords?.attributes.getNamedItem("content")?.value.split(",") || [] + + return keywords.includes("moodle") +} diff --git a/packages/crawlers/lib/Scrappers/ExtractTokens/ExtractTokens.ts b/packages/crawlers/lib/Scrappers/ExtractTokens/ExtractTokens.ts new file mode 100644 index 0000000..91a4d16 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ExtractTokens/ExtractTokens.ts @@ -0,0 +1,28 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { routes } from "../../routes" +import { extractSessKey } from "./extractSessKey" +import { extractUserId } from "./extractUserId" + +export const ExtractTokens = (crawlerFetch: ICrawlerFetch) => async () => { + const data = await crawlerFetch({ + url: routes.my(), + options: { method: "get" } + }).then((res) => res.text()) + + const sessKey = extractSessKey(data) + const userId = extractUserId(data) + + return { sessKey, userId } +} + +export const ExtractTokensSessKey = + (crawlerFetch: ICrawlerFetch) => async () => { + const { sessKey } = await ExtractTokens(crawlerFetch)() + return sessKey + } + +export const ExtractTokensUserId = + (crawlerFetch: ICrawlerFetch) => async () => { + const { userId } = await ExtractTokens(crawlerFetch)() + return userId + } diff --git a/packages/crawlers/lib/Scrappers/ExtractTokens/extractSessKey.ts b/packages/crawlers/lib/Scrappers/ExtractTokens/extractSessKey.ts new file mode 100644 index 0000000..965840b --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ExtractTokens/extractSessKey.ts @@ -0,0 +1,4 @@ +export const extractSessKey = (pageContent: string): string | null => { + const [, sesskey = null] = pageContent.match(/"sesskey":"([^"]+)"/) || [] + return sesskey +} diff --git a/packages/crawlers/lib/Scrappers/ExtractTokens/extractUserId.ts b/packages/crawlers/lib/Scrappers/ExtractTokens/extractUserId.ts new file mode 100644 index 0000000..98dcc46 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ExtractTokens/extractUserId.ts @@ -0,0 +1,4 @@ +export const extractUserId = (pageContent: string): number | null => { + const [, userid = null] = pageContent.match(/data-userid="([\d]+)"/) || [] + return userid ? +userid : null +} diff --git a/packages/crawlers/lib/Scrappers/ListCourses/ListCourses.ts b/packages/crawlers/lib/Scrappers/ListCourses/ListCourses.ts new file mode 100644 index 0000000..e2637a3 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ListCourses/ListCourses.ts @@ -0,0 +1,15 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { routes } from "../../routes" +import { extractPageCourses } from "./extractPageCourses" + +export const ListCourses = (crawlerFetch: ICrawlerFetch) => async () => { + const response = await crawlerFetch({ url: routes.myTabOverviewCourses() }) + + if (response.redirected) { + throw new Error() + } + + const pageContent = await response.text() + + return extractPageCourses(pageContent) +} diff --git a/packages/crawlers/lib/Scrappers/ListCourses/extractCourses.ts b/packages/crawlers/lib/Scrappers/ListCourses/extractCourses.ts new file mode 100644 index 0000000..fcf5b92 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ListCourses/extractCourses.ts @@ -0,0 +1,18 @@ +import { strcmp } from "@ava-pro/shared/lib/Utils/strcmp" +import { selectAll } from "css-select" +import { Document, Element } from "domhandler" +import { parseDocument } from "htmlparser2" +import { extractedCourseFromNode } from "./extractedCourseFromNode" + +export const extractCourses = (data: string, baseSelector: string = "") => { + const dom = parseDocument(data, {}) + + const links = selectAll( + `${baseSelector} #pc-for-in-progress .course-info-container a`.trim(), + dom + ) + + return links + .map(extractedCourseFromNode) + .sort((a, b) => strcmp(a.name, b.name)) +} diff --git a/packages/crawlers/lib/Scrappers/ListCourses/extractPageCourses.ts b/packages/crawlers/lib/Scrappers/ListCourses/extractPageCourses.ts new file mode 100644 index 0000000..9a354ab --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ListCourses/extractPageCourses.ts @@ -0,0 +1,15 @@ +import { IPageExtractedCourses } from "@ava-pro/shared/lib/Interfaces/IPageExtractedCourses" +import { extractCourses } from "./extractCourses" + +enum SelectorsMyOverviewCourses { + IN_PROGRESS = "#myoverview_courses_view_in_progress", + FUTURE = "#myoverview_courses_view_future", + PAST = "#myoverview_courses_view_past" +} + +export const extractPageCourses = (data: string): IPageExtractedCourses => + ({ + PAST: extractCourses(data, SelectorsMyOverviewCourses.PAST), + IN_PROGRESS: extractCourses(data, SelectorsMyOverviewCourses.IN_PROGRESS), + FUTURE: extractCourses(data, SelectorsMyOverviewCourses.FUTURE) + } as any) diff --git a/packages/crawlers/lib/Scrappers/ListCourses/extractedCourseFromNode.ts b/packages/crawlers/lib/Scrappers/ListCourses/extractedCourseFromNode.ts new file mode 100644 index 0000000..ddad2d3 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/ListCourses/extractedCourseFromNode.ts @@ -0,0 +1,14 @@ +import { IExtractedCourseSlim } from "@ava-pro/shared/lib/Interfaces/IExtractedCourseSlim" +import { Element } from "domhandler" +import { getAttributeValue, innerText } from "domutils" +import normalizeUrl from "normalize-url" +import { getCourseIdFromURL } from "../getCourseIdFromURL" + +export const extractedCourseFromNode = ( + node: Element +): IExtractedCourseSlim => { + const name = innerText(node).trim() + const url = normalizeUrl(getAttributeValue(node, "href") ?? "#") + const courseId = getCourseIdFromURL(url)! + return { name, url, courseId } +} diff --git a/packages/crawlers/lib/Scrappers/Login/Login.ts b/packages/crawlers/lib/Scrappers/Login/Login.ts new file mode 100644 index 0000000..12084db --- /dev/null +++ b/packages/crawlers/lib/Scrappers/Login/Login.ts @@ -0,0 +1,27 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { routes } from "../../routes" +import { getLoginState } from "../getLoginState" +import { Logout } from "../Logout/Logout" + +export const Login = + (crawlerFetch: ICrawlerFetch) => + async (username: string, password: string, skipLogout: boolean = false) => { + if (!skipLogout) { + await Logout(crawlerFetch)() + } + + const body = new FormData() + + body.set("username", username) + body.set("password", password) + + const response = await crawlerFetch({ + url: routes.login(), + options: { + body, + method: "post" + } + }).then((res) => res.text()) + + return getLoginState(response) + } diff --git a/packages/crawlers/lib/Scrappers/Logout/Logout.ts b/packages/crawlers/lib/Scrappers/Logout/Logout.ts new file mode 100644 index 0000000..8cc75a2 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/Logout/Logout.ts @@ -0,0 +1,18 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { MayBePromise } from "@ava-pro/shared/lib/Interfaces/MayBePromise" +import { routes } from "../../routes" +import { ExtractTokensSessKey } from "../ExtractTokens/ExtractTokens" + +export const Logout = + (crawlerFetch: ICrawlerFetch) => + async ( + incomingSessKey: MayBePromise = ExtractTokensSessKey( + crawlerFetch + )() + ) => { + const sesskey = await incomingSessKey + sesskey && + (await crawlerFetch({ + url: routes.logout({ sesskey }) + })) + } diff --git a/packages/crawlers/lib/Scrappers/SmartLogin/SmartLogin.ts b/packages/crawlers/lib/Scrappers/SmartLogin/SmartLogin.ts new file mode 100644 index 0000000..d31b245 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/SmartLogin/SmartLogin.ts @@ -0,0 +1,25 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { CheckLogin } from "../CheckLogin/CheckLogin" +import { Login } from "../Login/Login" + +export const SmartLogin = + (crawlerFetch: ICrawlerFetch) => + async ( + username: string, + password: string, + skipCheckLogin = false, + skipLogout = skipCheckLogin + ) => { + let isLoggedIn = false + + if (!skipCheckLogin) { + isLoggedIn = await CheckLogin(crawlerFetch)() + } + + if (!isLoggedIn && username && password) { + await Login(crawlerFetch)(username, password, skipLogout) + isLoggedIn = await CheckLogin(crawlerFetch)() + } + + return isLoggedIn + } diff --git a/packages/crawlers/lib/Scrappers/getCourseIdFromURL.ts b/packages/crawlers/lib/Scrappers/getCourseIdFromURL.ts new file mode 100644 index 0000000..9f2b5c6 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/getCourseIdFromURL.ts @@ -0,0 +1,4 @@ +const QUERY_COURSE_ID = "id" + +export const getCourseIdFromURL = (url: string) => + new URL(url).searchParams.get(QUERY_COURSE_ID) diff --git a/packages/crawlers/lib/Scrappers/getLoginState.ts b/packages/crawlers/lib/Scrappers/getLoginState.ts new file mode 100644 index 0000000..45767d7 --- /dev/null +++ b/packages/crawlers/lib/Scrappers/getLoginState.ts @@ -0,0 +1,5 @@ +export const getLoginState = (data: string) => { + const [body = ""] = data.match(/]*>/g) || [] + const isLoggedIn = !body.includes("notloggedin") + return isLoggedIn +} diff --git a/packages/crawlers/lib/Typings/IMessageArea.ts b/packages/crawlers/lib/Typings/IMessageArea.ts new file mode 100644 index 0000000..62c1bf8 --- /dev/null +++ b/packages/crawlers/lib/Typings/IMessageArea.ts @@ -0,0 +1,12 @@ +import { IMessageAreaMessage } from "./IMessageAreaMessage" + +export type IMessageArea = { + currentuserid: number + isblocked: boolean + iscurrentuser: boolean + isonline: boolean + messages: IMessageAreaMessage[] + otheruserfullname: string + otheruserid: number + showonlinestatus: boolean +} diff --git a/packages/crawlers/lib/Typings/IMessageAreaContact.ts b/packages/crawlers/lib/Typings/IMessageAreaContact.ts new file mode 100644 index 0000000..f3915a0 --- /dev/null +++ b/packages/crawlers/lib/Typings/IMessageAreaContact.ts @@ -0,0 +1,15 @@ +export type IMessageAreaContact = { + userid: number + fullname: string + profileimageurl: string + profileimageurlsmall: string + ismessaging: boolean + sentfromcurrentuser: boolean + lastmessage: string | null + messageid: number | null + showonlinestatus: boolean + isonline: boolean | null + isread: boolean + isblocked: boolean + unreadcount: number | null +} diff --git a/packages/crawlers/lib/Typings/IMessageAreaConversations.ts b/packages/crawlers/lib/Typings/IMessageAreaConversations.ts new file mode 100644 index 0000000..b3cedd2 --- /dev/null +++ b/packages/crawlers/lib/Typings/IMessageAreaConversations.ts @@ -0,0 +1,5 @@ +import { IMessageAreaContact } from "./IMessageAreaContact" + +export type IMessageAreaConversations = { + contacts: IMessageAreaContact[] +} diff --git a/packages/crawlers/lib/Typings/IMessageAreaMessage.ts b/packages/crawlers/lib/Typings/IMessageAreaMessage.ts new file mode 100644 index 0000000..bfc5ab0 --- /dev/null +++ b/packages/crawlers/lib/Typings/IMessageAreaMessage.ts @@ -0,0 +1,17 @@ +export enum MessagePosition { + LEFT = "left", + RIGHT = "right" +} + +export type IMessageAreaMessage = { + id: number + useridfrom: number + useridto: number + text: string + displayblocktime: boolean + blocktime: string + position: MessagePosition + timesent: string + timecreated: number + isread: boolean +} diff --git a/packages/crawlers/lib/Typings/IMessageAreaSearchContacts.ts b/packages/crawlers/lib/Typings/IMessageAreaSearchContacts.ts new file mode 100644 index 0000000..775bf07 --- /dev/null +++ b/packages/crawlers/lib/Typings/IMessageAreaSearchContacts.ts @@ -0,0 +1,7 @@ +import { IMessageAreaContact } from "./IMessageAreaContact" + +export type IMessageAreaSearchContacts = { + courses: any[] + contacts: IMessageAreaContact[] + noncontacts: IMessageAreaContact[] +} diff --git a/packages/crawlers/lib/Typings/IMoodleTokens.ts b/packages/crawlers/lib/Typings/IMoodleTokens.ts new file mode 100644 index 0000000..39ceb5d --- /dev/null +++ b/packages/crawlers/lib/Typings/IMoodleTokens.ts @@ -0,0 +1,4 @@ +export type IMoodleTokens = { + sesskey: string | null + userid: number | null +} diff --git a/packages/crawlers/lib/ajaxService.ts b/packages/crawlers/lib/ajaxService.ts new file mode 100644 index 0000000..3cbd0f9 --- /dev/null +++ b/packages/crawlers/lib/ajaxService.ts @@ -0,0 +1,54 @@ +import { + ICrawlerFetch, + ICrawlerFetchOptions +} from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { MayBePromise } from "@ava-pro/shared/lib/Interfaces/MayBePromise" +import { routes } from "./routes" +import { ExtractTokensSessKey } from "./Scrappers/ExtractTokens/ExtractTokens" + +export const ajaxService = + (crawlerFetch: ICrawlerFetch) => + async ( + methodname: string, + args: Args, + crawlerFetchOptions: ICrawlerFetchOptions["options"] = {}, + incomingSessKey: MayBePromise = ExtractTokensSessKey( + crawlerFetch + )() + ) => { + const sessKey = await incomingSessKey + + if (!sessKey) { + throw new Error("Invalid session key.") + } + + const params = new URLSearchParams() + params.set("sesskey", sessKey) + params.set("info", methodname) + + const response = await crawlerFetch({ + url: `${routes.ajax()}?${params.toString()}`, + options: { + method: "post", + body: JSON.stringify([ + { + args, + index: 0, + methodname + } + ]), + headers: { + "content-type": "application/json" + }, + ...crawlerFetchOptions + } + }) + + const [{ error, exception, data }] = await response.json() + + if (error) { + throw exception + } + + return data as Response + } diff --git a/packages/crawlers/lib/routes.ts b/packages/crawlers/lib/routes.ts new file mode 100644 index 0000000..7c37d7b --- /dev/null +++ b/packages/crawlers/lib/routes.ts @@ -0,0 +1,18 @@ +import { Ruty } from "ruty" + +const { route } = Ruty.configure() + +export const routes = { + home: route("/").build(), + + my: route("/my/").build(), + + myTabOverviewCourses: route("/my/?myoverviewtab=courses").build(), + + course: route("/course/view.php?id").build<{ id: string | number }>(), + + login: route("/login/index.php").build(), + logout: route("/login/logout.php?sesskey").build<{ sesskey: string }>(), + + ajax: route("/lib/ajax/service.php").build() +} diff --git a/packages/crawlers/package.json b/packages/crawlers/package.json new file mode 100644 index 0000000..336e985 --- /dev/null +++ b/packages/crawlers/package.json @@ -0,0 +1,12 @@ +{ + "name": "@ava-pro/crawlers", + "dependencies": { + "@ava-pro/shared": "workspace:*", + "css-select": "^4.1.3", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "htmlparser2": "^7.2.0", + "normalize-url": "^7.0.2", + "ruty": "^1.2.3" + } +} diff --git a/packages/crawlers/tsconfig.json b/packages/crawlers/tsconfig.json new file mode 100644 index 0000000..43c8e04 --- /dev/null +++ b/packages/crawlers/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2016", + "lib": ["DOM", "ESNext"], + "module": "ESNext", + "strictNullChecks": true, + "rootDir": "./lib", + "moduleResolution": "node", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/packages/shared/lib/Hooks/useExtensionStorageSlicer.tsx b/packages/shared/lib/Hooks/useExtensionStorageSlicer.tsx new file mode 100644 index 0000000..652ba35 --- /dev/null +++ b/packages/shared/lib/Hooks/useExtensionStorageSlicer.tsx @@ -0,0 +1,22 @@ +import { useEffect, useState } from "react" +import { IExtensionStorageSlicer } from "../Interfaces/IExtensionStorageSlicer" +import { storageSyncSlicer } from "../storageSyncSlicer" + +export const useExtensionStorageSlicer = ( + slice: IExtensionStorageSlicer, + placeholderValue: T +) => { + const [value, setValue] = useState(placeholderValue) + const [isLoading, setIsLoading] = useState(true) + + useEffect(() => { + const cleanup = storageSyncSlicer( + slice, + (state) => setValue(state), + setIsLoading + ) + return () => void cleanup() + }, [slice]) + + return { isLoading, value } +} diff --git a/packages/shared/lib/Interfaces/ICentralStorageListener.ts b/packages/shared/lib/Interfaces/ICentralStorageListener.ts new file mode 100644 index 0000000..91b1270 --- /dev/null +++ b/packages/shared/lib/Interfaces/ICentralStorageListener.ts @@ -0,0 +1 @@ +export type ICentralStorageListener = () => void diff --git a/packages/shared/lib/Interfaces/ICoursesOrderBy.ts b/packages/shared/lib/Interfaces/ICoursesOrderBy.ts new file mode 100644 index 0000000..d23f65d --- /dev/null +++ b/packages/shared/lib/Interfaces/ICoursesOrderBy.ts @@ -0,0 +1,4 @@ +export enum ICoursesOrderBy { + NAME, + LAST_VISIT +} diff --git a/packages/shared/lib/Interfaces/ICrawlerFetch.ts b/packages/shared/lib/Interfaces/ICrawlerFetch.ts new file mode 100644 index 0000000..5c66489 --- /dev/null +++ b/packages/shared/lib/Interfaces/ICrawlerFetch.ts @@ -0,0 +1,8 @@ +export type ICrawlerFetchOptions = { + url: string + options?: RequestInit +} + +export type ICrawlerFetch = ( + crawlerFetchOptions: ICrawlerFetchOptions +) => Promise diff --git a/packages/shared/lib/Interfaces/IDetectedRoom.ts b/packages/shared/lib/Interfaces/IDetectedRoom.ts new file mode 100644 index 0000000..c9b24b0 --- /dev/null +++ b/packages/shared/lib/Interfaces/IDetectedRoom.ts @@ -0,0 +1,7 @@ +import { IDetectedRoomResponse } from "./IDetectedRoomResponse" + +export type IDetectedRoom = { + url: string + name: string + response: IDetectedRoomResponse +} diff --git a/packages/shared/lib/Interfaces/IDetectedRoomResponse.ts b/packages/shared/lib/Interfaces/IDetectedRoomResponse.ts new file mode 100644 index 0000000..7e0bb5f --- /dev/null +++ b/packages/shared/lib/Interfaces/IDetectedRoomResponse.ts @@ -0,0 +1,5 @@ +export enum IDetectedRoomResponse { + NONE, + ACCEPTED, + REJECTED +} diff --git a/packages/shared/lib/Interfaces/IExtensionStorageMutation.ts b/packages/shared/lib/Interfaces/IExtensionStorageMutation.ts new file mode 100644 index 0000000..ff02db0 --- /dev/null +++ b/packages/shared/lib/Interfaces/IExtensionStorageMutation.ts @@ -0,0 +1,6 @@ +import { IStorage } from "./IStorage" + +export type IExtensionStorageMutation = ( + state: IStorage, + payload: T +) => void diff --git a/packages/shared/lib/Interfaces/IExtensionStorageSlicer.ts b/packages/shared/lib/Interfaces/IExtensionStorageSlicer.ts new file mode 100644 index 0000000..ccf40bd --- /dev/null +++ b/packages/shared/lib/Interfaces/IExtensionStorageSlicer.ts @@ -0,0 +1,3 @@ +import { IStorage } from "./IStorage" + +export type IExtensionStorageSlicer = (state: IStorage) => T diff --git a/packages/shared/lib/Interfaces/IExtractedCourse.ts b/packages/shared/lib/Interfaces/IExtractedCourse.ts new file mode 100644 index 0000000..61a365c --- /dev/null +++ b/packages/shared/lib/Interfaces/IExtractedCourse.ts @@ -0,0 +1,6 @@ +import { IExtractedCourseSlim } from "./IExtractedCourseSlim" + +export type IExtractedCourse = IExtractedCourseSlim & { + isPinned: boolean + lastVisit: null | number +} diff --git a/packages/shared/lib/Interfaces/IExtractedCourseSlim.ts b/packages/shared/lib/Interfaces/IExtractedCourseSlim.ts new file mode 100644 index 0000000..f2295fb --- /dev/null +++ b/packages/shared/lib/Interfaces/IExtractedCourseSlim.ts @@ -0,0 +1,5 @@ +export type IExtractedCourseSlim = { + url: string + name: string + courseId: string +} diff --git a/packages/shared/lib/Interfaces/IFilterCoursesByStatus.ts b/packages/shared/lib/Interfaces/IFilterCoursesByStatus.ts new file mode 100644 index 0000000..8830eef --- /dev/null +++ b/packages/shared/lib/Interfaces/IFilterCoursesByStatus.ts @@ -0,0 +1,5 @@ +export enum IFilterCoursesByStatus { + PAST, + IN_PROGRESS, + FUTURE +} diff --git a/packages/shared/lib/Interfaces/IPageExtractedCourses.ts b/packages/shared/lib/Interfaces/IPageExtractedCourses.ts new file mode 100644 index 0000000..160f6c6 --- /dev/null +++ b/packages/shared/lib/Interfaces/IPageExtractedCourses.ts @@ -0,0 +1,10 @@ +import { IExtractedCourse } from "./IExtractedCourse" +import { IExtractedCourseSlim } from "./IExtractedCourseSlim" + +export type IPageExtractedCourses< + T extends IExtractedCourseSlim = IExtractedCourse +> = { + PAST: T[] + IN_PROGRESS: T[] + FUTURE: T[] +} diff --git a/packages/shared/lib/Interfaces/IRoom.ts b/packages/shared/lib/Interfaces/IRoom.ts new file mode 100644 index 0000000..86de5c7 --- /dev/null +++ b/packages/shared/lib/Interfaces/IRoom.ts @@ -0,0 +1,17 @@ +import { IPageExtractedCourses } from "./IPageExtractedCourses" + +export type IRoom = { + id: string + url: string + name: string + + cache: { + courses: IPageExtractedCourses | null + } + + credentials: { + username: string + password: string + autoLogin: boolean + } +} diff --git a/packages/shared/lib/Interfaces/IRuntimeMessageType.ts b/packages/shared/lib/Interfaces/IRuntimeMessageType.ts new file mode 100644 index 0000000..b5c9ee1 --- /dev/null +++ b/packages/shared/lib/Interfaces/IRuntimeMessageType.ts @@ -0,0 +1,3 @@ +export enum IRuntimeMessageType { + NOTIFY_DETECTED_ROOM +} diff --git a/packages/shared/lib/Interfaces/ISettings.ts b/packages/shared/lib/Interfaces/ISettings.ts new file mode 100644 index 0000000..252a1f1 --- /dev/null +++ b/packages/shared/lib/Interfaces/ISettings.ts @@ -0,0 +1,9 @@ +import { ICoursesOrderBy } from "./ICoursesOrderBy" +import { IFilterCoursesByStatus } from "./IFilterCoursesByStatus" +import { IRoom } from "./IRoom" + +export type ISettings = { + selectedRoom: IRoom["id"] | null + coursesOrderBy: ICoursesOrderBy + filterCoursesByStatus: IFilterCoursesByStatus +} diff --git a/packages/shared/lib/Interfaces/IStorage.ts b/packages/shared/lib/Interfaces/IStorage.ts new file mode 100644 index 0000000..c034a85 --- /dev/null +++ b/packages/shared/lib/Interfaces/IStorage.ts @@ -0,0 +1,10 @@ +import { IDetectedRoom } from "./IDetectedRoom" +import { IRoom } from "./IRoom" +import { ISettings } from "./ISettings" + +export type IStorage = { + rooms: IRoom[] + detectedRooms: IDetectedRoom[] + settings: ISettings + lastMigration?: null | number +} diff --git a/packages/shared/lib/Interfaces/IStorageMigration.ts b/packages/shared/lib/Interfaces/IStorageMigration.ts new file mode 100644 index 0000000..a7caf53 --- /dev/null +++ b/packages/shared/lib/Interfaces/IStorageMigration.ts @@ -0,0 +1,4 @@ +export interface IStorageMigration { + id: number + up: () => void | Promise +} diff --git a/packages/shared/lib/Interfaces/MayBePromise.ts b/packages/shared/lib/Interfaces/MayBePromise.ts new file mode 100644 index 0000000..fcbfd5b --- /dev/null +++ b/packages/shared/lib/Interfaces/MayBePromise.ts @@ -0,0 +1 @@ +export type MayBePromise = Promise | T diff --git a/packages/shared/lib/RuntimeMessage/addRuntimeMessageListener.ts b/packages/shared/lib/RuntimeMessage/addRuntimeMessageListener.ts new file mode 100644 index 0000000..7464e5b --- /dev/null +++ b/packages/shared/lib/RuntimeMessage/addRuntimeMessageListener.ts @@ -0,0 +1,15 @@ +import browser from "webextension-polyfill" +import { IRuntimeMessageType } from "../Interfaces/IRuntimeMessageType" + +export const addRuntimeMessageListener = ( + type: IRuntimeMessageType, + callback: (payload: Payload) => void +) => { + const handleRuntimeMessage = (message: any) => { + console.log("oshe°°°°????", message) + if (message?.type === type) { + callback(message.payload) + } + } + browser.runtime.onMessage.addListener(handleRuntimeMessage) +} diff --git a/packages/shared/lib/RuntimeMessage/sendRuntimeMessage.ts b/packages/shared/lib/RuntimeMessage/sendRuntimeMessage.ts new file mode 100644 index 0000000..d61faa7 --- /dev/null +++ b/packages/shared/lib/RuntimeMessage/sendRuntimeMessage.ts @@ -0,0 +1,9 @@ +import browser from "webextension-polyfill" +import { IRuntimeMessageType } from "../Interfaces/IRuntimeMessageType" + +export const sendRuntimeMessage = async ( + type: IRuntimeMessageType, + payload: T +) => { + await browser.runtime.sendMessage({ type, payload }) +} diff --git a/packages/shared/lib/Schemas/DetectedRoom.ts b/packages/shared/lib/Schemas/DetectedRoom.ts new file mode 100644 index 0000000..f936baa --- /dev/null +++ b/packages/shared/lib/Schemas/DetectedRoom.ts @@ -0,0 +1,8 @@ +import { string, type } from "superstruct" +import { DetectedRoomResponse } from "./DetectedRoomResponse" + +export const DetectedRoom = type({ + url: string(), + name: string(), + response: DetectedRoomResponse +}) diff --git a/packages/shared/lib/Schemas/DetectedRoomResponse.ts b/packages/shared/lib/Schemas/DetectedRoomResponse.ts new file mode 100644 index 0000000..f093570 --- /dev/null +++ b/packages/shared/lib/Schemas/DetectedRoomResponse.ts @@ -0,0 +1,7 @@ +import { defaulted, enums } from "superstruct" +import { IDetectedRoomResponse } from "../Interfaces/IDetectedRoomResponse" + +export const DetectedRoomResponse = defaulted( + enums(Object.values(IDetectedRoomResponse as any) as number[]), + IDetectedRoomResponse.NONE +) diff --git a/packages/shared/lib/Schemas/Room.ts b/packages/shared/lib/Schemas/Room.ts new file mode 100644 index 0000000..6b7fa20 --- /dev/null +++ b/packages/shared/lib/Schemas/Room.ts @@ -0,0 +1,24 @@ +import { boolean, defaulted, nullable, string, type } from "superstruct" +import { RoomPageCourses } from "./RoomPageCourses" + +export const Room = type({ + id: string(), + url: string(), + name: string(), + + cache: defaulted( + type({ + courses: defaulted(nullable(RoomPageCourses), () => null) + }), + () => ({}) + ), + + credentials: defaulted( + type({ + username: defaulted(string(), () => ""), + password: defaulted(string(), () => ""), + autoLogin: defaulted(boolean(), () => true) + }), + () => ({}) + ) +}) diff --git a/packages/shared/lib/Schemas/RoomCourse.ts b/packages/shared/lib/Schemas/RoomCourse.ts new file mode 100644 index 0000000..1e5df8f --- /dev/null +++ b/packages/shared/lib/Schemas/RoomCourse.ts @@ -0,0 +1,10 @@ +import { boolean, defaulted, nullable, number, string, type } from "superstruct" + +export const RoomCourse = type({ + url: string(), + name: string(), + courseId: string(), + + isPinned: defaulted(boolean(), () => false), + lastVisit: defaulted(nullable(number()), () => null) +}) diff --git a/packages/shared/lib/Schemas/RoomPageCourses.ts b/packages/shared/lib/Schemas/RoomPageCourses.ts new file mode 100644 index 0000000..4bf9d88 --- /dev/null +++ b/packages/shared/lib/Schemas/RoomPageCourses.ts @@ -0,0 +1,8 @@ +import { array, type } from "superstruct" +import { RoomCourse } from "./RoomCourse" + +export const RoomPageCourses = type({ + PAST: array(RoomCourse), + IN_PROGRESS: array(RoomCourse), + FUTURE: array(RoomCourse) +}) diff --git a/packages/shared/lib/Schemas/Services/getCourseCompareFn.tsx b/packages/shared/lib/Schemas/Services/getCourseCompareFn.tsx new file mode 100644 index 0000000..1a6c9f3 --- /dev/null +++ b/packages/shared/lib/Schemas/Services/getCourseCompareFn.tsx @@ -0,0 +1,18 @@ +import { ICoursesOrderBy } from "../../Interfaces/ICoursesOrderBy" +import { IExtractedCourse } from "../../Interfaces/IExtractedCourse" +import { strcmp } from "../../Utils/strcmp" + +const courseNameCompare = (a: IExtractedCourse, b: IExtractedCourse) => + strcmp(a.name, b.name) + +const courseLastVisitCompare = (a: IExtractedCourse, b: IExtractedCourse) => + (b.lastVisit ?? 0) - (a.lastVisit ?? 0) + +export const getCourseCompareFn = (orderBy: ICoursesOrderBy) => { + switch (orderBy) { + case ICoursesOrderBy.NAME: + return courseNameCompare + case ICoursesOrderBy.LAST_VISIT: + return courseLastVisitCompare + } +} diff --git a/packages/shared/lib/Schemas/Services/getCourseFilterFn.tsx b/packages/shared/lib/Schemas/Services/getCourseFilterFn.tsx new file mode 100644 index 0000000..de0c6ac --- /dev/null +++ b/packages/shared/lib/Schemas/Services/getCourseFilterFn.tsx @@ -0,0 +1,21 @@ +import { IExtractedCourseSlim } from "../../Interfaces/IExtractedCourseSlim" +import { IFilterCoursesByStatus } from "../../Interfaces/IFilterCoursesByStatus" +import { IPageExtractedCourses } from "../../Interfaces/IPageExtractedCourses" + +export const getCourseFilterFn = + (filterCoursesBy: IFilterCoursesByStatus) => + ( + pageCourses: IPageExtractedCourses | null + ): T[] => { + if (!pageCourses) { + return [] + } + switch (filterCoursesBy) { + case IFilterCoursesByStatus.PAST: + return pageCourses.PAST + case IFilterCoursesByStatus.IN_PROGRESS: + return pageCourses.IN_PROGRESS + case IFilterCoursesByStatus.FUTURE: + return pageCourses.FUTURE + } + } diff --git a/packages/shared/lib/Schemas/Settings.ts b/packages/shared/lib/Schemas/Settings.ts new file mode 100644 index 0000000..93c93bc --- /dev/null +++ b/packages/shared/lib/Schemas/Settings.ts @@ -0,0 +1,19 @@ +import { defaulted, enums, type } from "superstruct" +import { ICoursesOrderBy } from "../Interfaces/ICoursesOrderBy" +import { IFilterCoursesByStatus } from "../Interfaces/IFilterCoursesByStatus" +import { SettingsSelectedRoom } from "./SettingsSelectedRoom" + +export const Settings = defaulted( + type({ + selectedRoom: SettingsSelectedRoom, + coursesOrderBy: defaulted( + enums(Object.values(ICoursesOrderBy as any)), + () => ICoursesOrderBy.NAME + ), + filterCoursesByStatus: defaulted( + enums(Object.values(IFilterCoursesByStatus as any)), + () => IFilterCoursesByStatus.IN_PROGRESS + ) + }), + () => ({}) +) diff --git a/packages/shared/lib/Schemas/SettingsSelectedRoom.ts b/packages/shared/lib/Schemas/SettingsSelectedRoom.ts new file mode 100644 index 0000000..a0de647 --- /dev/null +++ b/packages/shared/lib/Schemas/SettingsSelectedRoom.ts @@ -0,0 +1,7 @@ +import { defaulted, nullable } from "superstruct" +import { Room } from "./Room" + +export const SettingsSelectedRoom = defaulted( + nullable(Room.schema.id), + () => null +) diff --git a/packages/shared/lib/Schemas/Storage.ts b/packages/shared/lib/Schemas/Storage.ts new file mode 100644 index 0000000..90d49d9 --- /dev/null +++ b/packages/shared/lib/Schemas/Storage.ts @@ -0,0 +1,11 @@ +import { array, defaulted, nullable, number, type } from "superstruct" +import { DetectedRoom } from "./DetectedRoom" +import { Room } from "./Room" +import { Settings } from "./Settings" + +export const Storage = type({ + rooms: defaulted(array(Room), () => []), + detectedRooms: defaulted(array(DetectedRoom), () => []), + lastMigration: defaulted(nullable(number()), () => null), + settings: Settings +}) diff --git a/packages/shared/lib/Storage/Hooks/useCourseCompareFn.tsx b/packages/shared/lib/Storage/Hooks/useCourseCompareFn.tsx new file mode 100644 index 0000000..49acc64 --- /dev/null +++ b/packages/shared/lib/Storage/Hooks/useCourseCompareFn.tsx @@ -0,0 +1,13 @@ +import { useMemo } from "react" +import { useExtensionStorageSlicer } from "../../Hooks/useExtensionStorageSlicer" +import { ICoursesOrderBy } from "../../Interfaces/ICoursesOrderBy" +import { getCourseCompareFn } from "../../Schemas/Services/getCourseCompareFn" +import { getCoursesOrderBy } from "../Slicers/StorageSettingsSlicers" + +export const useCourseCompareFn = () => { + const { value: orderBy } = useExtensionStorageSlicer( + getCoursesOrderBy, + ICoursesOrderBy.NAME + ) + return useMemo(() => getCourseCompareFn(orderBy), [orderBy]) +} diff --git a/packages/shared/lib/Storage/Hooks/useFilterPageCoursesFn.tsx b/packages/shared/lib/Storage/Hooks/useFilterPageCoursesFn.tsx new file mode 100644 index 0000000..88b5590 --- /dev/null +++ b/packages/shared/lib/Storage/Hooks/useFilterPageCoursesFn.tsx @@ -0,0 +1,13 @@ +import { useMemo } from "react" +import { useExtensionStorageSlicer } from "../../Hooks/useExtensionStorageSlicer" +import { IFilterCoursesByStatus } from "../../Interfaces/IFilterCoursesByStatus" +import { getCourseFilterFn } from "../../Schemas/Services/getCourseFilterFn" +import { getFilterCoursesBy } from "../Slicers/StorageSettingsSlicers" + +export const useFilterPageCoursesFn = () => { + const { value: filterCoursesBy } = useExtensionStorageSlicer( + getFilterCoursesBy, + IFilterCoursesByStatus.IN_PROGRESS + ) + return useMemo(() => getCourseFilterFn(filterCoursesBy), [filterCoursesBy]) +} diff --git a/packages/shared/lib/Storage/Migrations/migration0001.ts b/packages/shared/lib/Storage/Migrations/migration0001.ts new file mode 100644 index 0000000..d9f8473 --- /dev/null +++ b/packages/shared/lib/Storage/Migrations/migration0001.ts @@ -0,0 +1,58 @@ +import { nanoid } from "nanoid" +import { create } from "superstruct" +import browser from "webextension-polyfill" +import { IRoom } from "../../Interfaces/IRoom" +import { IStorage } from "../../Interfaces/IStorage" +import { IStorageMigration } from "../../Interfaces/IStorageMigration" +import * as logger from "../../logger" +import { Storage } from "../../Schemas/Storage" + +const hasOwn = (obj: any, prop: string) => + Object.hasOwnProperty.call(obj ?? {}, prop) + +export const migration0001: IStorageMigration = { + id: 1, + async up() { + const nextStorageData: IStorage = create({}, Storage) as IStorage + + const { rooms: prevRooms, settings: prevSettings } = + await browser.storage.local.get() + + const isV001 = prevSettings && hasOwn(prevSettings, "detectedRooms") + + if (isV001) { + logger.debug("previous storage version was 0.0.1, applying migrations...") + + if (Array.isArray(prevRooms)) { + for (const oldRoom of prevRooms) { + const migratedRoom = { + id: oldRoom.id ?? nanoid(), + url: oldRoom.url, + name: oldRoom.name, + credentials: { + username: oldRoom.credentials?.username ?? "", + password: oldRoom.credentials?.password ?? "", + autoLogin: oldRoom.credentials?.isAutoLoginEnabled ?? true + }, + cache: { + courses: null + } + } as IRoom + nextStorageData.rooms.push(migratedRoom) + } + } + + if (prevSettings.selectedRoom !== null) { + nextStorageData.settings.selectedRoom = + nextStorageData.rooms.find((i) => i.url === prevSettings.selectedRoom) + ?.id ?? null + } + + nextStorageData.settings.coursesOrderBy = prevSettings.coursesOrderBy + nextStorageData.detectedRooms = prevSettings.detectedRooms + + delete nextStorageData.lastMigration + await browser.storage.local.set(nextStorageData) + } + } +} diff --git a/packages/shared/lib/Storage/Migrations/migrations.ts b/packages/shared/lib/Storage/Migrations/migrations.ts new file mode 100644 index 0000000..56f4497 --- /dev/null +++ b/packages/shared/lib/Storage/Migrations/migrations.ts @@ -0,0 +1,3 @@ +import { migration0001 } from "./migration0001" + +export const migrations = [migration0001] diff --git a/packages/shared/lib/Storage/Mutations/StorageDetectedRoomMutations.ts b/packages/shared/lib/Storage/Mutations/StorageDetectedRoomMutations.ts new file mode 100644 index 0000000..727065d --- /dev/null +++ b/packages/shared/lib/Storage/Mutations/StorageDetectedRoomMutations.ts @@ -0,0 +1,34 @@ +import { IDetectedRoom } from "../../Interfaces/IDetectedRoom" +import { IDetectedRoomResponse } from "../../Interfaces/IDetectedRoomResponse" +import { IExtensionStorageMutation } from "../../Interfaces/IExtensionStorageMutation" + +export const createDetectedRoom: IExtensionStorageMutation< + Omit & Partial> +> = (state, { url, name, response }) => { + const detectedRoom: IDetectedRoom = { + url, + name, + response: response ?? IDetectedRoomResponse.NONE + } + + state.detectedRooms.push(detectedRoom) + + return detectedRoom +} + +export const updateDetectedRoom: IExtensionStorageMutation<{ + url: IDetectedRoom["url"] + recipe: (detectedRoom: IDetectedRoom) => void +}> = (state, { url, recipe }) => { + const detectedRoom = state.detectedRooms.find((i) => i.url === url) + + if (detectedRoom) { + recipe(detectedRoom) + } +} + +export const removeDetectedRoom: IExtensionStorageMutation< + IDetectedRoom["url"] +> = (state, url) => { + state.detectedRooms = state.detectedRooms.filter((i) => i.url !== url) +} diff --git a/packages/shared/lib/Storage/Mutations/StorageRoomsMutations.ts b/packages/shared/lib/Storage/Mutations/StorageRoomsMutations.ts new file mode 100644 index 0000000..0ff65e2 --- /dev/null +++ b/packages/shared/lib/Storage/Mutations/StorageRoomsMutations.ts @@ -0,0 +1,73 @@ +import { produce } from "immer" +import { nanoid } from "nanoid" +import normalizeUrl from "normalize-url" +import { IExtensionStorageMutation } from "../../Interfaces/IExtensionStorageMutation" +import { IExtractedCourse } from "../../Interfaces/IExtractedCourse" +import { IPageExtractedCourses } from "../../Interfaces/IPageExtractedCourses" +import { IRoom } from "../../Interfaces/IRoom" + +export const addRoom: IExtensionStorageMutation> = ( + state, + { url, name } +) => { + const normalizedUrl = normalizeUrl(url, { + removeSingleSlash: true, + removeTrailingSlash: true, + stripHash: true + }) + + const room: Partial = { + name, + id: nanoid(), + url: normalizedUrl + } + + state.rooms.push(room as IRoom) + + return room +} + +type IUpdateRoomPayload = { + id: IRoom["id"] + recipe: (room: Omit) => void +} + +export const updateRoom: IExtensionStorageMutation = ( + state, + { id, recipe } +) => { + state.rooms = state.rooms.map((i) => + i.id === id ? { ...produce(i, recipe), id } : i + ) +} + +type IUpdateRoomCoursePayload = { + roomId: IRoom["id"] + courseId: IExtractedCourse["courseId"] + recipe: (course: Omit) => void +} + +export const updateRoomCourse: IExtensionStorageMutation< + IUpdateRoomCoursePayload +> = (state, { roomId, courseId, recipe }) => { + const targetRoom = state.rooms.find((room) => room.id === roomId) + if (targetRoom) { + const targetCourse = Object.values( + targetRoom.cache.courses ?? ({} as unknown as IPageExtractedCourses) + ) + .flat(1) + .find((course) => course.courseId === courseId) + + if (targetCourse) { + recipe(targetCourse) + targetCourse.courseId = courseId + } + } +} + +export const removeRoom: IExtensionStorageMutation = ( + state, + id +) => { + state.rooms = state.rooms.filter((i) => i.id !== id) +} diff --git a/packages/shared/lib/Storage/Mutations/StorageSettingsMutations.ts b/packages/shared/lib/Storage/Mutations/StorageSettingsMutations.ts new file mode 100644 index 0000000..2635ef5 --- /dev/null +++ b/packages/shared/lib/Storage/Mutations/StorageSettingsMutations.ts @@ -0,0 +1,15 @@ +import { ICoursesOrderBy } from "../../Interfaces/ICoursesOrderBy" +import { IExtensionStorageMutation } from "../../Interfaces/IExtensionStorageMutation" +import { IRoom } from "../../Interfaces/IRoom" + +export const updateCoursesOrderBy: IExtensionStorageMutation< + ICoursesOrderBy +> = (state, payload) => { + state.settings.coursesOrderBy = payload +} + +export const updateSelectedRoom: IExtensionStorageMutation< + IRoom["id"] | null +> = (state, payload) => { + state.settings.selectedRoom = payload +} diff --git a/packages/shared/lib/Storage/Services/DetectedRoom/acceptDetectedRoom.tsx b/packages/shared/lib/Storage/Services/DetectedRoom/acceptDetectedRoom.tsx new file mode 100644 index 0000000..ba7a6e0 --- /dev/null +++ b/packages/shared/lib/Storage/Services/DetectedRoom/acceptDetectedRoom.tsx @@ -0,0 +1,25 @@ +import { IDetectedRoom } from "../../../Interfaces/IDetectedRoom" +import { IDetectedRoomResponse } from "../../../Interfaces/IDetectedRoomResponse" +import { applyStorageMutation } from "../../applyStorageMutation" +import { getSlicedStorageData } from "../../getSlicedStorageData" +import { updateDetectedRoom } from "../../Mutations/StorageDetectedRoomMutations" +import { addRoom } from "../../Mutations/StorageRoomsMutations" +import { listRooms } from "../../Slicers/StorageRoomSlicers" + +export const acceptDetectedRoom = async ({ url, name }: IDetectedRoom) => { + await applyStorageMutation(updateDetectedRoom, { + url, + recipe: (detectedRoom) => { + detectedRoom.response = IDetectedRoomResponse.ACCEPTED + } + }) + + const rooms = await getSlicedStorageData(listRooms) + + const isDetectedRoomAlreadyRegistered = + rooms.find((i) => i.url === url) !== undefined + + if (!isDetectedRoomAlreadyRegistered) { + await applyStorageMutation(addRoom, { url, name }) + } +} diff --git a/packages/shared/lib/Storage/Services/DetectedRoom/rejectDetectedRoom.tsx b/packages/shared/lib/Storage/Services/DetectedRoom/rejectDetectedRoom.tsx new file mode 100644 index 0000000..9e9debd --- /dev/null +++ b/packages/shared/lib/Storage/Services/DetectedRoom/rejectDetectedRoom.tsx @@ -0,0 +1,12 @@ +import { IDetectedRoom } from "../../../Interfaces/IDetectedRoom" +import { IDetectedRoomResponse } from "../../../Interfaces/IDetectedRoomResponse" +import { applyStorageMutation } from "../../applyStorageMutation" +import { updateDetectedRoom } from "../../Mutations/StorageDetectedRoomMutations" + +export const rejectDetectedRoom = ({ url }: IDetectedRoom) => + applyStorageMutation(updateDetectedRoom, { + url, + recipe: (detectedRoom) => { + detectedRoom.response = IDetectedRoomResponse.REJECTED + } + }) diff --git a/packages/shared/lib/Storage/Slicers/StorageDetectedRoomSlicers.ts b/packages/shared/lib/Storage/Slicers/StorageDetectedRoomSlicers.ts new file mode 100644 index 0000000..e86506e --- /dev/null +++ b/packages/shared/lib/Storage/Slicers/StorageDetectedRoomSlicers.ts @@ -0,0 +1,14 @@ +import { IDetectedRoom } from "../../Interfaces/IDetectedRoom" +import { IExtensionStorageSlicer } from "../../Interfaces/IExtensionStorageSlicer" + +export const getDetectedRoomByUrl: ( + url: IDetectedRoom["url"] +) => IExtensionStorageSlicer = (url) => (state) => { + return state.detectedRooms.find((detectedRoom) => + url.startsWith(detectedRoom.url) + ) +} + +export const listDetectedRooms: IExtensionStorageSlicer = ( + state +) => state.detectedRooms diff --git a/packages/shared/lib/Storage/Slicers/StorageRoomSlicers.ts b/packages/shared/lib/Storage/Slicers/StorageRoomSlicers.ts new file mode 100644 index 0000000..bab9efc --- /dev/null +++ b/packages/shared/lib/Storage/Slicers/StorageRoomSlicers.ts @@ -0,0 +1,22 @@ +import { IExtensionStorageSlicer } from "../../Interfaces/IExtensionStorageSlicer" +import { IRoom } from "../../Interfaces/IRoom" + +export const getRoomById: ( + id: IRoom["id"] +) => IExtensionStorageSlicer = (id) => (state) => + state.rooms.find((i) => i.id === id) || null + +export const hasRoom: (id: IRoom["id"]) => IExtensionStorageSlicer = + (id) => (state) => + state.rooms.findIndex((i) => i.id === id) > -1 + +export const listRooms: IExtensionStorageSlicer = (state) => + state.rooms + +export const listRoomsOmitCache: IExtensionStorageSlicer< + Omit[] +> = (state) => { + /* eslint-disable @typescript-eslint/no-unused-vars */ + return state.rooms.map(({ cache: _, ...room }) => room) + /* eslint-enable @typescript-eslint/no-unused-vars */ +} diff --git a/packages/shared/lib/Storage/Slicers/StorageSettingsSlicers.ts b/packages/shared/lib/Storage/Slicers/StorageSettingsSlicers.ts new file mode 100644 index 0000000..b96b7a4 --- /dev/null +++ b/packages/shared/lib/Storage/Slicers/StorageSettingsSlicers.ts @@ -0,0 +1,14 @@ +import { IExtensionStorageSlicer } from "../../Interfaces/IExtensionStorageSlicer" +import { IStorage } from "../../Interfaces/IStorage" + +export const getCoursesOrderBy: IExtensionStorageSlicer< + IStorage["settings"]["coursesOrderBy"] +> = (state) => state.settings.coursesOrderBy + +export const getFilterCoursesBy: IExtensionStorageSlicer< + IStorage["settings"]["filterCoursesByStatus"] +> = (state) => state.settings.filterCoursesByStatus + +export const getSelectedRoom: IExtensionStorageSlicer< + IStorage["settings"]["selectedRoom"] +> = (state) => state.settings.selectedRoom diff --git a/packages/shared/lib/Storage/applyStorageMutation.ts b/packages/shared/lib/Storage/applyStorageMutation.ts new file mode 100644 index 0000000..537bb3b --- /dev/null +++ b/packages/shared/lib/Storage/applyStorageMutation.ts @@ -0,0 +1,24 @@ +import { produce } from "immer" +import { create } from "superstruct" +import browser from "webextension-polyfill" +import { data } from "../centralStorage" +import { IExtensionStorageMutation } from "../Interfaces/IExtensionStorageMutation" +import { Storage } from "../Schemas/Storage" + +export const applyStorageMutation = async ( + recipe: IExtensionStorageMutation, + payload: T +) => { + let mutationReturnValue: any + + const updatedData = create( + produce(await data, (draft) => { + mutationReturnValue = recipe(draft, payload) + }), + Storage + ) + + await browser.storage.local.set(updatedData) + + return mutationReturnValue +} diff --git a/packages/shared/lib/Storage/getSlicedStorageData.ts b/packages/shared/lib/Storage/getSlicedStorageData.ts new file mode 100644 index 0000000..978c315 --- /dev/null +++ b/packages/shared/lib/Storage/getSlicedStorageData.ts @@ -0,0 +1,9 @@ +import { data } from "../centralStorage" +import { IExtensionStorageSlicer } from "../Interfaces/IExtensionStorageSlicer" +import { IStorage } from "../Interfaces/IStorage" +import { MayBePromise } from "../Interfaces/MayBePromise" + +export const getSlicedStorageData = async ( + slicer: IExtensionStorageSlicer, + incomingData: MayBePromise = data +): Promise => slicer(await incomingData) diff --git a/packages/shared/lib/Storage/getStorageData.ts b/packages/shared/lib/Storage/getStorageData.ts new file mode 100644 index 0000000..fb93bcc --- /dev/null +++ b/packages/shared/lib/Storage/getStorageData.ts @@ -0,0 +1,23 @@ +import { create } from "superstruct" +import browser from "webextension-polyfill" +import { IStorage } from "../Interfaces/IStorage" +import * as logger from "../logger" +import { Storage } from "../Schemas/Storage" +import { runMigrations } from "./runMigrations" + +export const getStorageData = async () => { + try { + await runMigrations() + const storageData = await browser.storage.local.get() + return create(storageData, Storage) as IStorage + } catch (e) { + // XXX: something went wrong + + logger.info( + "cloud not load previous storage.local data :( | cleaning storage and loading default data..." + ) + await browser.storage.local.clear() + + return create({}, Storage) as IStorage + } +} diff --git a/packages/shared/lib/Storage/makeStorageMutator.ts b/packages/shared/lib/Storage/makeStorageMutator.ts new file mode 100644 index 0000000..ad4e949 --- /dev/null +++ b/packages/shared/lib/Storage/makeStorageMutator.ts @@ -0,0 +1,7 @@ +import { IExtensionStorageMutation } from "../Interfaces/IExtensionStorageMutation" +import { applyStorageMutation } from "./applyStorageMutation" + +export const makeStorageMutator = + (recipe: IExtensionStorageMutation) => + (payload: T) => + applyStorageMutation(recipe, payload) diff --git a/packages/shared/lib/Storage/runMigrations.ts b/packages/shared/lib/Storage/runMigrations.ts new file mode 100644 index 0000000..50ac776 --- /dev/null +++ b/packages/shared/lib/Storage/runMigrations.ts @@ -0,0 +1,35 @@ +import browser from "webextension-polyfill" +import * as logger from "../logger" +import { migrations } from "./Migrations/migrations" + +let uniqueRunner: null | Promise = null + +export const runMigrations = async () => { + if (uniqueRunner !== null) { + await uniqueRunner + return + } + + uniqueRunner = (async () => { + const { lastMigration = -Infinity } = await browser.storage.local.get( + "lastMigration" + ) + const pendingMigrations = migrations.filter(({ id }) => id > lastMigration) + + if (pendingMigrations.length > 0) { + for (const pendingMigration of pendingMigrations) { + logger.info(`migration "${pendingMigration.id}": starting...`) + await pendingMigration.up() + logger.info(`migration "${pendingMigration.id}": done.`) + + await browser.storage.local.set({ lastMigration: pendingMigration.id }) + logger.debug( + `storage.local.lastMigration: updated to "${pendingMigration.id}".` + ) + } + } + })() + + await uniqueRunner + uniqueRunner = null +} diff --git a/packages/shared/lib/Utils/isUrl.ts b/packages/shared/lib/Utils/isUrl.ts new file mode 100644 index 0000000..3674c15 --- /dev/null +++ b/packages/shared/lib/Utils/isUrl.ts @@ -0,0 +1,8 @@ +export const isUrl = (value: string) => { + try { + new URL(value) + return true + } catch (e) { + return false + } +} diff --git a/packages/shared/lib/Utils/strcmp.ts b/packages/shared/lib/Utils/strcmp.ts new file mode 100644 index 0000000..93d860e --- /dev/null +++ b/packages/shared/lib/Utils/strcmp.ts @@ -0,0 +1 @@ +export const strcmp = (a: string, b: string) => a.localeCompare(b) diff --git a/packages/shared/lib/centralStorage.ts b/packages/shared/lib/centralStorage.ts new file mode 100644 index 0000000..412783a --- /dev/null +++ b/packages/shared/lib/centralStorage.ts @@ -0,0 +1,33 @@ +import browser from "webextension-polyfill" +import { ICentralStorageListener } from "./Interfaces/ICentralStorageListener" +import { IStorage } from "./Interfaces/IStorage" +import { getStorageData } from "./Storage/getStorageData" + +export let data: Promise = getStorageData() + +const listeners = new Set() + +export const addListener = (callback: ICentralStorageListener) => + void listeners.add(callback) + +export const removeListener = (callback: ICentralStorageListener) => + void listeners.delete(callback) + +const callListeners = () => { + for (const callback of listeners) { + callback() + } +} + +const handleStorageChange = (changes?: any, area?: string) => { + if (area === "local") { + data = getStorageData() + callListeners() + } +} + +const setup = () => { + browser.storage.onChanged.addListener(handleStorageChange) +} + +setup() diff --git a/packages/shared/lib/i18n/getMessage.ts b/packages/shared/lib/i18n/getMessage.ts new file mode 100644 index 0000000..b6394e6 --- /dev/null +++ b/packages/shared/lib/i18n/getMessage.ts @@ -0,0 +1,4 @@ +import browser from "webextension-polyfill" + +export const getMessage = (messageName: string, substitutions?: any) => + browser.i18n.getMessage(messageName, substitutions) diff --git a/packages/shared/lib/logger.ts b/packages/shared/lib/logger.ts new file mode 100644 index 0000000..a5a71b5 --- /dev/null +++ b/packages/shared/lib/logger.ts @@ -0,0 +1,9 @@ +const identifier = "ava-pro" + +export const info = (...args: any[]) => { + console.info(`[${identifier}::info]`, ...args) +} + +export const debug = (...args: any[]) => { + console.info(`[${identifier}::debug]`, ...args) +} diff --git a/packages/shared/lib/makeCrawlerFetch.ts b/packages/shared/lib/makeCrawlerFetch.ts new file mode 100644 index 0000000..04e5a3a --- /dev/null +++ b/packages/shared/lib/makeCrawlerFetch.ts @@ -0,0 +1,24 @@ +import normalizeUrl from "normalize-url" +import { ICrawlerFetch } from "./Interfaces/ICrawlerFetch" + +export const makeCrawlerFetch = (baseUrl: string) => { + const normalizedRoomURL = normalizeUrl(baseUrl, { removeTrailingSlash: true }) + + const crawlerFetch: ICrawlerFetch = async ({ url, options }) => { + const targetUrl = normalizedRoomURL + url + + const response = await fetch(targetUrl, { + ...options, + mode: "cors", + credentials: "include" + }) + + if (!response.ok) { + throw new Error("Response not ok") + } + + return response + } + + return crawlerFetch +} diff --git a/packages/shared/lib/storageSyncSlicer.ts b/packages/shared/lib/storageSyncSlicer.ts new file mode 100644 index 0000000..20c2d4a --- /dev/null +++ b/packages/shared/lib/storageSyncSlicer.ts @@ -0,0 +1,33 @@ +import equal from "fast-deep-equal" +import * as centralStorage from "./centralStorage" +import { IExtensionStorageSlicer } from "./Interfaces/IExtensionStorageSlicer" +import { getSlicedStorageData } from "./Storage/getSlicedStorageData" + +export const storageSyncSlicer = ( + slicer: IExtensionStorageSlicer, + callback: (state: T) => void, + setLoadingStatus?: (status: boolean) => void +) => { + let prevState: null | T = null + + const checkChanges = async () => { + setLoadingStatus?.(true) + + const state = await getSlicedStorageData(slicer) + + if (!equal(state, prevState)) { + callback(state) + prevState = state + } + + setLoadingStatus?.(false) + } + + void checkChanges() + + centralStorage.addListener(checkChanges) + + return () => { + centralStorage.removeListener(checkChanges) + } +} diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..0474230 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,16 @@ +{ + "name": "@ava-pro/shared", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "immer": "^9.0.7", + "nanoid": "^3.1.30", + "normalize-url": "^7.0.2", + "react": "^17.0.2", + "superstruct": "^0.15.3", + "webextension-polyfill": "^0.8.0" + }, + "devDependencies": { + "@types/webextension-polyfill": "^0.8.2", + "typescript": "^4.5.2" + } +} diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..43c8e04 --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2016", + "lib": ["DOM", "ESNext"], + "module": "ESNext", + "strictNullChecks": true, + "rootDir": "./lib", + "moduleResolution": "node", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/packages/webextension/.parcelrc b/packages/webextension/.parcelrc new file mode 100644 index 0000000..fb5969c --- /dev/null +++ b/packages/webextension/.parcelrc @@ -0,0 +1,15 @@ +{ + "extends": [ + "@parcel/config-default", + "@parcel/config-webextension" + ], + "transformers": { + "*.{ts,tsx}": [ + "@parcel/transformer-typescript-tsc" + ] + }, + "resolvers": [ + "parcel-resolver-pnpm", + "..." + ] +} \ No newline at end of file diff --git a/packages/webextension/package.json b/packages/webextension/package.json new file mode 100644 index 0000000..e448567 --- /dev/null +++ b/packages/webextension/package.json @@ -0,0 +1,52 @@ +{ + "name": "@ava-pro/webextension", + "targets": { + "dev": { + "sourceMap": { + "inline": true, + "inlineSources": true + } + }, + "prod": {} + }, + "scripts": { + "clear:dist": "rm -rf dist", + "start": "parcel watch src/manifest.json --target dev", + "build": "parcel build src/manifest.json --target prod" + }, + "dependencies": { + "@ava-pro/crawlers": "workspace:*", + "@ava-pro/shared": "workspace:*", + "@emotion/react": "^11.7.0", + "@emotion/styled": "^11.6.0", + "@hookform/error-message": "^2.0.0", + "@mui/icons-material": "^5.2.0", + "@mui/material": "^5.2.1", + "dompurify": "^2.3.3", + "normalize-url": "^7.0.2", + "notistack": "^2.0.3", + "postcss": "^8.4.4", + "react": "^17.0.2", + "react-async": "^10.0.1", + "react-dom": "^17.0.2", + "react-hook-form": "^7.20.4", + "react-query": "^3.33.7", + "react-router": "^6.0.2", + "react-router-dom": "^6.0.2", + "react-virtuoso": "^2.2.8", + "typescript": "^4.5.2", + "use-context-selector": "^1.3.9", + "use-debounce": "^7.0.1", + "webextension-polyfill": "^0.8.0" + }, + "devDependencies": { + "@parcel/config-default": "^2.0.1", + "@parcel/config-webextension": "^2.0.1", + "@parcel/transformer-typescript-tsc": "^2.0.1", + "@types/dompurify": "^2.3.1", + "@types/react": "^17.0.37", + "@types/react-dom": "^17.0.11", + "parcel": "^2.0.1", + "parcel-resolver-pnpm": "^0.0.1" + } +} diff --git a/packages/webextension/src/ABOUT.ts b/packages/webextension/src/ABOUT.ts new file mode 100644 index 0000000..ba48a9f --- /dev/null +++ b/packages/webextension/src/ABOUT.ts @@ -0,0 +1,12 @@ +import browser from "webextension-polyfill" + +export const ABOUT = { + VERSION: browser.runtime.getManifest().version ?? "unknown", + + PROJECT_LICENSE: "AGLP-3.0", + PROJECT_REPOSITORY: "https://github.com/guesant/ava-pro", + + AUTHOR_NAME: "Gabriel R. Antunes", + AUTHOR_EMAIL: "gabrielrodantunes@gmail.com", + AUTHOR_GITHUB: "https://github.com/guesant" +} diff --git a/packages/webextension/src/BackgroundScript/CheckChatUpdates/HandleCheckChatsUpdates.ts b/packages/webextension/src/BackgroundScript/CheckChatUpdates/HandleCheckChatsUpdates.ts new file mode 100644 index 0000000..0ac449e --- /dev/null +++ b/packages/webextension/src/BackgroundScript/CheckChatUpdates/HandleCheckChatsUpdates.ts @@ -0,0 +1,60 @@ +import { Conversations } from "@ava-pro/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Conversations" +import { CheckLogin } from "@ava-pro/crawlers/lib/Scrappers/CheckLogin/CheckLogin" +import { ExtractTokensUserId } from "@ava-pro/crawlers/lib/Scrappers/ExtractTokens/ExtractTokens" +import { Login } from "@ava-pro/crawlers/lib/Scrappers/Login/Login" +import { data } from "@ava-pro/shared/lib/centralStorage" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { makeCrawlerFetch } from "@ava-pro/shared/lib/makeCrawlerFetch" +import { notifyUnreadRoomMessages } from "./NotifyUnreadRoomMessages" + +const prevChatData = new Map() + +export const handleCheckChatsUpdates = async () => { + const { rooms } = await data + + for (const { + id, + url, + name, + credentials: { username, password, autoLogin } + } of rooms) { + const crawlerFetch = makeCrawlerFetch(url) + + let isLoggedIn = await CheckLogin(crawlerFetch)() + + if (!isLoggedIn && autoLogin) { + isLoggedIn = await Login(crawlerFetch)(username, password, true) + } + + if (isLoggedIn) { + const userId = await ExtractTokensUserId(crawlerFetch)() + + if (userId !== null) { + const { contacts } = await Conversations(crawlerFetch)({ + userid: userId + }) + + const unreadChats = contacts.filter((i) => (i.unreadcount ?? 0) > 0) + + const unreadChatsCount = unreadChats.length + + const unreadMessagesCount = unreadChats.reduce( + (acc, i) => acc + (i.unreadcount ?? 0), + 0 + ) + + const prevUnreadMessagesCount = prevChatData.get(id) ?? 0 + + if (unreadMessagesCount !== prevUnreadMessagesCount) { + await notifyUnreadRoomMessages( + unreadChatsCount, + unreadMessagesCount, + name + ) + } + + prevChatData.set(id, unreadMessagesCount) + } + } + } +} diff --git a/packages/webextension/src/BackgroundScript/CheckChatUpdates/NotifyUnreadRoomMessages.ts b/packages/webextension/src/BackgroundScript/CheckChatUpdates/NotifyUnreadRoomMessages.ts new file mode 100644 index 0000000..b17be2e --- /dev/null +++ b/packages/webextension/src/BackgroundScript/CheckChatUpdates/NotifyUnreadRoomMessages.ts @@ -0,0 +1,15 @@ +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import browser from "webextension-polyfill" + +export const notifyUnreadRoomMessages = async ( + unreadChatsCount: number, + unreadMessagesCount: number, + roomName: IRoom["name"] +) => { + await browser.notifications.create({ + type: "basic", + title: `Você possui novas mensagens não lidas no ambiente "${roomName}"`, + message: `${unreadMessagesCount} mensagem(s) de ${unreadChatsCount} chat(s).`, + iconUrl: browser.runtime.getURL("icons/icon.png") + }) +} diff --git a/packages/webextension/src/BackgroundScript/CheckChatUpdates/SetupCheckChatsUpdates.ts b/packages/webextension/src/BackgroundScript/CheckChatUpdates/SetupCheckChatsUpdates.ts new file mode 100644 index 0000000..e221707 --- /dev/null +++ b/packages/webextension/src/BackgroundScript/CheckChatUpdates/SetupCheckChatsUpdates.ts @@ -0,0 +1,19 @@ +import { handleCheckChatsUpdates } from "./HandleCheckChatsUpdates" + +const interval = 3 * 60 * 1000 + +let intervalId: any | null = null + +const setupCheckChatsUpdatesInterval = (interval: number) => { + intervalId !== null && clearInterval(intervalId) + + intervalId = setInterval(() => { + handleCheckChatsUpdates().catch(() => {}) + }, interval) + + handleCheckChatsUpdates().catch(() => {}) +} + +export const setupCheckChatsUpdates = () => { + setupCheckChatsUpdatesInterval(interval) +} diff --git a/packages/webextension/src/BackgroundScript/RuntimeMessageHandlers/HandleNotifyDetectedRoom/HandleNotifyDetectedRoom.ts b/packages/webextension/src/BackgroundScript/RuntimeMessageHandlers/HandleNotifyDetectedRoom/HandleNotifyDetectedRoom.ts new file mode 100644 index 0000000..771b488 --- /dev/null +++ b/packages/webextension/src/BackgroundScript/RuntimeMessageHandlers/HandleNotifyDetectedRoom/HandleNotifyDetectedRoom.ts @@ -0,0 +1,12 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { IDetectedRoom } from "@ava-pro/shared/lib/Interfaces/IDetectedRoom" +import browser from "webextension-polyfill" + +export const handleNotifyDetectedRoom = async ({ name }: IDetectedRoom) => { + await browser.notifications.create({ + type: "basic", + title: getMessage("notification_newDetectedRoom_title"), + message: getMessage("notification_newDetectedRoom_description", name), + iconUrl: browser.runtime.getURL("icons/icon.png") + }) +} diff --git a/packages/webextension/src/BackgroundScript/RuntimeMessageHandlers/setupRuntimeMessagesListeners.ts b/packages/webextension/src/BackgroundScript/RuntimeMessageHandlers/setupRuntimeMessagesListeners.ts new file mode 100644 index 0000000..128a576 --- /dev/null +++ b/packages/webextension/src/BackgroundScript/RuntimeMessageHandlers/setupRuntimeMessagesListeners.ts @@ -0,0 +1,10 @@ +import { IRuntimeMessageType } from "@ava-pro/shared/lib/Interfaces/IRuntimeMessageType" +import { addRuntimeMessageListener } from "@ava-pro/shared/lib/RuntimeMessage/addRuntimeMessageListener" +import { handleNotifyDetectedRoom } from "./HandleNotifyDetectedRoom/HandleNotifyDetectedRoom" + +export const setupRuntimeMessagesListeners = () => { + addRuntimeMessageListener( + IRuntimeMessageType.NOTIFY_DETECTED_ROOM, + handleNotifyDetectedRoom + ) +} diff --git a/packages/webextension/src/BackgroundScript/index.ts b/packages/webextension/src/BackgroundScript/index.ts new file mode 100644 index 0000000..03e406d --- /dev/null +++ b/packages/webextension/src/BackgroundScript/index.ts @@ -0,0 +1,5 @@ +import { setupCheckChatsUpdates } from "./CheckChatUpdates/SetupCheckChatsUpdates" +import { setupRuntimeMessagesListeners } from "./RuntimeMessageHandlers/setupRuntimeMessagesListeners" + +setupRuntimeMessagesListeners() +setupCheckChatsUpdates() diff --git a/packages/webextension/src/BrowserAction/App.css b/packages/webextension/src/BrowserAction/App.css new file mode 100644 index 0000000..ad457f7 --- /dev/null +++ b/packages/webextension/src/BrowserAction/App.css @@ -0,0 +1,32 @@ +a { + color: inherit; + text-decoration: none; +} + +p { + margin: 0; + padding: 0; + line-height: 1; +} + +img { + display: block; + max-width: 100%; +} + +.page { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; +} + +html, +body, +.app, +.page { + width: 400px; + height: 450px; + display: flex; + overflow: hidden; +} diff --git a/packages/webextension/src/BrowserAction/App.tsx b/packages/webextension/src/BrowserAction/App.tsx new file mode 100644 index 0000000..03b93d7 --- /dev/null +++ b/packages/webextension/src/BrowserAction/App.tsx @@ -0,0 +1,95 @@ +import Box from "@mui/material/Box" +import { lazy, Suspense } from "react" +import { Navigate, Route, Routes } from "react-router-dom" +import "./App.css" +import Loading from "./Components/Loading/Loading" +import AppInitialRoute from "./Pages/AppInitialRoute/AppInitialRoute" + +const PageErrorNotFound = lazy( + () => import("./Pages/PageErrorNotFound/PageErrorNotFound") +) + +const AddRoom = lazy(() => import("./Pages/AddRoom/AddRoom")) + +const ListRooms = lazy(() => import("./Pages/ListRooms/ListRooms")) + +const ListAllDetectedRooms = lazy( + () => import("./Pages/ListAllDetectedRooms/ListAllDetectedRooms") +) + +const ShowRoom = lazy(() => import("./Pages/ShowRoom/ShowRoom")) + +const ShowRoomOverview = lazy( + () => import("./Pages/ShowRoomOverview/ShowRoomOverview") +) + +const ShowRoomCredentials = lazy( + () => import("./Pages/ShowRoomCredentials/ShowRoomCredentials") +) + +const ShowRoomOverviewCourses = lazy( + () => import("./Pages/ShowRoomOverviewCourses/ShowRoomOverviewCourses") +) + +const ShowRoomOverviewChats = lazy( + () => import("./Pages/ShowRoomOverviewChats/ShowRoomOverviewChats") +) + +const ShowRoomSearchCourses = lazy( + () => import("./Pages/ShowRoomSearchCourses/ShowRoomSearchCourses") +) + +const ShowRoomSearchChats = lazy( + () => import("./Pages/ShowRoomSearchChats/ShowRoomSearchChats") +) + +const ShowRoomEdit = lazy(() => import("./Pages/ShowRoomEdit/ShowRoomEdit")) + +const ShowRoomChat = lazy(() => import("./Pages/ShowRoomChat/ShowRoomChat")) + +const Settings = lazy(() => import("./Pages/Settings/Settings")) + +const App = () => ( + + }> + + } /> + + } /> + + } /> + + }> + }> + } /> + + + }> + } /> + + + } /> + + + } /> + } /> + + + } /> + + } /> + + } /> + + + } /> + + } /> + + } /> + + + +) + +export default App diff --git a/packages/webextension/src/BrowserAction/Components/AppErrorMessage/AppErrorMessage.tsx b/packages/webextension/src/BrowserAction/Components/AppErrorMessage/AppErrorMessage.tsx new file mode 100644 index 0000000..28ed427 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/AppErrorMessage/AppErrorMessage.tsx @@ -0,0 +1,12 @@ +import { ErrorMessage } from "@hookform/error-message" +import Alert from "@mui/material/Alert" +import { FC } from "react" + +const AppErrorMessage: FC = (props) => ( + {message}} + /> +) + +export default AppErrorMessage diff --git a/packages/webextension/src/BrowserAction/Components/ContactCard/ContactCard.tsx b/packages/webextension/src/BrowserAction/Components/ContactCard/ContactCard.tsx new file mode 100644 index 0000000..8e14b67 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/ContactCard/ContactCard.tsx @@ -0,0 +1,78 @@ +import { IMessageAreaContact } from "@ava-pro/crawlers/lib/Typings/IMessageAreaContact" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Avatar from "@mui/material/Avatar" +import Badge from "@mui/material/Badge" +import ListItem from "@mui/material/ListItem" +import ListItemAvatar from "@mui/material/ListItemAvatar" +import ListItemSecondaryAction from "@mui/material/ListItemSecondaryAction" +import ListItemText from "@mui/material/ListItemText" +import Typography from "@mui/material/Typography" +import { FC } from "react" + +type IContactCardProps = { + contact: IMessageAreaContact + + showOnlineStatus?: boolean + showLastChatMessage?: boolean + showUnreadBadge?: boolean + + divider?: boolean +} + +const ContactCard: FC = ({ + contact, + divider, + showOnlineStatus, + showLastChatMessage, + showUnreadBadge +}) => { + const isOnlineBadgeVisible = showOnlineStatus && contact.isonline + + const isUnreadBadgeVisible = showUnreadBadge && !contact.isread + + return ( + + + + + + + + + {contact.fullname} + + {showLastChatMessage && ( + + + {contact.sentfromcurrentuser + ? getMessage("component_contactCard_lastMessage_sentBy_me") + : ""} + + + {contact.lastmessage} + + )} + + + {isUnreadBadgeVisible && ( + + + + )} + + ) +} + +export default ContactCard diff --git a/packages/webextension/src/BrowserAction/Components/CourseList/CourseList.tsx b/packages/webextension/src/BrowserAction/Components/CourseList/CourseList.tsx new file mode 100644 index 0000000..a57cd1f --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/CourseList/CourseList.tsx @@ -0,0 +1,68 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { IExtractedCourse } from "@ava-pro/shared/lib/Interfaces/IExtractedCourse" +import Divider from "@mui/material/Divider" +import List from "@mui/material/List" +import ListSubheader from "@mui/material/ListSubheader" +import { FC, useMemo } from "react" +import CourseListItem from "../CourseListItem/CourseListItem" + +type ICourseListProps = { + courses: IExtractedCourse[] + + keepTrailingDivider?: boolean +} + +const CourseList: FC = ({ + courses, + keepTrailingDivider = true +}) => { + const pinnedCourses = useMemo( + () => courses.filter((course) => course.isPinned), + [courses] + ) + + const notPinnedCourses = useMemo( + () => courses.filter((course) => !course.isPinned), + [courses] + ) + + const hasPinnedCourses = pinnedCourses.length > 0 + + const hasNotPinnedCourses = notPinnedCourses.length > 0 + + return ( + + {hasPinnedCourses && ( + <> + + {getMessage("component_courseList_subheader_pinned")} + + + + + {pinnedCourses.map((course) => ( + + ))} + + )} + + {hasNotPinnedCourses && ( + <> + {hasPinnedCourses && ( + + {getMessage("component_courseList_subheader_notPinned")} + + )} + + {!hasPinnedCourses && keepTrailingDivider && } + + {notPinnedCourses.map((course) => ( + + ))} + + )} + + ) +} + +export default CourseList diff --git a/packages/webextension/src/BrowserAction/Components/CourseListItem/CourseListItem.module.css b/packages/webextension/src/BrowserAction/Components/CourseListItem/CourseListItem.module.css new file mode 100644 index 0000000..6b33884 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/CourseListItem/CourseListItem.module.css @@ -0,0 +1,3 @@ +.item:not(.pinned):not(:hover) .secondaryActions { + display: none !important; +} diff --git a/packages/webextension/src/BrowserAction/Components/CourseListItem/CourseListItem.tsx b/packages/webextension/src/BrowserAction/Components/CourseListItem/CourseListItem.tsx new file mode 100644 index 0000000..b177355 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/CourseListItem/CourseListItem.tsx @@ -0,0 +1,43 @@ +import { IExtractedCourse } from "@ava-pro/shared/lib/Interfaces/IExtractedCourse" +import PushPinIcon from "@mui/icons-material/PushPin" +import PushPinOutlinedIcon from "@mui/icons-material/PushPinOutlined" +import IconButton from "@mui/material/IconButton" +import ListItem from "@mui/material/ListItem" +import ListItemSecondaryAction from "@mui/material/ListItemSecondaryAction" +import ListItemText from "@mui/material/ListItemText" +import Typography from "@mui/material/Typography" +import { FC, useCallback } from "react" +import { useCoursePinState } from "../../Hooks/useCoursePinState" +import * as classes from "./CourseListItem.module.css" + +const CourseListItem: FC<{ course: IExtractedCourse }> = ({ course }) => { + const { isPinned, togglePinState } = useCoursePinState(course) + + const handleClick = useCallback(() => { + window.open(course.url) + }, [course.url]) + + return ( + + + {course.name} + + + + {isPinned ? : } + + + + ) +} + +export default CourseListItem diff --git a/packages/webextension/src/BrowserAction/Components/DetectedRoomList/DetectedRoomList.tsx b/packages/webextension/src/BrowserAction/Components/DetectedRoomList/DetectedRoomList.tsx new file mode 100644 index 0000000..72437cb --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/DetectedRoomList/DetectedRoomList.tsx @@ -0,0 +1,47 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { IDetectedRoom } from "@ava-pro/shared/lib/Interfaces/IDetectedRoom" +import Box from "@mui/material/Box" +import Divider from "@mui/material/Divider" +import List from "@mui/material/List" +import ListSubheader from "@mui/material/ListSubheader" +import { FC } from "react" +import DetectedRoomListItem from "../DetectedRoomListItem/DetectedRoomListItem" + +type IDetectedRoomListProps = { + detectedRooms: IDetectedRoom[] + showSubHeader?: boolean + showDivider?: boolean +} + +const DetectedRoomList: FC = ({ + detectedRooms, + showSubHeader = true, + showDivider = true +}) => { + if (detectedRooms.length === 0) { + return null + } + + return ( + + + {showSubHeader && ( + + {getMessage("component_detectedRoomList_subheader")} + + )} + + {showDivider && } + + {detectedRooms.map((detectedRoom) => ( + + ))} + + + ) +} + +export default DetectedRoomList diff --git a/packages/webextension/src/BrowserAction/Components/DetectedRoomListItem/DetectedRoomListItem.tsx b/packages/webextension/src/BrowserAction/Components/DetectedRoomListItem/DetectedRoomListItem.tsx new file mode 100644 index 0000000..3d1722a --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/DetectedRoomListItem/DetectedRoomListItem.tsx @@ -0,0 +1,112 @@ +import { IDetectedRoom } from "@ava-pro/shared/lib/Interfaces/IDetectedRoom" +import { IDetectedRoomResponse } from "@ava-pro/shared/lib/Interfaces/IDetectedRoomResponse" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { removeDetectedRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageDetectedRoomMutations" +import { acceptDetectedRoom } from "@ava-pro/shared/lib/Storage/Services/DetectedRoom/acceptDetectedRoom" +import { rejectDetectedRoom } from "@ava-pro/shared/lib/Storage/Services/DetectedRoom/rejectDetectedRoom" +import CheckIcon from "@mui/icons-material/Check" +import CloseIcon from "@mui/icons-material/Close" +import DeleteIcon from "@mui/icons-material/Delete" +import IconButton, { IconButtonProps } from "@mui/material/IconButton" +import ListItem from "@mui/material/ListItem" +import ListItemSecondaryAction from "@mui/material/ListItemSecondaryAction" +import ListItemText from "@mui/material/ListItemText" +import Typography from "@mui/material/Typography" +import { FC, useCallback } from "react" +import { useAsync } from "react-async" + +const IconButtonResponseActive: IconButtonProps = { + color: "primary", + sx: { border: "1px solid currentColor" } +} + +const IconButtonResponseInactive: IconButtonProps = { + color: "inherit", + sx: { border: "1px solid transparent" } +} + +type IDetectedRoomProps = { + detectedRoom: IDetectedRoom + showSecondaryActions?: boolean +} + +enum ButtonResponse { + ACCEPT, + REJECT +} + +const handleResponse = ([response, detectedRoom]: [ + ButtonResponse, + IDetectedRoom +]) => { + switch (response) { + case ButtonResponse.ACCEPT: + return acceptDetectedRoom(detectedRoom) + case ButtonResponse.REJECT: + return rejectDetectedRoom(detectedRoom) + } +} + +const handleRemoveDetectedRoom = makeStorageMutator(removeDetectedRoom) + +const DetectedRoomListItem: FC = ({ + detectedRoom, + showSecondaryActions = true +}) => { + const { run, isLoading } = useAsync({ deferFn: handleResponse }) + + const handleAcceptRequest = useCallback( + () => run(ButtonResponse.ACCEPT, detectedRoom), + [run, detectedRoom] + ) + + const handleRejectRequest = useCallback( + () => run(ButtonResponse.REJECT, detectedRoom), + [run, detectedRoom] + ) + + const canSubmit = !isLoading + + return ( + + + + {detectedRoom.name} + + + {showSecondaryActions && ( + <> + + handleRemoveDetectedRoom(detectedRoom.url)} + > + + + + + + + + + + + + + )} + + ) +} + +export default DetectedRoomListItem diff --git a/packages/webextension/src/BrowserAction/Components/FeedbackLoading/FeedbackLoading.tsx b/packages/webextension/src/BrowserAction/Components/FeedbackLoading/FeedbackLoading.tsx new file mode 100644 index 0000000..a62e7b5 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/FeedbackLoading/FeedbackLoading.tsx @@ -0,0 +1,8 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" + +const FeedbackLoading = () => ( + {getMessage("feedback_loading")} +) + +export default FeedbackLoading diff --git a/packages/webextension/src/BrowserAction/Components/Loading/Loading.tsx b/packages/webextension/src/BrowserAction/Components/Loading/Loading.tsx new file mode 100644 index 0000000..00dd0fa --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/Loading/Loading.tsx @@ -0,0 +1,19 @@ +import Box from "@mui/material/Box" +import CircularProgress from "@mui/material/CircularProgress" + +const Loading = () => ( + + + +) + +export default Loading diff --git a/packages/webextension/src/BrowserAction/Components/MenuButton/MenuButton.tsx b/packages/webextension/src/BrowserAction/Components/MenuButton/MenuButton.tsx new file mode 100644 index 0000000..6b5d4f6 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/MenuButton/MenuButton.tsx @@ -0,0 +1,34 @@ +import IconButton from "@mui/material/IconButton" +import Menu from "@mui/material/Menu" +import { FC, ReactNode, useCallback, useState } from "react" + +type IMenuButtonProps = { + icon?: ReactNode + menuContent?: ReactNode +} + +const MenuButton: FC = ({ icon, menuContent }) => { + const [anchorEl, setAnchorEl] = useState(null) + + const handleClose = useCallback(() => setAnchorEl(null), []) + + const handleOpenClick = useCallback( + (e: any) => setAnchorEl(e.currentTarget), + [] + ) + + const isOpen = anchorEl !== null + + return ( + <> + + {icon} + + + {menuContent} + + + ) +} + +export default MenuButton diff --git a/packages/webextension/src/BrowserAction/Components/Message/Message.module.css b/packages/webextension/src/BrowserAction/Components/Message/Message.module.css new file mode 100644 index 0000000..f73d772 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/Message/Message.module.css @@ -0,0 +1,24 @@ +.message { + max-width: 100%; + word-wrap: anywhere; + padding: 15px 12px 10px; + border: 1px solid rgba(0, 0, 0, 0.3); +} + +.message p { + line-height: 1.4; +} + +.message.byContact { + justify-self: start; + grid-column: 1 / -2; + border-radius: 8px 8px 8px 0; + background-color: rgba(0, 0, 0, 0.1); +} + +.message.byMe { + justify-self: end; + grid-column: 2 / -1; + border-radius: 8px 8px 0 8px; + background-color: rgba(0, 0, 0, 0.05); +} diff --git a/packages/webextension/src/BrowserAction/Components/Message/Message.tsx b/packages/webextension/src/BrowserAction/Components/Message/Message.tsx new file mode 100644 index 0000000..ef8c948 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/Message/Message.tsx @@ -0,0 +1,60 @@ +import { + IMessageAreaMessage, + MessagePosition +} from "@ava-pro/crawlers/lib/Typings/IMessageAreaMessage" +import Box from "@mui/material/Box" +import Typography from "@mui/material/Typography" +import { sanitize } from "dompurify" +import { FC, useMemo } from "react" +import MessageBlockTime from "../MessageBlockTime/MessageBlockTime" +import * as styles from "./Message.module.css" + +type IMessageProps = { + message: IMessageAreaMessage +} + +const getClassForMessagePosition = (messagePosition: MessagePosition) => { + switch (messagePosition) { + case MessagePosition.LEFT: + return styles.byContact + case MessagePosition.RIGHT: + return styles.byMe + } +} + +const Message: FC = ({ message }) => { + const sanitizedHTML = useMemo( + () => sanitize(message.text, { USE_PROFILES: { html: true } }), + [message.text] + ) + + return ( + <> + {message.displayblocktime && ( + + )} + + +
+ + + {message.timesent} + + + + ) +} + +export default Message diff --git a/packages/webextension/src/BrowserAction/Components/MessageBlockTime/MessageBlockTime.module.css b/packages/webextension/src/BrowserAction/Components/MessageBlockTime/MessageBlockTime.module.css new file mode 100644 index 0000000..47c2bb6 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/MessageBlockTime/MessageBlockTime.module.css @@ -0,0 +1,3 @@ +.blockTime { + grid-column: 1 / -1; +} diff --git a/packages/webextension/src/BrowserAction/Components/MessageBlockTime/MessageBlockTime.tsx b/packages/webextension/src/BrowserAction/Components/MessageBlockTime/MessageBlockTime.tsx new file mode 100644 index 0000000..322fbbc --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/MessageBlockTime/MessageBlockTime.tsx @@ -0,0 +1,16 @@ +import Box from "@mui/material/Box" +import Typography from "@mui/material/Typography" +import { FC } from "react" +import * as classes from "./MessageBlockTime.module.css" + +type IMessageBlockTimeProps = { + blockTime: string +} + +const MessageBlockTime: FC = ({ blockTime }) => ( + + {blockTime} + +) + +export default MessageBlockTime diff --git a/packages/webextension/src/BrowserAction/Components/Messages/Messages.module.css b/packages/webextension/src/BrowserAction/Components/Messages/Messages.module.css new file mode 100644 index 0000000..dfb9f08 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/Messages/Messages.module.css @@ -0,0 +1,4 @@ +.messages { + display: grid; + grid-template-columns: 2fr repeat(6, 1fr) 2fr; +} diff --git a/packages/webextension/src/BrowserAction/Components/Messages/Messages.tsx b/packages/webextension/src/BrowserAction/Components/Messages/Messages.tsx new file mode 100644 index 0000000..0bae0ec --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/Messages/Messages.tsx @@ -0,0 +1,24 @@ +import { IMessageAreaMessage } from "@ava-pro/crawlers/lib/Typings/IMessageAreaMessage" +import Box from "@mui/material/Box" +import { FC } from "react" +import Message from "../Message/Message" +import * as classes from "./Messages.module.css" + +type IMessagesProps = { + messages: IMessageAreaMessage[] +} + +const Messages: FC = ({ messages }) => { + return ( + + {messages.map((message) => ( + + ))} + + ) +} + +export default Messages diff --git a/packages/webextension/src/BrowserAction/Components/Page/Page.tsx b/packages/webextension/src/BrowserAction/Components/Page/Page.tsx new file mode 100644 index 0000000..6c21a5d --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/Page/Page.tsx @@ -0,0 +1,6 @@ +import Box from "@mui/material/Box" +import { FC } from "react" + +const Page: FC = ({ children }) => {children} + +export default Page diff --git a/packages/webextension/src/BrowserAction/Components/PageContent/PageContent.tsx b/packages/webextension/src/BrowserAction/Components/PageContent/PageContent.tsx new file mode 100644 index 0000000..f4dafd4 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/PageContent/PageContent.tsx @@ -0,0 +1,14 @@ +import Box from "@mui/material/Box" +import { FC } from "react" +import PageSpacing from "../PageSpacing/PageSpacing" + +const PageContent: FC<{ spacing?: boolean }> = ({ + children, + spacing = true +}) => ( + + {spacing ? {children} : children} + +) + +export default PageContent diff --git a/packages/webextension/src/BrowserAction/Components/PageHeader/PageHeader.module.css b/packages/webextension/src/BrowserAction/Components/PageHeader/PageHeader.module.css new file mode 100644 index 0000000..714eeb2 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/PageHeader/PageHeader.module.css @@ -0,0 +1,16 @@ +.gutters { + padding: 4px 12px 4px 3px !important; + gap: 4px; +} + +.gutters > :first-child:not(h6) { + margin-left: 4px; +} + +.gutters > :first-child:not(:not(h6)) { + margin-left: 16px; +} + +.title { + flex: 1; +} diff --git a/packages/webextension/src/BrowserAction/Components/PageHeader/PageHeader.tsx b/packages/webextension/src/BrowserAction/Components/PageHeader/PageHeader.tsx new file mode 100644 index 0000000..604ce32 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/PageHeader/PageHeader.tsx @@ -0,0 +1,42 @@ +import AppBar from "@mui/material/AppBar" +import Toolbar from "@mui/material/Toolbar" +import Typography from "@mui/material/Typography" +import { FC, ReactNode } from "react" +import * as classes from "./PageHeader.module.css" + +type IPageHeaderProps = { + beforeTitle?: ReactNode + title?: string + afterTitle?: ReactNode + mainContent?: ReactNode +} + +const PageHeader: FC = ({ + title, + mainContent, + beforeTitle, + afterTitle +}) => ( + + + {beforeTitle} + + {title && ( + + {title} + + )} + + {mainContent} + + {afterTitle} + + +) + +export default PageHeader diff --git a/packages/webextension/src/BrowserAction/Components/PageSpacing/PageSpacing.tsx b/packages/webextension/src/BrowserAction/Components/PageSpacing/PageSpacing.tsx new file mode 100644 index 0000000..4b06947 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/PageSpacing/PageSpacing.tsx @@ -0,0 +1,8 @@ +import Box from "@mui/material/Box" +import { FC } from "react" + +const PageSpacing: FC = ({ children }) => ( + {children} +) + +export default PageSpacing diff --git a/packages/webextension/src/BrowserAction/Components/RoomAuthedContext.tsx b/packages/webextension/src/BrowserAction/Components/RoomAuthedContext.tsx new file mode 100644 index 0000000..2e23bf3 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomAuthedContext.tsx @@ -0,0 +1,92 @@ +import { CheckLogin } from "@ava-pro/crawlers/lib/Scrappers/CheckLogin/CheckLogin" +import { ExtractTokens } from "@ava-pro/crawlers/lib/Scrappers/ExtractTokens/ExtractTokens" +import { Login } from "@ava-pro/crawlers/lib/Scrappers/Login/Login" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { updateRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import { FC, useCallback, useEffect, useState } from "react" +import { createContext, useContextSelector } from "use-context-selector" +import { RoomContext } from "./RoomContext" + +type IRoomAuthedContext = { + isLoggedIn: boolean + userId: number | null + sessKey: string | null +} + +export const RoomAuthedContext = createContext({} as IRoomAuthedContext) + +const handleUpdateRoom = makeStorageMutator(updateRoom) + +const useRoomSmartLogin = () => { + const handleRoomUpdateAutoLogin = useCallback( + (id: IRoom["id"], autoLogin: boolean) => + handleUpdateRoom({ + id, + recipe: (room) => { + room.credentials.autoLogin = autoLogin + } + }), + [] + ) + + const [isLoggedIn, setIsLoggedIn] = useState(false) + + const id = useContextSelector(RoomContext, ({ room: { id } }) => id) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const { username, password, autoLogin } = useContextSelector( + RoomContext, + ({ room: { credentials } }) => credentials + ) + + const handleAutoLogin = useCallback(async () => { + let isLoggedIn = await CheckLogin(crawlerFetch)() + + if (!isLoggedIn && autoLogin && username && password) { + isLoggedIn = await Login(crawlerFetch)(username, password, true) + + if (!isLoggedIn) { + await handleRoomUpdateAutoLogin(id, false) + } + } + + setIsLoggedIn(isLoggedIn) + }, [username, password, autoLogin, crawlerFetch, handleRoomUpdateAutoLogin]) + + useEffect(() => void handleAutoLogin(), [handleAutoLogin]) + + return { isLoggedIn } +} + +export const RoomAuthedContextProvider: FC = ({ children }) => { + const { isLoggedIn } = useRoomSmartLogin() + + const [userId, setUserId] = useState(null) + const [sessKey, setSessKey] = useState(null) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const handleFetchTokens = useCallback(async () => { + const { userId, sessKey } = await ExtractTokens(crawlerFetch)() + setUserId(userId) + setSessKey(sessKey) + }, [crawlerFetch, isLoggedIn]) + + useEffect(() => { + void handleFetchTokens() + }, [handleFetchTokens]) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Components/RoomCachedCoursesContext.tsx b/packages/webextension/src/BrowserAction/Components/RoomCachedCoursesContext.tsx new file mode 100644 index 0000000..9a246e9 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomCachedCoursesContext.tsx @@ -0,0 +1,25 @@ +import { FC } from "react" +import { createContext } from "use-context-selector" +import { useRoomCachedCourses } from "../Hooks/useRoomCachedCourses" +import { RoomCoursesContextProvider } from "./RoomCoursesContext" + +type IRoomCachedCoursesContext = {} & ReturnType + +export const RoomCachedCoursesContext = createContext( + {} as IRoomCachedCoursesContext +) + +export const RoomCachedCoursesContextProvider: FC = ({ children }) => { + const { coursesCache, hasCache, isLoadingCache, loadCacheError, reload } = + useRoomCachedCourses() + + return ( + + + {children} + + + ) +} diff --git a/packages/webextension/src/BrowserAction/Components/RoomChatContext.tsx b/packages/webextension/src/BrowserAction/Components/RoomChatContext.tsx new file mode 100644 index 0000000..48b517e --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomChatContext.tsx @@ -0,0 +1,66 @@ +import { Messages } from "@ava-pro/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Messages" +import { MarkAllMessagesAsRead } from "@ava-pro/crawlers/lib/Scrappers/AjaxServices/CoreMessage/MarkAllMessagesAsRead" +import { IMessageArea } from "@ava-pro/crawlers/lib/Typings/IMessageArea" +import { FC } from "react" +import { useQuery, UseQueryResult } from "react-query" +import { createContext, useContextSelector } from "use-context-selector" +import { RoomAuthedContext } from "./RoomAuthedContext" +import { RoomContext } from "./RoomContext" + +type IRoomChatContext = { + contactId: number + chatQuery: UseQueryResult +} + +export const RoomChatContext = createContext({} as IRoomChatContext) + +type IRoomChatContextProviderProps = { + contactId: number +} + +const useRoomChat = (contactId: number) => { + const userId = useContextSelector(RoomAuthedContext, ({ userId }) => userId) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const chatQuery = useQuery( + [userId, "chat", contactId, crawlerFetch], + async () => { + if (userId !== null) { + const response = await Messages(crawlerFetch)({ + currentuserid: userId, + otheruserid: contactId + }) + + if (response.messages.length > 0) { + await MarkAllMessagesAsRead(crawlerFetch)({ + useridfrom: contactId, + useridto: userId + }) + } + + return response + } + + return null + } + ) + + return chatQuery +} + +export const RoomChatContextProvider: FC = ({ + children, + contactId +}) => { + const chatQuery = useRoomChat(+contactId) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Components/RoomChatsContext.tsx b/packages/webextension/src/BrowserAction/Components/RoomChatsContext.tsx new file mode 100644 index 0000000..f7a1d82 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomChatsContext.tsx @@ -0,0 +1,20 @@ +import { IMessageAreaContact } from "@ava-pro/crawlers/lib/Typings/IMessageAreaContact" +import { FC } from "react" +import { UseQueryResult } from "react-query" +import { createContext } from "use-context-selector" +import { useRoomConversationsQuery } from "../Hooks/useRoomConversationsQuery" + +type IRoomChatsContext = { + conversationsQuery: UseQueryResult +} + +export const RoomChatsContext = createContext({} as IRoomChatsContext) + +export const RoomChatsContextProvider: FC = ({ children }) => { + const { conversationsQuery } = useRoomConversationsQuery() + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Components/RoomContext.tsx b/packages/webextension/src/BrowserAction/Components/RoomContext.tsx new file mode 100644 index 0000000..952602c --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomContext.tsx @@ -0,0 +1,24 @@ +import { ICrawlerFetch } from "@ava-pro/shared/lib/Interfaces/ICrawlerFetch" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { makeCrawlerFetch } from "@ava-pro/shared/lib/makeCrawlerFetch" +import { FC, useMemo } from "react" +import { createContext } from "use-context-selector" + +type IRoomContext = { + room: IRoom + crawlerFetch: ICrawlerFetch +} + +export const RoomContext = createContext({} as IRoomContext) + +export const RoomContextProvider: FC<{ room: IRoom }> = ({ + room, + children +}) => { + const crawlerFetch = useMemo(() => makeCrawlerFetch(room.url), [room.url]) + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Components/RoomCoursesContext.tsx b/packages/webextension/src/BrowserAction/Components/RoomCoursesContext.tsx new file mode 100644 index 0000000..563bbb5 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomCoursesContext.tsx @@ -0,0 +1,35 @@ +import { IExtractedCourse } from "@ava-pro/shared/lib/Interfaces/IExtractedCourse" +import { IPageExtractedCourses } from "@ava-pro/shared/lib/Interfaces/IPageExtractedCourses" +import { useCourseCompareFn } from "@ava-pro/shared/lib/Storage/Hooks/useCourseCompareFn" +import { useFilterPageCoursesFn } from "@ava-pro/shared/lib/Storage/Hooks/useFilterPageCoursesFn" +import { FC, useMemo } from "react" +import { createContext } from "use-context-selector" + +type IRoomCoursesContext = { + courses: IExtractedCourse[] +} + +export const RoomCoursesContext = createContext({} as IRoomCoursesContext) + +export const RoomCoursesContextProvider: FC<{ + pageCourses?: IPageExtractedCourses | null +}> = ({ children, pageCourses }) => { + const compareFn = useCourseCompareFn() + const filterFn = useFilterPageCoursesFn() + + const courses = useMemo( + () => filterFn(pageCourses ?? null), + [pageCourses, filterFn] + ) + + const sortedCourses = useMemo( + () => Array.from(courses).sort(compareFn), + [compareFn, courses] + ) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Components/RoomForm/IRoomFormData.ts b/packages/webextension/src/BrowserAction/Components/RoomForm/IRoomFormData.ts new file mode 100644 index 0000000..629987d --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomForm/IRoomFormData.ts @@ -0,0 +1,3 @@ +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" + +export type IRoomFormData = Pick diff --git a/packages/webextension/src/BrowserAction/Components/RoomForm/RoomForm.tsx b/packages/webextension/src/BrowserAction/Components/RoomForm/RoomForm.tsx new file mode 100644 index 0000000..552d2b6 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomForm/RoomForm.tsx @@ -0,0 +1,12 @@ +import Box from "@mui/material/Box" +import RoomFormInputName from "./RoomFormInputName" +import RoomFormInputURL from "./RoomFormInputURL" + +const RoomForm = () => ( + + + + +) + +export default RoomForm diff --git a/packages/webextension/src/BrowserAction/Components/RoomForm/RoomFormInputName.tsx b/packages/webextension/src/BrowserAction/Components/RoomForm/RoomFormInputName.tsx new file mode 100644 index 0000000..65119db --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomForm/RoomFormInputName.tsx @@ -0,0 +1,36 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import TextField from "@mui/material/TextField" +import { useFormContext } from "react-hook-form" +import AppErrorMessage from "../../Components/AppErrorMessage/AppErrorMessage" +import { handleSelectAllOnFocus } from "../../Hooks/handleSelectAllOnFocus" + +const RoomFormInputName = () => { + const { + register, + formState: { errors } + } = useFormContext() + + return ( + <> + + + + ) +} + +export default RoomFormInputName diff --git a/packages/webextension/src/BrowserAction/Components/RoomForm/RoomFormInputURL.tsx b/packages/webextension/src/BrowserAction/Components/RoomForm/RoomFormInputURL.tsx new file mode 100644 index 0000000..c4293a6 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomForm/RoomFormInputURL.tsx @@ -0,0 +1,40 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { isUrl } from "@ava-pro/shared/lib/Utils/isUrl" +import TextField from "@mui/material/TextField" +import { useFormContext } from "react-hook-form" +import AppErrorMessage from "../../Components/AppErrorMessage/AppErrorMessage" +import { handleSelectAllOnFocus } from "../../Hooks/handleSelectAllOnFocus" + +const RoomFormInputURL = () => { + const { + register, + formState: { errors } + } = useFormContext() + + return ( + <> + + isUrl(value) || + getMessage("component_roomForm_input_url_feedback_invalid") + } + }) + }} + /> + + + + ) +} + +export default RoomFormInputURL diff --git a/packages/webextension/src/BrowserAction/Components/RoomGuardNeedsAuth/RoomGuardNeedsAuth.tsx b/packages/webextension/src/BrowserAction/Components/RoomGuardNeedsAuth/RoomGuardNeedsAuth.tsx new file mode 100644 index 0000000..76946dd --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomGuardNeedsAuth/RoomGuardNeedsAuth.tsx @@ -0,0 +1,33 @@ +import { FC, Fragment } from "react" +import { useContextSelector } from "use-context-selector" +import PageSpacing from "../PageSpacing/PageSpacing" +import { RoomAuthedContext } from "../RoomAuthedContext" +import RoomGuardNeedsAuthFeedback from "./RoomGuardNeedsAuthFeedback" + +type IRoomGuardNeedsAuthProps = { + wrapFeedback?: boolean +} + +const RoomGuardNeedsAuth: FC = ({ + children, + wrapFeedback = false +}) => { + const isLoggedIn = useContextSelector( + RoomAuthedContext, + ({ isLoggedIn }) => isLoggedIn + ) + + const Wrapper = wrapFeedback ? PageSpacing : Fragment + + if (!isLoggedIn) { + return ( + + + + ) + } + + return <>{children} +} + +export default RoomGuardNeedsAuth diff --git a/packages/webextension/src/BrowserAction/Components/RoomGuardNeedsAuth/RoomGuardNeedsAuthFeedback.tsx b/packages/webextension/src/BrowserAction/Components/RoomGuardNeedsAuth/RoomGuardNeedsAuthFeedback.tsx new file mode 100644 index 0000000..5098e3d --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomGuardNeedsAuth/RoomGuardNeedsAuthFeedback.tsx @@ -0,0 +1,43 @@ +import { routes } from "@ava-pro/crawlers/lib/routes" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import normalizeUrl from "normalize-url" +import { useMemo } from "react" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import { RoomContext } from "../RoomContext" + +const RoomGuardNeedsAuthFeedback = () => { + const id = useContextSelector(RoomContext, ({ room: { id } }) => id) + const url = useContextSelector(RoomContext, ({ room: { url } }) => url) + + const manualLoginUrl = useMemo( + () => + `${normalizeUrl(url, { + removeTrailingSlash: true, + removeSingleSlash: true + })}${routes.login()}`, + [url] + ) + + const automaticLoginUrl = useMemo(() => `/rooms/${id}/credentials`, [id]) + + return ( + + {getMessage("component_roomGuardNeedsAuth_feedback_1_intro")}{" "} + window.open(manualLoginUrl)} + style={{ cursor: "pointer", fontWeight: "bold" }} + > + {getMessage("component_roomGuardNeedsAuth_feedback_2_manualLogin")} + {" "} + {getMessage("component_roomGuardNeedsAuth_feedback_3_orSetupThe")}{" "} + + {getMessage("component_roomGuardNeedsAuth_feedback_4_automaticLogin")} + + . + + ) +} + +export default RoomGuardNeedsAuthFeedback diff --git a/packages/webextension/src/BrowserAction/Components/RoomList/RoomList.tsx b/packages/webextension/src/BrowserAction/Components/RoomList/RoomList.tsx new file mode 100644 index 0000000..aa5dd78 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomList/RoomList.tsx @@ -0,0 +1,41 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import Divider from "@mui/material/Divider" +import List from "@mui/material/List" +import ListSubheader from "@mui/material/ListSubheader" +import { FC } from "react" +import RoomListItem from "../RoomListItem/RoomListItem" + +type IRoomListProps = { + rooms: Omit[] + showSubHeader?: boolean + showBeforeFirstItemDivider?: boolean +} + +const RoomList: FC = ({ + rooms, + showSubHeader, + showBeforeFirstItemDivider = true +}) => { + if (rooms.length === 0) { + return null + } + + return ( + + {showSubHeader && ( + + {getMessage("component_roomList_subheader")} + + )} + + {showBeforeFirstItemDivider && } + + {rooms.map((room) => ( + + ))} + + ) +} + +export default RoomList diff --git a/packages/webextension/src/BrowserAction/Components/RoomListItem/RoomListItem.tsx b/packages/webextension/src/BrowserAction/Components/RoomListItem/RoomListItem.tsx new file mode 100644 index 0000000..ad80e16 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/RoomListItem/RoomListItem.tsx @@ -0,0 +1,41 @@ +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { applyStorageMutation } from "@ava-pro/shared/lib/Storage/applyStorageMutation" +import { removeRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import DeleteIcon from "@mui/icons-material/Delete" +import IconButton from "@mui/material/IconButton" +import ListItem from "@mui/material/ListItem" +import ListItemSecondaryAction from "@mui/material/ListItemSecondaryAction" +import ListItemText from "@mui/material/ListItemText" +import Typography from "@mui/material/Typography" +import { FC, useCallback } from "react" +import { useGoToRoom } from "../../Hooks/useGoToRoom" + +type IRoomListItemProps = { + room: Omit +} + +const RoomListItem: FC = ({ room: { name, id } }) => { + const handleGoToRoom = useGoToRoom() + + const handleRemoveClick = useCallback( + () => applyStorageMutation(removeRoom, id), + [id] + ) + + return ( + handleGoToRoom(id)}> + + + {name} + + + + + + + + + ) +} + +export default RoomListItem diff --git a/packages/webextension/src/BrowserAction/Components/SearchField/SearchField.tsx b/packages/webextension/src/BrowserAction/Components/SearchField/SearchField.tsx new file mode 100644 index 0000000..e3fd361 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Components/SearchField/SearchField.tsx @@ -0,0 +1,49 @@ +import CloseIcon from "@mui/icons-material/Close" +import IconButton from "@mui/material/IconButton" +import InputAdornment from "@mui/material/InputAdornment" +import InputBase from "@mui/material/InputBase" +import { InputBaseProps } from "@mui/material/InputBase/InputBase" +import { FC, useCallback, useRef } from "react" +import { handleSelectAllOnFocus } from "../../Hooks/handleSelectAllOnFocus" + +type ISearchFieldProps = { + value: string + setValue: (value: string) => void +} & InputBaseProps + +const SearchField: FC = ({ value, setValue, ...props }) => { + const inputRef = useRef() + + const clearValue = useCallback(() => { + setValue("") + inputRef.current?.focus() + }, [setValue, inputRef]) + + return ( + <> + setValue(e.target.value)} + inputProps={{ style: { color: "inherit" } }} + endAdornment={ + + {value.length > 0 && ( + + + + )} + + } + {...props} + /> + + ) +} + +export default SearchField diff --git a/packages/webextension/src/BrowserAction/Hooks/handleSelectAllOnFocus.tsx b/packages/webextension/src/BrowserAction/Hooks/handleSelectAllOnFocus.tsx new file mode 100644 index 0000000..da8334b --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/handleSelectAllOnFocus.tsx @@ -0,0 +1,5 @@ +import { FocusEvent } from "react" + +export const handleSelectAllOnFocus = ( + e: FocusEvent +) => e.target.select() diff --git a/packages/webextension/src/BrowserAction/Hooks/useCoursePinState.tsx b/packages/webextension/src/BrowserAction/Hooks/useCoursePinState.tsx new file mode 100644 index 0000000..866016f --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/useCoursePinState.tsx @@ -0,0 +1,26 @@ +import { IExtractedCourse } from "@ava-pro/shared/lib/Interfaces/IExtractedCourse" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { updateRoomCourse } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import { useCallback } from "react" +import { useContextSelector } from "use-context-selector" +import { RoomContext } from "../Components/RoomContext" + +const handleUpdateRoomCourse = makeStorageMutator(updateRoomCourse) + +export const useCoursePinState = ({ courseId, isPinned }: IExtractedCourse) => { + const roomId = useContextSelector(RoomContext, ({ room: { id } }) => id) + + const togglePinState = useCallback( + () => + handleUpdateRoomCourse({ + roomId, + courseId, + recipe: (course) => { + course.isPinned = !course.isPinned + } + }), + [roomId, courseId] + ) + + return { togglePinState, isPinned } +} diff --git a/packages/webextension/src/BrowserAction/Hooks/useFeedbackFetchCoursesQuery.tsx b/packages/webextension/src/BrowserAction/Hooks/useFeedbackFetchCoursesQuery.tsx new file mode 100644 index 0000000..ca1c884 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/useFeedbackFetchCoursesQuery.tsx @@ -0,0 +1,36 @@ +import { useSnackbar } from "notistack" +import { useEffect, useState } from "react" +import { useContextSelector } from "use-context-selector" +import { RoomCachedCoursesContext } from "../Components/RoomCachedCoursesContext" + +export const useFeedbackFetchCoursesQuery = () => { + const { enqueueSnackbar, closeSnackbar } = useSnackbar() + + const [key, setKey] = useState(null) + + const [feedbackTimeoutId, setFeedbackTimeoutId] = useState(null) + + const isLoadingCache = useContextSelector( + RoomCachedCoursesContext, + ({ isLoadingCache }) => isLoadingCache + ) + + useEffect(() => { + if (isLoadingCache && key === null && feedbackTimeoutId === null) { + const showFeedbackTimeout = setTimeout(() => { + const key = enqueueSnackbar("Atualizando a lista de cursos...", { + persist: true + }) + setKey(key) + }, 300) + setFeedbackTimeoutId(showFeedbackTimeout) + } + + if (!isLoadingCache && feedbackTimeoutId !== null) { + clearTimeout(feedbackTimeoutId) + setFeedbackTimeoutId(null) + key && closeSnackbar(key) + setKey(null) + } + }, [isLoadingCache, feedbackTimeoutId, key, enqueueSnackbar]) +} diff --git a/packages/webextension/src/BrowserAction/Hooks/useGoToRoom.tsx b/packages/webextension/src/BrowserAction/Hooks/useGoToRoom.tsx new file mode 100644 index 0000000..358f97d --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/useGoToRoom.tsx @@ -0,0 +1,7 @@ +import { useCallback } from "react" +import { useNavigate } from "react-router-dom" + +export const useGoToRoom = () => { + const navigate = useNavigate() + return useCallback((id: string) => void navigate(`/rooms/${id}`), [navigate]) +} diff --git a/packages/webextension/src/BrowserAction/Hooks/useRoomCachedCourses.tsx b/packages/webextension/src/BrowserAction/Hooks/useRoomCachedCourses.tsx new file mode 100644 index 0000000..5efb957 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/useRoomCachedCourses.tsx @@ -0,0 +1,67 @@ +import { ListCourses } from "@ava-pro/crawlers/lib/Scrappers/ListCourses/ListCourses" +import { IPageExtractedCourses } from "@ava-pro/shared/lib/Interfaces/IPageExtractedCourses" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { updateRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import { useCallback, useEffect } from "react" +import { useAsync } from "react-async" +import { useContextSelector } from "use-context-selector" +import { RoomContext } from "../Components/RoomContext" + +const handleUpdateRoom = makeStorageMutator(updateRoom) + +export const useRoomCachedCourses = () => { + const id = useContextSelector(RoomContext, ({ room: { id } }) => id) + + const handleUpdateRoomCoursesCache = useCallback( + (id: IRoom["id"], pageCourses: IPageExtractedCourses) => + handleUpdateRoom({ + id, + recipe: (room) => { + room.cache.courses = pageCourses + } + }), + [] + ) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const coursesCache = useContextSelector( + RoomContext, + ({ + room: { + cache: { courses } + } + }) => courses + ) + + const hasCache = coursesCache !== null + + const updateCoursesCache = useCallback(async () => { + const pageCourses = await ListCourses(crawlerFetch)() + await handleUpdateRoomCoursesCache(id, pageCourses) + }, [hasCache, handleUpdateRoomCoursesCache, crawlerFetch]) + + const { run, error, isLoading } = useAsync({ + deferFn: updateCoursesCache + }) + + const reload = useCallback(() => run(), [run]) + + useEffect(() => { + !hasCache && reload() + }, [hasCache, reload]) + + const loadCacheError = !hasCache && error + + return { + coursesCache, + hasCache, + isLoadingCache: isLoading, + loadCacheError, + reload + } +} diff --git a/packages/webextension/src/BrowserAction/Hooks/useRoomConversationsQuery.tsx b/packages/webextension/src/BrowserAction/Hooks/useRoomConversationsQuery.tsx new file mode 100644 index 0000000..558d5e9 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/useRoomConversationsQuery.tsx @@ -0,0 +1,29 @@ +import { Conversations } from "@ava-pro/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Conversations" +import { useQuery } from "react-query" +import { useContextSelector } from "use-context-selector" +import { RoomAuthedContext } from "../Components/RoomAuthedContext" +import { RoomContext } from "../Components/RoomContext" + +export const useRoomConversationsQuery = () => { + const userId = useContextSelector(RoomAuthedContext, ({ userId }) => userId) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const conversationsQuery = useQuery( + [userId, "conversations", crawlerFetch], + async () => { + if (userId !== null) { + const { contacts } = await Conversations(crawlerFetch)({ + userid: userId + }) + return contacts + } + return [] + } + ) + + return { conversationsQuery } +} diff --git a/packages/webextension/src/BrowserAction/Hooks/useRouteMatch.tsx b/packages/webextension/src/BrowserAction/Hooks/useRouteMatch.tsx new file mode 100644 index 0000000..de3627a --- /dev/null +++ b/packages/webextension/src/BrowserAction/Hooks/useRouteMatch.tsx @@ -0,0 +1,14 @@ +import { useMemo } from "react" +import { matchPath, useLocation } from "react-router-dom" + +export const useRouteMatch = (patterns: string[]) => { + const location = useLocation() + + return useMemo( + () => + patterns.find((pattern) => + Boolean(matchPath(pattern, location.pathname)) + ), + [location] + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoom.tsx b/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoom.tsx new file mode 100644 index 0000000..74f6c74 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoom.tsx @@ -0,0 +1,42 @@ +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { addRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import { useCallback } from "react" +import { FormProvider, useForm } from "react-hook-form" +import { useNavigate } from "react-router" +import { IRoomFormData } from "../../Components/RoomForm/IRoomFormData" +import AddRoomContent from "./AddRoomContent" +import AddRoomHeader from "./AddRoomHeader" + +const handleAddRoom = makeStorageMutator(addRoom) + +const AddRoom = () => { + const navigate = useNavigate() + + const methods = useForm({ mode: "onChange" }) + + const handleAddRoomFromFormData = useCallback( + async ({ name, url }: IRoomFormData) => { + const { id } = await handleAddRoom({ name, url }) + navigate(`/rooms/${id}`) + }, + [navigate] + ) + + const { handleSubmit } = methods + + return ( + <> + +
+ + + +
+ + ) +} + +export default AddRoom diff --git a/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoomContent.tsx b/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoomContent.tsx new file mode 100644 index 0000000..355e364 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoomContent.tsx @@ -0,0 +1,10 @@ +import PageContent from "../../Components/PageContent/PageContent" +import RoomForm from "../../Components/RoomForm/RoomForm" + +const AddRoomContent = () => ( + + + +) + +export default AddRoomContent diff --git a/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoomHeader.tsx b/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoomHeader.tsx new file mode 100644 index 0000000..f09729c --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/AddRoom/AddRoomHeader.tsx @@ -0,0 +1,45 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBack from "@mui/icons-material/ArrowBack" +import Button from "@mui/material/Button" +import IconButton from "@mui/material/IconButton" + +import { useFormContext } from "react-hook-form" +import { Link } from "react-router-dom" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const AddRoomHeader = () => { + const { + formState: { isValid, isDirty } + } = useFormContext() + + const canSubmit = !isDirty || !isValid + + return ( + + + + + + + + } + afterTitle={ + <> + + + } + /> + ) +} + +export default AddRoomHeader diff --git a/packages/webextension/src/BrowserAction/Pages/AppInitialRoute/AppInitialRoute.tsx b/packages/webextension/src/BrowserAction/Pages/AppInitialRoute/AppInitialRoute.tsx new file mode 100644 index 0000000..592e65a --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/AppInitialRoute/AppInitialRoute.tsx @@ -0,0 +1,31 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { getSelectedRoom } from "@ava-pro/shared/lib/Storage/Slicers/StorageSettingsSlicers" +import { Navigate } from "react-router-dom" +import Loading from "../../Components/Loading/Loading" +import Page from "../../Components/Page/Page" +import PageContent from "../../Components/PageContent/PageContent" + +const AppInitialRoute = () => { + const { value: selectedRoom, isLoading } = useExtensionStorageSlicer( + getSelectedRoom, + null + ) + + if (isLoading) { + return ( + + + + + + ) + } + + return selectedRoom !== null ? ( + + ) : ( + + ) +} + +export default AppInitialRoute diff --git a/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRooms.tsx b/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRooms.tsx new file mode 100644 index 0000000..ed1801e --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRooms.tsx @@ -0,0 +1,12 @@ +import Page from "../../Components/Page/Page" +import ListAllDetectedRoomsContent from "./ListAllDetectedRoomsContent" +import ListAllDetectedRoomsHeader from "./ListAllDetectedRoomsHeader" + +const ListAllDetectedRooms = () => ( + + + + +) + +export default ListAllDetectedRooms diff --git a/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRoomsContent.tsx b/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRoomsContent.tsx new file mode 100644 index 0000000..2351ed3 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRoomsContent.tsx @@ -0,0 +1,42 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { listDetectedRooms } from "@ava-pro/shared/lib/Storage/Slicers/StorageDetectedRoomSlicers" +import Alert from "@mui/material/Alert" +import DetectedRoomList from "../../Components/DetectedRoomList/DetectedRoomList" +import FeedbackLoading from "../../Components/FeedbackLoading/FeedbackLoading" +import PageContent from "../../Components/PageContent/PageContent" + +const ListAllDetectedRoomsContent = () => { + const { value: detectedRooms, isLoading } = useExtensionStorageSlicer( + listDetectedRooms, + [] + ) + + const showLoading = isLoading && detectedRooms.length === 0 + + if (showLoading) { + return ( + + + + ) + } + + if (detectedRooms.length === 0) { + return ( + + + {getMessage("page_listDetectedRooms_feedback_noneDetected")} + + + ) + } + + return ( + + + + ) +} + +export default ListAllDetectedRoomsContent diff --git a/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRoomsHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRoomsHeader.tsx new file mode 100644 index 0000000..647a0d8 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ListAllDetectedRooms/ListAllDetectedRoomsHeader.tsx @@ -0,0 +1,23 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBack from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ListAllDetectedRoomsHeader = () => { + return ( + + + + + + + + } + /> + ) +} +export default ListAllDetectedRoomsHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRooms.tsx b/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRooms.tsx new file mode 100644 index 0000000..61aafc2 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRooms.tsx @@ -0,0 +1,12 @@ +import Page from "../../Components/Page/Page" +import ListRoomsContent from "./ListRoomsContent" +import ListRoomsHeader from "./ListRoomsHeader" + +const ListRooms = () => ( + + + + +) + +export default ListRooms diff --git a/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRoomsContent.tsx b/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRoomsContent.tsx new file mode 100644 index 0000000..2a31c85 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRoomsContent.tsx @@ -0,0 +1,73 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { IDetectedRoomResponse } from "@ava-pro/shared/lib/Interfaces/IDetectedRoomResponse" +import { applyStorageMutation } from "@ava-pro/shared/lib/Storage/applyStorageMutation" +import { updateSelectedRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageSettingsMutations" +import { listDetectedRooms } from "@ava-pro/shared/lib/Storage/Slicers/StorageDetectedRoomSlicers" +import { listRoomsOmitCache } from "@ava-pro/shared/lib/Storage/Slicers/StorageRoomSlicers" +import Alert from "@mui/material/Alert" +import { useEffect, useMemo } from "react" +import DetectedRoomList from "../../Components/DetectedRoomList/DetectedRoomList" +import FeedbackLoading from "../../Components/FeedbackLoading/FeedbackLoading" +import PageContent from "../../Components/PageContent/PageContent" +import PageSpacing from "../../Components/PageSpacing/PageSpacing" +import RoomList from "../../Components/RoomList/RoomList" + +const ListRoomsContent = () => { + useEffect(() => { + void applyStorageMutation(updateSelectedRoom, null) + }, []) + + const { value: rooms, isLoading } = useExtensionStorageSlicer( + listRoomsOmitCache, + [] + ) + + const { value: detectedRooms } = useExtensionStorageSlicer( + listDetectedRooms, + [] + ) + + const detectedRoomsWithoutResponse = useMemo( + () => + detectedRooms + .filter(({ response }) => response === IDetectedRoomResponse.NONE) + .filter(({ url }) => rooms.every((i) => i.url !== url)), + [detectedRooms, rooms] + ) + + const showRoomTooltip = + !isLoading && + rooms.length === 0 && + detectedRoomsWithoutResponse.length === 0 + + const showLoading = + isLoading && rooms.length !== 0 && detectedRoomsWithoutResponse.length !== 0 + + return ( + + {(showLoading || showRoomTooltip) && ( + + {showLoading && } + + {showRoomTooltip && ( + + {getMessage("page_listRooms_feedback_noRoomsTooltip")} + + )} + + )} + + + + 0 && rooms.length > 0 + } + /> + + ) +} + +export default ListRoomsContent diff --git a/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRoomsHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRoomsHeader.tsx new file mode 100644 index 0000000..9c32173 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ListRooms/ListRoomsHeader.tsx @@ -0,0 +1,36 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import AddIcon from "@mui/icons-material/Add" +import HistoryIcon from "@mui/icons-material/History" +import SettingsIcon from "@mui/icons-material/Settings" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ListRoomsHeader = () => { + return ( + + + + + + + + + + + + + + + + + + } + /> + ) +} + +export default ListRoomsHeader diff --git a/packages/webextension/src/BrowserAction/Pages/PageErrorNotFound/PageErrorNotFound.tsx b/packages/webextension/src/BrowserAction/Pages/PageErrorNotFound/PageErrorNotFound.tsx new file mode 100644 index 0000000..5e8207f --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/PageErrorNotFound/PageErrorNotFound.tsx @@ -0,0 +1,27 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import Page from "../../Components/Page/Page" +import PageContent from "../../Components/PageContent/PageContent" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const PageErrorNotFound = () => ( + + + + + + + + + } + /> + {getMessage("page_error_notFound_message")} + +) + +export default PageErrorNotFound diff --git a/packages/webextension/src/BrowserAction/Pages/Settings/Settings.tsx b/packages/webextension/src/BrowserAction/Pages/Settings/Settings.tsx new file mode 100644 index 0000000..48f084b --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/Settings/Settings.tsx @@ -0,0 +1,32 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBack from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import Page from "../../Components/Page/Page" +import PageContent from "../../Components/PageContent/PageContent" +import PageHeader from "../../Components/PageHeader/PageHeader" +import SettingsAbout from "../SettingsAbout/SettingsAbout" +import SettingsContribute from "../SettingsContribute/SettingsContribute" + +const Settings = () => ( + + + + + + + + + } + /> + + + + + +) + +export default Settings diff --git a/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAbout.tsx b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAbout.tsx new file mode 100644 index 0000000..ad1f40a --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAbout.tsx @@ -0,0 +1,20 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Divider from "@mui/material/Divider" +import ListSubheader from "@mui/material/ListSubheader" +import SettingsAboutAuthor from "./SettingsAboutAuthor" +import SettingsAboutSourceCode from "./SettingsAboutSourceCode" +import SettingsAboutVersion from "./SettingsAboutVersion" + +const SettingsAbout = () => ( + <> + {getMessage("page_settings_about")} + + + + + + + +) + +export default SettingsAbout diff --git a/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutAuthor.tsx b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutAuthor.tsx new file mode 100644 index 0000000..546a532 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutAuthor.tsx @@ -0,0 +1,25 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ListItem from "@mui/material/ListItem" +import ListItemText from "@mui/material/ListItemText" +import { ABOUT } from "../../../ABOUT" + +const SettingsAboutAuthor = () => ( + window.open(ABOUT.AUTHOR_GITHUB)} + > + + {ABOUT.AUTHOR_NAME} +
+ {ABOUT.AUTHOR_EMAIL} + + } + /> +
+) + +export default SettingsAboutAuthor diff --git a/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutSourceCode.tsx b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutSourceCode.tsx new file mode 100644 index 0000000..6db3c6d --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutSourceCode.tsx @@ -0,0 +1,28 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ListItem from "@mui/material/ListItem" +import ListItemText from "@mui/material/ListItemText" +import { ABOUT } from "../../../ABOUT" + +const SettingsAboutSourceCode = () => ( + window.open(ABOUT.PROJECT_REPOSITORY)} + > + + + {getMessage("page_settings_about_source_code_license")}:{" "} + {ABOUT.PROJECT_LICENSE} + +
+ {ABOUT.PROJECT_REPOSITORY} + + } + /> +
+) + +export default SettingsAboutSourceCode diff --git a/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutVersion.tsx b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutVersion.tsx new file mode 100644 index 0000000..ba4ecc1 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/SettingsAbout/SettingsAboutVersion.tsx @@ -0,0 +1,15 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ListItem from "@mui/material/ListItem" +import ListItemText from "@mui/material/ListItemText" +import { ABOUT } from "../../../ABOUT" + +const SettingsAboutVersion = () => ( + + + +) + +export default SettingsAboutVersion diff --git a/packages/webextension/src/BrowserAction/Pages/SettingsContribute/SettingsContribute.tsx b/packages/webextension/src/BrowserAction/Pages/SettingsContribute/SettingsContribute.tsx new file mode 100644 index 0000000..8baf887 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/SettingsContribute/SettingsContribute.tsx @@ -0,0 +1,38 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ListItem from "@mui/material/ListItem" +import ListItemText from "@mui/material/ListItemText" +import ListSubheader from "@mui/material/ListSubheader" + +const CONTRIBUTE = { + AUTHOR_PIX: "42b01653-2b72-44d1-b485-e290a6088a66", + AUTHOR_PIX_QRCODE: + "https://github.com/guesant/assets/raw/project-improved-moodle/pix.jpg" +} + +const SettingsContribute = () => ( + <> + {getMessage("page_settings_contribute")} + + + + + + + + {`Chave + + +) + +export default SettingsContribute diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoom.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoom.tsx new file mode 100644 index 0000000..cd4fbdc --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoom.tsx @@ -0,0 +1,10 @@ +import ShowRoomContent from "./ShowRoomContent" +import { ShowRoomContextProvider } from "./ShowRoomContext" + +const ShowRoom = () => ( + + + +) + +export default ShowRoom diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContent.tsx new file mode 100644 index 0000000..ce01ad2 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContent.tsx @@ -0,0 +1,37 @@ +import { applyStorageMutation } from "@ava-pro/shared/lib/Storage/applyStorageMutation" +import { updateSelectedRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageSettingsMutations" +import { useEffect } from "react" +import { useContextSelector } from "use-context-selector" +import ShowRoomFallbackNotFound from "../ShowRoomFallbackNotFound/ShowRoomFallbackNotFound" +import ShowRoomContentOutlet from "./ShowRoomContentOutlet" +import { ShowRoomContext } from "./ShowRoomContext" +import ShowRoomFallbackLoading from "./ShowRoomFallbackLoading" + +const ShowRoomContent = () => { + const id = useContextSelector(ShowRoomContext, ({ room }) => room?.id ?? null) + + useEffect(() => { + void applyStorageMutation(updateSelectedRoom, id) + }, [id]) + + const isLoading = useContextSelector( + ShowRoomContext, + ({ isLoading }) => isLoading + ) + + if (!id) { + return isLoading ? ( + + ) : ( + + ) + } + + return ( + <> + + + ) +} + +export default ShowRoomContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContentOutlet.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContentOutlet.tsx new file mode 100644 index 0000000..5c5632f --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContentOutlet.tsx @@ -0,0 +1,19 @@ +import { Outlet } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import { RoomAuthedContextProvider } from "../../Components/RoomAuthedContext" +import { RoomContextProvider } from "../../Components/RoomContext" +import { ShowRoomContext } from "./ShowRoomContext" + +const ShowRoomContentOutlet = () => { + const room = useContextSelector(ShowRoomContext, ({ room }) => room!) + + return ( + + + + + + ) +} + +export default ShowRoomContentOutlet diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContext.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContext.tsx new file mode 100644 index 0000000..1dd5a20 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomContext.tsx @@ -0,0 +1,30 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { getRoomById } from "@ava-pro/shared/lib/Storage/Slicers/StorageRoomSlicers" +import { FC, useMemo } from "react" +import { useParams } from "react-router-dom" +import { createContext } from "use-context-selector" + +type IShowRoomContext = { + room: IRoom | null + isLoading: boolean +} + +export const ShowRoomContext = createContext({} as IShowRoomContext) + +export const ShowRoomContextProvider: FC = ({ children }) => { + const { id = "" } = useParams<"id">() + + const getRoomSlicer = useMemo(() => getRoomById(id), [id]) + + const { value: room, isLoading } = useExtensionStorageSlicer( + getRoomSlicer, + null + ) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomFallbackLoading.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomFallbackLoading.tsx new file mode 100644 index 0000000..8bf0b08 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoom/ShowRoomFallbackLoading.tsx @@ -0,0 +1,30 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import Loading from "../../Components/Loading/Loading" +import Page from "../../Components/Page/Page" +import PageContent from "../../Components/PageContent/PageContent" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ShowRoomFallbackLoading = () => ( + + + + + + + + + } + /> + + + + +) + +export default ShowRoomFallbackLoading diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChat.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChat.tsx new file mode 100644 index 0000000..497c78b --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChat.tsx @@ -0,0 +1,23 @@ +import { useParams } from "react-router-dom" +import Page from "../../Components/Page/Page" +import { RoomChatContextProvider } from "../../Components/RoomChatContext" +import ShowRoomGuardNeedsAuth from "../ShowRoomGuardNeedsAuth/ShowRoomGuardNeedsAuth" +import ShowRoomChatContent from "./ShowRoomChatContent" +import ShowRoomChatFallbacks from "./ShowRoomChatFallbacks" + +const ShowRoomChat = () => { + const { contactId = "" } = useParams<"contactId">() + return ( + + + + + + + + + + ) +} + +export default ShowRoomChat diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContent.tsx new file mode 100644 index 0000000..6b4fd23 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContent.tsx @@ -0,0 +1,23 @@ +import Box from "@mui/material/Box" +import Divider from "@mui/material/Divider" +import PageContent from "../../Components/PageContent/PageContent" +import ShowRoomChatContentMessages from "./ShowRoomChatContentMessages" +import ShowRoomChatContentTypeMessage from "./ShowRoomChatContentTypeMessage" +import ShowRoomChatHeader from "./ShowRoomChatHeader" + +const ShowRoomChatContent = () => ( + <> + + + + + + + + + + + +) + +export default ShowRoomChatContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContentMessages.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContentMessages.tsx new file mode 100644 index 0000000..06c73b9 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContentMessages.tsx @@ -0,0 +1,42 @@ +import Box from "@mui/material/Box" +import { useCallback, useEffect, useRef } from "react" +import { useContextSelector } from "use-context-selector" +import Messages from "../../Components/Messages/Messages" +import { RoomChatContext } from "../../Components/RoomChatContext" + +const ShowRoomChatContentMessages = () => { + const divRef = useRef(null) + + const messages = useContextSelector( + RoomChatContext, + ({ chatQuery: { data } }) => data?.messages || [] + ) + + const scrollToBottom = useCallback(() => { + const element = divRef.current + + if (element) { + element.scrollTo(element.scrollLeft, element.scrollHeight) + } + }, [divRef, messages.length]) + + useEffect(() => { + scrollToBottom() + }, [scrollToBottom]) + + if (messages.length === 0) { + return null + } + + return ( + scrollToBottom()} + sx={{ overflow: "auto", height: "100%", width: "100%" }} + > + + + ) +} + +export default ShowRoomChatContentMessages diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContentTypeMessage.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContentTypeMessage.tsx new file mode 100644 index 0000000..5fd4c4f --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatContentTypeMessage.tsx @@ -0,0 +1,98 @@ +import { SendInstantMessages } from "@ava-pro/crawlers/lib/Scrappers/AjaxServices/CoreMessage/SendInstantMessages" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import SendIcon from "@mui/icons-material/Send" +import Box from "@mui/material/Box" +import IconButton from "@mui/material/IconButton" +import TextField from "@mui/material/TextField" +import { useCallback } from "react" +import { useForm } from "react-hook-form" +import { useContextSelector } from "use-context-selector" +import { RoomChatContext } from "../../Components/RoomChatContext" +import { RoomContext } from "../../Components/RoomContext" + +type IFormData = { + text: string +} + +const ShowRoomChatContentTypeMessage = () => { + const refetch = useContextSelector( + RoomChatContext, + ({ chatQuery: { refetch } }) => refetch + ) + + const contactId = useContextSelector( + RoomChatContext, + ({ contactId }) => contactId + ) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const { + register, + reset, + handleSubmit, + formState: { isValid, isDirty, isSubmitting } + } = useForm({ mode: "onChange" }) + + const sendMessage = useCallback( + async (text: string) => { + if (text.trim().length > 0) { + const createdMessages = await SendInstantMessages(crawlerFetch)({ + text, + touserid: contactId + }) + + if (createdMessages.length > 0) { + reset() + await refetch() + } + } + }, + [crawlerFetch, contactId, refetch] + ) + + const onSubmitMessage = ({ text }: IFormData) => sendMessage(text) + + const canSubmit = isDirty && isValid && !isSubmitting + + return ( +
+ + + { + if (e.code === "Enter") { + if (!e.shiftKey) { + e.preventDefault() + handleSubmit(onSubmitMessage)(e) + } + } + }} + /> + + + + + + + +
+ ) +} + +export default ShowRoomChatContentTypeMessage diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatFallbackLoading.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatFallbackLoading.tsx new file mode 100644 index 0000000..8246230 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatFallbackLoading.tsx @@ -0,0 +1,26 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ShowRoomChatFallbackLoading = () => { + return ( + <> + + + + + + + + } + title={getMessage("feedback_loading")} + /> + + ) +} + +export default ShowRoomChatFallbackLoading diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatFallbacks.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatFallbacks.tsx new file mode 100644 index 0000000..75367f7 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatFallbacks.tsx @@ -0,0 +1,29 @@ +import { FC } from "react" +import { useContextSelector } from "use-context-selector" +import { RoomChatContext } from "../../Components/RoomChatContext" +import ShowRoomChatFallbackLoading from "./ShowRoomChatFallbackLoading" +import ShowRoomChatsFallbackNotFound from "./ShowRoomChatsFallbackNotFound" + +const ShowRoomChatFallbacks: FC = ({ children }) => { + const error = useContextSelector( + RoomChatContext, + ({ chatQuery: { error } }) => error + ) + + const isLoading = useContextSelector( + RoomChatContext, + ({ chatQuery: { isLoading, data } }) => isLoading || !data + ) + + if (isLoading) { + return + } + + if (error) { + return + } + + return <>{children} +} + +export default ShowRoomChatFallbacks diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatHeader.tsx new file mode 100644 index 0000000..0e534d4 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatHeader.tsx @@ -0,0 +1,55 @@ +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import RefreshIcon from "@mui/icons-material/Refresh" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import PageHeader from "../../Components/PageHeader/PageHeader" +import { RoomChatContext } from "../../Components/RoomChatContext" + +const ShowRoomChatHeader = () => { + const refetch = useContextSelector( + RoomChatContext, + ({ chatQuery: { refetch } }) => refetch + ) + const isLoading = useContextSelector( + RoomChatContext, + ({ chatQuery: { isLoading } }) => isLoading + ) + + const contactName = useContextSelector( + RoomChatContext, + ({ chatQuery: { data } }) => data?.otheruserfullname + ) + + return ( + + + + + + + + } + afterTitle={ + <> + { + if (!isLoading) { + void refetch() + } + }} + disabled={isLoading} + color={"inherit"} + > + + + + } + /> + ) +} + +export default ShowRoomChatHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatsFallbackNotFound.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatsFallbackNotFound.tsx new file mode 100644 index 0000000..b7b92ea --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomChat/ShowRoomChatsFallbackNotFound.tsx @@ -0,0 +1,32 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import Alert from "@mui/material/Alert" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import PageContent from "../../Components/PageContent/PageContent" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ShowRoomChatsFallbackNotFound = () => ( + <> + + + + + + + + } + title={getMessage("page_showRoom_chats_fallback_notFound")} + /> + + + + {getMessage("page_showRoom_chats_fallback_notFound_message")} + + + +) + +export default ShowRoomChatsFallbackNotFound diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentials.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentials.tsx new file mode 100644 index 0000000..435e127 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentials.tsx @@ -0,0 +1,64 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { updateRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import { getRoomById } from "@ava-pro/shared/lib/Storage/Slicers/StorageRoomSlicers" +import { useCallback, useEffect, useMemo } from "react" +import { FormProvider, useForm } from "react-hook-form" +import { useContextSelector } from "use-context-selector" +import Page from "../../Components/Page/Page" +import { ShowRoomContext } from "../ShowRoom/ShowRoomContext" +import ShowRoomCredentialsContent from "./ShowRoomCredentialsContent" +import ShowRoomCredentialsHeader from "./ShowRoomCredentialsHeader" + +type IFormCredentials = IRoom["credentials"] + +const handleUpdateRoom = makeStorageMutator(updateRoom) + +const useUpdateRoomCredentials = () => { + const id = useContextSelector(ShowRoomContext, ({ room }) => room!.id) + return useCallback( + async (credentials: IFormCredentials) => + handleUpdateRoom({ + id, + recipe: (draft) => { + draft.credentials = credentials + } + }).then(() => credentials), + [id] + ) +} + +const useRoomCredentials = () => { + const id = useContextSelector(ShowRoomContext, ({ room }) => room!.id) + + const slicerGetRoomById = useMemo(() => getRoomById(id), [id]) + const { value: room } = useExtensionStorageSlicer(slicerGetRoomById, null) + + return useMemo(() => room?.credentials ?? undefined, [room?.credentials]) +} + +const ShowRoomCredentials = () => { + const credentials = useRoomCredentials() + const updateRoomCredentials = useUpdateRoomCredentials() + const methods = useForm({ mode: "onChange", defaultValues: credentials }) + + const { handleSubmit, reset } = methods + + useEffect(() => { + credentials && reset(credentials) + }, [credentials]) + + return ( + +
+ + + + +
+
+ ) +} + +export default ShowRoomCredentials diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContent.tsx new file mode 100644 index 0000000..feb2197 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContent.tsx @@ -0,0 +1,17 @@ +import Box from "@mui/material/Box" +import PageContent from "../../Components/PageContent/PageContent" +import ShowRoomCredentialsContentInputAutoLogin from "./ShowRoomCredentialsContentInputAutoLogin" +import ShowRoomCredentialsContentInputPassword from "./ShowRoomCredentialsContentInputPassword" +import ShowRoomCredentialsContentInputUsername from "./ShowRoomCredentialsContentInputUsername" + +const ShowRoomCredentialsContent = () => ( + + + + + + + +) + +export default ShowRoomCredentialsContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputAutoLogin.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputAutoLogin.tsx new file mode 100644 index 0000000..40de616 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputAutoLogin.tsx @@ -0,0 +1,54 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import Checkbox from "@mui/material/Checkbox" +import FormControlLabel from "@mui/material/FormControlLabel" +import FormGroup from "@mui/material/FormGroup" +import { useFormContext } from "react-hook-form" + +const ShowRoomCredentialsContentInputAutoLogin = () => { + const { watch, setValue } = useFormContext() + const { username = "", password = "", autoLogin = false } = watch() + + const canBeToggled = username.length > 0 && password.length > 0 + + return ( + <> + + + setValue("autoLogin", !autoLogin, { + shouldDirty: true, + shouldTouch: true + }) + } + /> + } + /> + + + {!canBeToggled && ( + + {getMessage( + "page_showRoom_credentials_form_input_autoLogin_feedback_fillAll" + )} + + )} + + {canBeToggled && autoLogin && ( + + {getMessage( + "page_showRoom_credentials_form_input_autoLogin_feedback_autoDisable" + )} + + )} + + ) +} + +export default ShowRoomCredentialsContentInputAutoLogin diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputPassword.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputPassword.tsx new file mode 100644 index 0000000..32f3464 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputPassword.tsx @@ -0,0 +1,22 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import TextField from "@mui/material/TextField" +import { useFormContext } from "react-hook-form" +import { handleSelectAllOnFocus } from "../../Hooks/handleSelectAllOnFocus" + +const ShowRoomCredentialsContentInputPassword = () => { + const { register } = useFormContext() + return ( + + ) +} + +export default ShowRoomCredentialsContentInputPassword diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputUsername.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputUsername.tsx new file mode 100644 index 0000000..de12297 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsContentInputUsername.tsx @@ -0,0 +1,22 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import TextField from "@mui/material/TextField" +import { useFormContext } from "react-hook-form" +import { handleSelectAllOnFocus } from "../../Hooks/handleSelectAllOnFocus" + +const ShowRoomCredentialsContentInputUsername = () => { + const { register } = useFormContext() + + return ( + + ) +} + +export default ShowRoomCredentialsContentInputUsername diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsHeader.tsx new file mode 100644 index 0000000..f3db29b --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomCredentials/ShowRoomCredentialsHeader.tsx @@ -0,0 +1,44 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import Button from "@mui/material/Button" +import IconButton from "@mui/material/IconButton" +import { useFormContext } from "react-hook-form" +import { Link } from "react-router-dom" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ShowRoomCredentialsHeader = () => { + const { + formState: { isDirty, isSubmitting } + } = useFormContext() + + const canSubmit = isDirty && !isSubmitting + + return ( + + + + + + + + } + afterTitle={ + <> + + + } + /> + ) +} + +export default ShowRoomCredentialsHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEdit.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEdit.tsx new file mode 100644 index 0000000..44d2fe5 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEdit.tsx @@ -0,0 +1,62 @@ +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { updateRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import { useCallback, useEffect, useMemo } from "react" +import { FormProvider, useForm } from "react-hook-form" +import { useContextSelector } from "use-context-selector" +import Page from "../../Components/Page/Page" +import { RoomContext } from "../../Components/RoomContext" +import { IRoomFormData } from "../../Components/RoomForm/IRoomFormData" +import ShowRoomEditContent from "./ShowRoomEditContent" +import ShowRoomEditHeader from "./ShowRoomEditHeader" + +const useRoomSlimData = () => { + const url = useContextSelector(RoomContext, ({ room: { url } }) => url) + const name = useContextSelector(RoomContext, ({ room: { name } }) => name) + return useMemo(() => ({ url, name }), [url, name]) +} + +const handleUpdateRoom = makeStorageMutator(updateRoom) + +const ShowRoomEdit = () => { + const slimRoomData = useRoomSlimData() + const id = useContextSelector(RoomContext, ({ room: { id } }) => id) + + const methods = useForm({ mode: "onChange", defaultValues: slimRoomData }) + + const { + handleSubmit, + reset, + formState: { isValid, isDirty } + } = methods + + const canSubmit = isDirty && isValid + + const onSubmit = useCallback( + ({ url, name }: IRoomFormData) => + handleUpdateRoom({ + id, + recipe: (room) => { + room.url = url + room.name = name + } + }), + [id, canSubmit] + ) + + useEffect(() => { + reset(slimRoomData) + }, [slimRoomData]) + + return ( + +
+ + + + +
+
+ ) +} + +export default ShowRoomEdit diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEditContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEditContent.tsx new file mode 100644 index 0000000..2775aca --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEditContent.tsx @@ -0,0 +1,10 @@ +import PageContent from "../../Components/PageContent/PageContent" +import RoomForm from "../../Components/RoomForm/RoomForm" + +const ShowRoomEditContent = () => ( + + + +) + +export default ShowRoomEditContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEditHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEditHeader.tsx new file mode 100644 index 0000000..e9a64da --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomEdit/ShowRoomEditHeader.tsx @@ -0,0 +1,44 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import Button from "@mui/material/Button" +import IconButton from "@mui/material/IconButton" +import { useFormContext } from "react-hook-form" +import { Link } from "react-router-dom" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ShowRoomEditHeader = () => { + const { + formState: { isValid, isDirty } + } = useFormContext() + + const canSubmit = isDirty && isValid + + return ( + + + + + + + + } + afterTitle={ + <> + + + } + /> + ) +} + +export default ShowRoomEditHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomFallbackNotFound/ShowRoomFallbackNotFound.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomFallbackNotFound/ShowRoomFallbackNotFound.tsx new file mode 100644 index 0000000..7048f89 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomFallbackNotFound/ShowRoomFallbackNotFound.tsx @@ -0,0 +1,29 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import Page from "../../Components/Page/Page" +import PageContent from "../../Components/PageContent/PageContent" +import PageHeader from "../../Components/PageHeader/PageHeader" + +const ShowRoomFallbackNotFound = () => ( + + + + + + + + + } + /> + + {getMessage("page_showRoom_fallback_notFound_message")} + + +) + +export default ShowRoomFallbackNotFound diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomGuardNeedsAuth/ShowRoomGuardNeedsAuth.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomGuardNeedsAuth/ShowRoomGuardNeedsAuth.tsx new file mode 100644 index 0000000..6954623 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomGuardNeedsAuth/ShowRoomGuardNeedsAuth.tsx @@ -0,0 +1,47 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import Typography from "@mui/material/Typography" +import { FC } from "react" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import Page from "../../Components/Page/Page" +import PageContent from "../../Components/PageContent/PageContent" +import PageHeader from "../../Components/PageHeader/PageHeader" +import { RoomAuthedContext } from "../../Components/RoomAuthedContext" + +const ShowRoomGuardNeedsAuth: FC = ({ children }) => { + const isLoggedIn = useContextSelector( + RoomAuthedContext, + ({ isLoggedIn }) => isLoggedIn + ) + + if (!isLoggedIn) { + return ( + + + + + + + + + } + title={getMessage("page_showRoom_guard_needsAuth")} + /> + + + + {getMessage("page_showRoom_guard_needsAuth_message")} + + + + ) + } + + return <>{children} +} + +export default ShowRoomGuardNeedsAuth diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverview.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverview.tsx new file mode 100644 index 0000000..df114ee --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverview.tsx @@ -0,0 +1,29 @@ +import { Suspense } from "react" +import { Outlet } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import Loading from "../../Components/Loading/Loading" +import Page from "../../Components/Page/Page" +import { RoomContextProvider } from "../../Components/RoomContext" +import { ShowRoomContext } from "../ShowRoom/ShowRoomContext" +import ShowRoomOverviewHeader from "./ShowRoomOverviewHeader" +import { ShowRoomOverviewHeaderContextProvider } from "./ShowRoomOverviewHeaderContext" +import ShowRoomOverviewTabs from "./ShowRoomOverviewTabs" + +const ShowRoomOverview = () => { + const room = useContextSelector(ShowRoomContext, ({ room }) => room!) + return ( + + + + + + }> + + + + + + ) +} + +export default ShowRoomOverview diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeader.tsx new file mode 100644 index 0000000..0e49b8a --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeader.tsx @@ -0,0 +1,46 @@ +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import Box from "@mui/material/Box" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import PageHeader from "../../Components/PageHeader/PageHeader" +import { ShowRoomContext } from "../ShowRoom/ShowRoomContext" +import { ShowRoomOverviewHeaderContext } from "./ShowRoomOverviewHeaderContext" +import ShowRoomOverviewHeaderOptions from "./ShowRoomOverviewHeaderOptions" + +const ShowRoomOverviewHeader = () => { + const name = useContextSelector(ShowRoomContext, ({ room }) => room!.name) + + const setAfterTitleRef = useContextSelector( + ShowRoomOverviewHeaderContext, + ({ setAfterTitleRef }) => setAfterTitleRef + ) + + return ( + + + + + + + + } + afterTitle={ + <> + { + setAfterTitleRef(ref) + }} + sx={{ display: "flex", alignItems: "center", gap: 0.25 }} + /> + + + } + /> + ) +} + +export default ShowRoomOverviewHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderContext.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderContext.tsx new file mode 100644 index 0000000..7f9d055 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderContext.tsx @@ -0,0 +1,26 @@ +import { Dispatch, FC, SetStateAction, useState } from "react" +import { createContext } from "use-context-selector" + +type IShowRoomOverviewHeaderContext = { + afterTitleRef: HTMLDivElement | null + setAfterTitleRef: Dispatch> +} + +export const ShowRoomOverviewHeaderContext = createContext( + {} as IShowRoomOverviewHeaderContext +) + +export const ShowRoomOverviewHeaderContextProvider: FC = ({ children }) => { + // XXX: it seems use-context-selector dont support useRef... + const [afterTitleRef, setAfterTitleRef] = useState( + null + ) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderOptions.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderOptions.tsx new file mode 100644 index 0000000..f36f289 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderOptions.tsx @@ -0,0 +1,79 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { removeRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" +import DeleteIcon from "@mui/icons-material/Delete" +import EditIcon from "@mui/icons-material/Edit" +import LocalPoliceIcon from "@mui/icons-material/LocalPolice" +import MoreVertIcon from "@mui/icons-material/MoreVert" +import Divider from "@mui/material/Divider" +import ListItemIcon from "@mui/material/ListItemIcon" +import MenuItem from "@mui/material/MenuItem" +import MenuList from "@mui/material/MenuList" +import { FC, useCallback } from "react" +import { useNavigate } from "react-router" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import MenuButton from "../../Components/MenuButton/MenuButton" +import { RoomContext } from "../../Components/RoomContext" + +const handleRemoveRoom = makeStorageMutator(removeRoom) + +const ShowRoomOverviewHeaderOptionsDelete = () => { + const navigate = useNavigate() + const id = useContextSelector(RoomContext, ({ room }) => room.id) + + const handleDelete = useCallback(async () => { + await Promise.resolve(navigate("/rooms")) + await handleRemoveRoom(id) + }, [navigate]) + + return ( + + + + + {getMessage("page_showRoom_overview_header_options_delete")} + + ) +} + +const ShowRoomOverviewHeaderOptions: FC = () => ( + <> + } + menuContent={ +
+ + + + + + + + {getMessage("page_showRoom_overview_header_options_edit")} + + + + + + + + + + {getMessage( + "page_showRoom_overview_header_options_credentials" + )} + + + + + + + +
+ } + /> + +) + +export default ShowRoomOverviewHeaderOptions diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderSlot.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderSlot.tsx new file mode 100644 index 0000000..2d28129 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewHeaderSlot.tsx @@ -0,0 +1,19 @@ +import { FC } from "react" +import { createPortal } from "react-dom" +import { useContextSelector } from "use-context-selector" +import { ShowRoomOverviewHeaderContext } from "./ShowRoomOverviewHeaderContext" + +const ShowRoomOverviewHeaderSlot: FC = ({ children }) => { + const afterTitleRef = useContextSelector( + ShowRoomOverviewHeaderContext, + ({ afterTitleRef }) => afterTitleRef + ) + + if (!afterTitleRef) { + return null + } + + return <>{createPortal(<>{children}, afterTitleRef)} +} + +export default ShowRoomOverviewHeaderSlot diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewTabs.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewTabs.tsx new file mode 100644 index 0000000..75807da --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverview/ShowRoomOverviewTabs.tsx @@ -0,0 +1,39 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Box from "@mui/material/Box" +import Divider from "@mui/material/Divider" +import Tab from "@mui/material/Tab" +import Tabs from "@mui/material/Tabs" +import { Link } from "react-router-dom" +import { useRouteMatch } from "../../Hooks/useRouteMatch" + +const ShowRoomOverviewTabs = () => { + const currentTab = useRouteMatch(["/rooms/:id/courses", "/rooms/:id/chats"]) + + if (!currentTab) { + return null + } + + return ( + + + + + + + + + + ) +} + +export default ShowRoomOverviewTabs diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChats.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChats.tsx new file mode 100644 index 0000000..396b904 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChats.tsx @@ -0,0 +1,18 @@ +import { RoomChatsContextProvider } from "../../Components/RoomChatsContext" +import RoomGuardNeedsAuth from "../../Components/RoomGuardNeedsAuth/RoomGuardNeedsAuth" +import ShowRoomOverviewChatsContent from "./ShowRoomOverviewChatsContent" +import ShowRoomOverviewChatsFeedbacks from "./ShowRoomOverviewChatsFeedbacks" +import ShowRoomOverviewChatsHeader from "./ShowRoomOverviewChatsHeader" + +const ShowRoomOverviewChats = () => ( + + + + + + + + +) + +export default ShowRoomOverviewChats diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContent.tsx new file mode 100644 index 0000000..94eaf14 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContent.tsx @@ -0,0 +1,10 @@ +import PageContent from "../../Components/PageContent/PageContent" +import ShowRoomOverviewChatsContentList from "./ShowRoomOverviewChatsContentList" + +const ShowRoomOverviewChatsContent = () => ( + + + +) + +export default ShowRoomOverviewChatsContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContentList.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContentList.tsx new file mode 100644 index 0000000..ce33935 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContentList.tsx @@ -0,0 +1,25 @@ +import { Virtuoso } from "react-virtuoso" +import { useContextSelector } from "use-context-selector" +import { RoomChatsContext } from "../../Components/RoomChatsContext" +import ShowRoomOverviewChatsContentListItem from "./ShowRoomOverviewChatsContentListItem" + +const ShowRoomOverviewChatsContentList = () => { + const data = useContextSelector( + RoomChatsContext, + ({ conversationsQuery: { data } }) => data || [] + ) + + return ( + <> + ( + + )} + /> + + ) +} + +export default ShowRoomOverviewChatsContentList diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContentListItem.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContentListItem.tsx new file mode 100644 index 0000000..400c5fc --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsContentListItem.tsx @@ -0,0 +1,24 @@ +import { IMessageAreaContact } from "@ava-pro/crawlers/lib/Typings/IMessageAreaContact" +import { FC } from "react" +import { Link } from "react-router-dom" +import ContactCard from "../../Components/ContactCard/ContactCard" + +type IShowRoomOverviewChatsContentListItemProps = { + contact: IMessageAreaContact +} + +const ShowRoomOverviewChatsContentListItem: FC< + IShowRoomOverviewChatsContentListItemProps +> = ({ contact }) => ( + + + +) + +export default ShowRoomOverviewChatsContentListItem diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsFeedbacks.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsFeedbacks.tsx new file mode 100644 index 0000000..58e723d --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsFeedbacks.tsx @@ -0,0 +1,36 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import { FC } from "react" +import { useContextSelector } from "use-context-selector" +import Loading from "../../Components/Loading/Loading" +import PageContent from "../../Components/PageContent/PageContent" +import { RoomChatsContext } from "../../Components/RoomChatsContext" + +const ShowRoomOverviewChatsFeedbacks: FC = ({ children }) => { + const { isLoading, data, error } = useContextSelector( + RoomChatsContext, + ({ conversationsQuery }) => conversationsQuery + ) + + if (isLoading) { + return ( + + + + ) + } + + if (error || !data) { + return ( + + + {getMessage("page_showRoom_overviewChats_feedback_error")} + + + ) + } + + return <>{children} +} + +export default ShowRoomOverviewChatsFeedbacks diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeader.tsx new file mode 100644 index 0000000..3e628c3 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeader.tsx @@ -0,0 +1,13 @@ +import ShowRoomOverviewHeaderSlot from "../ShowRoomOverview/ShowRoomOverviewHeaderSlot" +import ShowRoomOverviewChatsHeaderRefresh from "./ShowRoomOverviewChatsHeaderRefresh" +import ShowRoomOverviewChatsHeaderSearch from "./ShowRoomOverviewChatsHeaderSearch" + +const ShowRoomOverviewChatsHeader = () => ( + <> + + + + + +) +export default ShowRoomOverviewChatsHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeaderRefresh.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeaderRefresh.tsx new file mode 100644 index 0000000..3f42f90 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeaderRefresh.tsx @@ -0,0 +1,40 @@ +import RefreshIcon from "@mui/icons-material/Refresh" +import IconButton from "@mui/material/IconButton" +import { useContextSelector } from "use-context-selector" +import { RoomAuthedContext } from "../../Components/RoomAuthedContext" +import { RoomChatsContext } from "../../Components/RoomChatsContext" + +const ShowRoomOverviewChatsHeaderRefresh = () => { + const isLoading = useContextSelector( + RoomChatsContext, + ({ conversationsQuery: { isLoading } }) => isLoading + ) + + const isLoggedIn = useContextSelector( + RoomAuthedContext, + ({ isLoggedIn }) => isLoggedIn + ) + + const reload = useContextSelector( + RoomChatsContext, + ({ conversationsQuery: { refetch } }) => refetch + ) + + const canRefresh = isLoggedIn && !isLoading + + return ( + { + if (canRefresh) { + void reload() + } + }} + > + + + ) +} + +export default ShowRoomOverviewChatsHeaderRefresh diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeaderSearch.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeaderSearch.tsx new file mode 100644 index 0000000..deea676 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewChats/ShowRoomOverviewChatsHeaderSearch.tsx @@ -0,0 +1,25 @@ +import SearchIcon from "@mui/icons-material/Search" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import { RoomAuthedContext } from "../../Components/RoomAuthedContext" + +const ShowRoomOverviewChatsHeaderSearch = () => { + const canSearch = useContextSelector( + RoomAuthedContext, + ({ isLoggedIn }) => isLoggedIn + ) + + return ( + !canSearch && e.preventDefault()} + > + + + + + ) +} + +export default ShowRoomOverviewChatsHeaderSearch diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCourses.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCourses.tsx new file mode 100644 index 0000000..aac0aa4 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCourses.tsx @@ -0,0 +1,15 @@ +import { RoomCachedCoursesContextProvider } from "../../Components/RoomCachedCoursesContext" +import ShowRoomOverviewCoursesContent from "./ShowRoomOverviewCoursesContent" +import ShowRoomOverviewCoursesFallback from "./ShowRoomOverviewCoursesFallback" +import ShowRoomOverviewCoursesHeader from "./ShowRoomOverviewCoursesHeader" + +const ShowRoomOverviewCoursesPage = () => ( + + + + + + +) + +export default ShowRoomOverviewCoursesPage diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesContent.tsx new file mode 100644 index 0000000..958f3f9 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesContent.tsx @@ -0,0 +1,22 @@ +import { useContextSelector } from "use-context-selector" +import CourseList from "../../Components/CourseList/CourseList" +import PageContent from "../../Components/PageContent/PageContent" +import { RoomCoursesContext } from "../../Components/RoomCoursesContext" +import { useFeedbackFetchCoursesQuery } from "../../Hooks/useFeedbackFetchCoursesQuery" + +const ShowRoomOverviewCoursesContent = () => { + const courses = useContextSelector( + RoomCoursesContext, + ({ courses }) => courses + ) + + useFeedbackFetchCoursesQuery() + + return ( + + + + ) +} + +export default ShowRoomOverviewCoursesContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallback.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallback.tsx new file mode 100644 index 0000000..c387020 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallback.tsx @@ -0,0 +1,45 @@ +import { FC } from "react" +import { useContextSelector } from "use-context-selector" +import { RoomCachedCoursesContext } from "../../Components/RoomCachedCoursesContext" +import { RoomCoursesContext } from "../../Components/RoomCoursesContext" +import ShowRoomOverviewCoursesFallbackErrorLoadingCache from "./ShowRoomOverviewCoursesFallbackErrorLoadingCache" +import ShowRoomOverviewCoursesFallbackLoadingCache from "./ShowRoomOverviewCoursesFallbackLoadingCache" +import ShowRoomOverviewCoursesFallbackNoCourses from "./ShowRoomOverviewCoursesFallbackNoCourses" + +const ShowRoomOverviewCoursesFallback: FC = ({ children }) => { + const hasCache = useContextSelector( + RoomCachedCoursesContext, + ({ hasCache }) => hasCache + ) + + const isLoadingCache = useContextSelector( + RoomCachedCoursesContext, + ({ isLoadingCache }) => isLoadingCache + ) + + const loadCacheError = useContextSelector( + RoomCachedCoursesContext, + ({ loadCacheError }) => loadCacheError + ) + + const courses = useContextSelector( + RoomCoursesContext, + ({ courses }) => courses + ) + + if (!hasCache && isLoadingCache) { + return + } + + if (!hasCache && (loadCacheError || !isLoadingCache)) { + return + } + + if (!isLoadingCache && courses.length === 0) { + return + } + + return <>{children} +} + +export default ShowRoomOverviewCoursesFallback diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackErrorLoadingCache.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackErrorLoadingCache.tsx new file mode 100644 index 0000000..49022f6 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackErrorLoadingCache.tsx @@ -0,0 +1,28 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import { useContextSelector } from "use-context-selector" +import PageContent from "../../Components/PageContent/PageContent" +import { RoomCachedCoursesContext } from "../../Components/RoomCachedCoursesContext" + +const ShowRoomOverviewCoursesFallbackErrorLoadingCache = () => { + const reload = useContextSelector( + RoomCachedCoursesContext, + ({ reload }) => reload + ) + + return ( + + + {getMessage("page_showRoom_overviewCourses_feedback_error")}{" "} + reload()} + style={{ fontWeight: "bold", cursor: "pointer" }} + > + {getMessage("feedback_tryAgain")}. + + + + ) +} + +export default ShowRoomOverviewCoursesFallbackErrorLoadingCache diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackLoadingCache.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackLoadingCache.tsx new file mode 100644 index 0000000..5b949c1 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackLoadingCache.tsx @@ -0,0 +1,13 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import PageContent from "../../Components/PageContent/PageContent" + +const ShowRoomOverviewCoursesFallbackLoadingCache = () => ( + + + {getMessage("page_showRoom_overviewCourses_feedback_loadingCache")} + + +) + +export default ShowRoomOverviewCoursesFallbackLoadingCache diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackNoCourses.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackNoCourses.tsx new file mode 100644 index 0000000..bdae7d3 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesFallbackNoCourses.tsx @@ -0,0 +1,13 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import PageContent from "../../Components/PageContent/PageContent" + +const ShowRoomOverviewCoursesFallbackNoCourses = () => ( + + + {getMessage("page_showRoom_overviewCourses_feedback_noCourses")} + + +) + +export default ShowRoomOverviewCoursesFallbackNoCourses diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeader.tsx new file mode 100644 index 0000000..21d2526 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeader.tsx @@ -0,0 +1,14 @@ +import ShowRoomOverviewHeaderSlot from "../ShowRoomOverview/ShowRoomOverviewHeaderSlot" +import ShowRoomOverviewCoursesHeaderQuery from "./ShowRoomOverviewCoursesHeaderQuery" +import ShowRoomOverviewCoursesHeaderRefresh from "./ShowRoomOverviewCoursesHeaderRefresh" +import ShowRoomOverviewCoursesHeaderSearch from "./ShowRoomOverviewCoursesHeaderSearch" + +const ShowRoomOverviewCoursesHeader = () => ( + + + + + +) + +export default ShowRoomOverviewCoursesHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQuery.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQuery.tsx new file mode 100644 index 0000000..303ecf5 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQuery.tsx @@ -0,0 +1,20 @@ +import FilterListIcon from "@mui/icons-material/FilterList" +import Divider from "@mui/material/Divider" +import MenuButton from "../../Components/MenuButton/MenuButton" +import ShowRoomOverviewCoursesHeaderQueryFilter from "./ShowRoomOverviewCoursesHeaderQueryFilter" +import ShowRoomOverviewCoursesHeaderQuerySort from "./ShowRoomOverviewCoursesHeaderQuerySort" + +const ShowRoomOverviewCoursesHeaderQuery = () => ( + } + menuContent={ +
+ + + +
+ } + /> +) + +export default ShowRoomOverviewCoursesHeaderQuery diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQueryFilter.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQueryFilter.tsx new file mode 100644 index 0000000..c84fbcd --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQueryFilter.tsx @@ -0,0 +1,73 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { IFilterCoursesByStatus } from "@ava-pro/shared/lib/Interfaces/IFilterCoursesByStatus" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { getFilterCoursesBy } from "@ava-pro/shared/lib/Storage/Slicers/StorageSettingsSlicers" +import DoneIcon from "@mui/icons-material/Done" +import PendingActionsIcon from "@mui/icons-material/PendingActions" +import UpdateIcon from "@mui/icons-material/Update" +import ListItemIcon from "@mui/material/ListItemIcon" + +import ListSubheader from "@mui/material/ListSubheader" +import MenuItem from "@mui/material/MenuItem" +import MenuList from "@mui/material/MenuList" +import Typography from "@mui/material/Typography" +import { createElement } from "react" + +const filterByOptions = [ + { + value: IFilterCoursesByStatus.PAST, + icon: DoneIcon, + text: getMessage("page_showRoom_overview_courses_query_filterBy_past") + }, + { + value: IFilterCoursesByStatus.IN_PROGRESS, + icon: PendingActionsIcon, + text: getMessage("page_showRoom_overview_courses_query_filterBy_inProgress") + }, + { + value: IFilterCoursesByStatus.FUTURE, + icon: UpdateIcon, + text: getMessage("page_showRoom_overview_courses_query_filterBy_future") + } +] + +const handleUpdateFilterCoursesBy = makeStorageMutator( + (state, payload: IFilterCoursesByStatus) => { + state.settings.filterCoursesByStatus = payload + } +) + +const ShowRoomOverviewCoursesHeaderQueryFilter = () => { + const { value: status } = useExtensionStorageSlicer( + getFilterCoursesBy, + -1 as IFilterCoursesByStatus | -1 + ) + + return ( + <> + + + {getMessage("page_showRoom_overview_courses_query_filterBy")} + + + + + {filterByOptions.map(({ value, text, icon }) => ( + void handleUpdateFilterCoursesBy(value)} + > + {createElement(icon)} + {text} + + ))} + + + ) +} + +export default ShowRoomOverviewCoursesHeaderQueryFilter diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQuerySort.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQuerySort.tsx new file mode 100644 index 0000000..743cfce --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderQuerySort.tsx @@ -0,0 +1,63 @@ +import { useExtensionStorageSlicer } from "@ava-pro/shared/lib/Hooks/useExtensionStorageSlicer" +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import { ICoursesOrderBy } from "@ava-pro/shared/lib/Interfaces/ICoursesOrderBy" +import { makeStorageMutator } from "@ava-pro/shared/lib/Storage/makeStorageMutator" +import { updateCoursesOrderBy } from "@ava-pro/shared/lib/Storage/Mutations/StorageSettingsMutations" +import { getCoursesOrderBy } from "@ava-pro/shared/lib/Storage/Slicers/StorageSettingsSlicers" +import HistoryIcon from "@mui/icons-material/History" +import SortByAlphaIcon from "@mui/icons-material/SortByAlpha" +import ListItemIcon from "@mui/material/ListItemIcon" +import ListSubheader from "@mui/material/ListSubheader" +import MenuItem from "@mui/material/MenuItem" +import MenuList from "@mui/material/MenuList" +import Typography from "@mui/material/Typography" +import { createElement } from "react" + +const orderByOptions = [ + { + value: ICoursesOrderBy.NAME, + icon: SortByAlphaIcon, + text: getMessage("page_showRoom_overview_courses_query_sortBy_name") + }, + { + value: ICoursesOrderBy.LAST_VISIT, + icon: HistoryIcon, + text: getMessage("page_showRoom_overview_courses_query_sortBy_lastAccess") + } +] + +const ShowRoomOverviewCoursesHeaderQuerySort = () => { + const handleUpdateCoursesOrderBy = makeStorageMutator(updateCoursesOrderBy) + + const { value: orderBy } = useExtensionStorageSlicer( + getCoursesOrderBy, + -1 as ICoursesOrderBy | -1 + ) + + return ( + <> + + + {getMessage("page_showRoom_overview_courses_query_sortBy")} + + + + + {orderByOptions.map(({ value, text, icon }) => ( + void handleUpdateCoursesOrderBy(value)} + > + {createElement(icon, {})} + {text} + + ))} + + + ) +} + +export default ShowRoomOverviewCoursesHeaderQuerySort diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderRefresh.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderRefresh.tsx new file mode 100644 index 0000000..93c22fe --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderRefresh.tsx @@ -0,0 +1,30 @@ +import RefreshIcon from "@mui/icons-material/Refresh" +import IconButton from "@mui/material/IconButton" +import { useContextSelector } from "use-context-selector" +import { RoomCachedCoursesContext } from "../../Components/RoomCachedCoursesContext" + +const ShowRoomOverviewCoursesHeaderRefresh = () => { + const isLoading = useContextSelector( + RoomCachedCoursesContext, + ({ isLoadingCache }) => isLoadingCache + ) + + const reload = useContextSelector( + RoomCachedCoursesContext, + ({ reload }) => reload + ) + + const canRefresh = !isLoading + + return ( + reload()} + > + + + ) +} + +export default ShowRoomOverviewCoursesHeaderRefresh diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderSearch.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderSearch.tsx new file mode 100644 index 0000000..7859478 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomOverviewCourses/ShowRoomOverviewCoursesHeaderSearch.tsx @@ -0,0 +1,24 @@ +import SearchIcon from "@mui/icons-material/Search" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import { RoomCachedCoursesContext } from "../../Components/RoomCachedCoursesContext" + +const ShowRoomOverviewCoursesHeaderSearch = () => { + const canSearch = useContextSelector( + RoomCachedCoursesContext, + ({ hasCache }) => hasCache + ) + return ( + !canSearch && e.preventDefault()} + > + + + + + ) +} + +export default ShowRoomOverviewCoursesHeaderSearch diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChats.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChats.tsx new file mode 100644 index 0000000..db2da82 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChats.tsx @@ -0,0 +1,18 @@ +import Page from "../../Components/Page/Page" +import { RoomCachedCoursesContextProvider } from "../../Components/RoomCachedCoursesContext" +import ShowRoomSearchChatsContent from "./ShowRoomSearchChatsContent" +import { ShowRoomSearchChatsContextProvider } from "./ShowRoomSearchChatsContext" +import ShowRoomSearchChatsHeader from "./ShowRoomSearchChatsHeader" + +const ShowRoomSearchChats = () => ( + + + + + + + + +) + +export default ShowRoomSearchChats diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsContent.tsx new file mode 100644 index 0000000..089b7cc --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsContent.tsx @@ -0,0 +1,67 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Box from "@mui/material/Box" +import Divider from "@mui/material/Divider" +import Tab from "@mui/material/Tab" +import Tabs from "@mui/material/Tabs" +import { lazy, Suspense, useState } from "react" +import Loading from "../../Components/Loading/Loading" +import PageContent from "../../Components/PageContent/PageContent" +import RoomGuardNeedsAuth from "../../Components/RoomGuardNeedsAuth/RoomGuardNeedsAuth" + +const ShowRoomSearchChatsConversations = lazy( + () => + import( + "../ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversations" + ) +) +const ShowRoomSearchChatsProfiles = lazy( + () => import("../ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfiles") +) + +const TAB_PROFILES = "profiles" +const TAB_CONVERSATIONS = "conversations" + +const ShowRoomSearchChatsContent = () => { + const [currentTab, setCurrentTab] = useState(TAB_CONVERSATIONS) + + return ( + + + setCurrentTab(tab)}> + + + + + + + + + }> + {currentTab === TAB_CONVERSATIONS && ( + + )} + {currentTab === TAB_PROFILES && } + + + + + + ) +} + +export default ShowRoomSearchChatsContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsContext.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsContext.tsx new file mode 100644 index 0000000..6a416e2 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsContext.tsx @@ -0,0 +1,42 @@ +import { IExtractedCourse } from "@ava-pro/shared/lib/Interfaces/IExtractedCourse" +import { Dispatch, FC, SetStateAction, useMemo, useState } from "react" +import { createContext, useContextSelector } from "use-context-selector" +import { RoomCoursesContext } from "../../Components/RoomCoursesContext" + +type IShowRoomSearchChatsContext = { + searchText: string + setSearchText: Dispatch> + + filteredCourses: IExtractedCourse[] +} + +export const ShowRoomSearchChatsContext = createContext( + {} as IShowRoomSearchChatsContext +) + +export const ShowRoomSearchChatsContextProvider: FC = ({ children }) => { + const [searchText, setSearchText] = useState("") + + const courses = useContextSelector( + RoomCoursesContext, + ({ courses }) => courses + ) + + const filteredCourses = useMemo( + () => + courses.filter((course) => + course.name + .toLocaleLowerCase() + .includes(searchText.trim().toLocaleLowerCase()) + ), + [courses, searchText] + ) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsHeader.tsx new file mode 100644 index 0000000..a53f2c1 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChats/ShowRoomSearchChatsHeader.tsx @@ -0,0 +1,44 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import PageHeader from "../../Components/PageHeader/PageHeader" +import SearchField from "../../Components/SearchField/SearchField" +import { ShowRoomSearchChatsContext } from "./ShowRoomSearchChatsContext" + +const ShowRoomSearchChatsHeader = () => { + const searchText = useContextSelector( + ShowRoomSearchChatsContext, + ({ searchText }) => searchText + ) + const setSearchText = useContextSelector( + ShowRoomSearchChatsContext, + ({ setSearchText }) => setSearchText + ) + + return ( + + + + + + + + } + mainContent={ + + } + /> + ) +} + +export default ShowRoomSearchChatsHeader diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversations.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversations.tsx new file mode 100644 index 0000000..acdff76 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversations.tsx @@ -0,0 +1,13 @@ +import ShowRoomSearchChatsConversationsContent from "./ShowRoomSearchChatsConversationsContent" +import { ShowRoomSearchChatsConversationsContextProvider } from "./ShowRoomSearchChatsConversationsContext" +import ShowRoomSearchChatsConversationsFallbacks from "./ShowRoomSearchChatsConversationsFallbacks" + +const ShowRoomSearchChatsConversations = () => ( + + + + + +) + +export default ShowRoomSearchChatsConversations diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsContent.tsx new file mode 100644 index 0000000..4ba2518 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsContent.tsx @@ -0,0 +1,49 @@ +import { strcmp } from "@ava-pro/shared/lib/Utils/strcmp" +import Box from "@mui/material/Box" +import { useMemo } from "react" +import { Link } from "react-router-dom" +import { Virtuoso } from "react-virtuoso" +import { useContextSelector } from "use-context-selector" +import ContactCard from "../../Components/ContactCard/ContactCard" +import { ShowRoomSearchChatsContext } from "../ShowRoomSearchChats/ShowRoomSearchChatsContext" +import { ShowRoomSearchChatsConversationsContext } from "./ShowRoomSearchChatsConversationsContext" + +const ShowRoomSearchChatsConversationsContent = () => { + const searchText = useContextSelector( + ShowRoomSearchChatsContext, + ({ searchText }) => searchText.trim().toLocaleLowerCase() + ) + + const conversations = useContextSelector( + ShowRoomSearchChatsConversationsContext, + ({ conversationsQuery: { data } }) => data ?? [] + ) + + const filteredConversations = useMemo( + () => + searchText.length > 0 + ? conversations + .filter((i) => i.fullname.toLocaleLowerCase().includes(searchText)) + .sort((a, b) => strcmp(a.fullname, b.fullname)) + : [], + [conversations, searchText] + ) + + return ( + + ( + <> + + + + + )} + /> + + ) +} + +export default ShowRoomSearchChatsConversationsContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsContext.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsContext.tsx new file mode 100644 index 0000000..dbe6c9b --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsContext.tsx @@ -0,0 +1,27 @@ +import { FC } from "react" +import { createContext } from "use-context-selector" +import { useRoomConversationsQuery } from "../../Hooks/useRoomConversationsQuery" + +type IShowRoomSearchChatsConversationsContext = { + conversationsQuery: ReturnType< + typeof useRoomConversationsQuery + >["conversationsQuery"] +} + +export const ShowRoomSearchChatsConversationsContext = createContext( + {} as IShowRoomSearchChatsConversationsContext +) + +export const ShowRoomSearchChatsConversationsContextProvider: FC = ({ + children +}) => { + const { conversationsQuery } = useRoomConversationsQuery() + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsFallbacks.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsFallbacks.tsx new file mode 100644 index 0000000..c8ec3fd --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsConversations/ShowRoomSearchChatsConversationsFallbacks.tsx @@ -0,0 +1,41 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import { FC } from "react" +import { useContextSelector } from "use-context-selector" +import Loading from "../../Components/Loading/Loading" +import PageSpacing from "../../Components/PageSpacing/PageSpacing" +import { ShowRoomSearchChatsConversationsContext } from "./ShowRoomSearchChatsConversationsContext" + +const ShowRoomSearchChatsConversationsFallbacks: FC = ({ children }) => { + const error = useContextSelector( + ShowRoomSearchChatsConversationsContext, + ({ conversationsQuery: { error } }) => error + ) + + const isLoading = useContextSelector( + ShowRoomSearchChatsConversationsContext, + ({ conversationsQuery: { isLoading } }) => isLoading + ) + + if (isLoading) { + return ( + + + + ) + } + + if (error) { + return ( + + + {getMessage("feedback_somethingWentWrong")} + + + ) + } + + return <>{children} +} + +export default ShowRoomSearchChatsConversationsFallbacks diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfiles.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfiles.tsx new file mode 100644 index 0000000..873ee78 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfiles.tsx @@ -0,0 +1,13 @@ +import ShowRoomSearchChatsProfilesContent from "./ShowRoomSearchChatsProfilesContent" +import { ShowRoomSearchChatsProfilesContextProvider } from "./ShowRoomSearchChatsProfilesContext" +import ShowRoomSearchChatsProfilesFallbacks from "./ShowRoomSearchChatsProfilesFallbacks" + +const ShowRoomSearchChatsProfiles = () => ( + + + + + +) + +export default ShowRoomSearchChatsProfiles diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesContent.tsx new file mode 100644 index 0000000..44d47ba --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesContent.tsx @@ -0,0 +1,54 @@ +import { IMessageAreaContact } from "@ava-pro/crawlers/lib/Typings/IMessageAreaContact" +import Box from "@mui/material/Box" +import Divider from "@mui/material/Divider" +import { useMemo } from "react" +import { Link } from "react-router-dom" +import { Virtuoso } from "react-virtuoso" +import { useContextSelector } from "use-context-selector" +import ContactCard from "../../Components/ContactCard/ContactCard" +import { ShowRoomSearchChatsProfilesContext } from "./ShowRoomSearchChatsProfilesContext" + +const ShowRoomSearchChatsProfilesContent = () => { + const contacts = useContextSelector( + ShowRoomSearchChatsProfilesContext, + ({ profilesQuery: { data } }) => data?.contacts ?? [] + ) + + const nonContacts = useContextSelector( + ShowRoomSearchChatsProfilesContext, + ({ profilesQuery: { data } }) => data?.nonContacts ?? [] + ) + + const allContacts = useMemo( + () => ([] as IMessageAreaContact[]).concat(contacts, nonContacts), + [contacts, nonContacts] + ) + + return ( + + ( + + + + + + + )} + /> + + ) +} + +export default ShowRoomSearchChatsProfilesContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesContext.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesContext.tsx new file mode 100644 index 0000000..cf9e8b3 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesContext.tsx @@ -0,0 +1,63 @@ +import { Search } from "@ava-pro/crawlers/lib/Scrappers/AjaxServices/CoreMessage/DataForMessageArea/Search" +import { IMessageAreaContact } from "@ava-pro/crawlers/lib/Typings/IMessageAreaContact" +import { FC } from "react" +import { useQuery, UseQueryResult } from "react-query" +import { createContext, useContextSelector } from "use-context-selector" +import { useDebounce } from "use-debounce" +import { RoomAuthedContext } from "../../Components/RoomAuthedContext" +import { RoomContext } from "../../Components/RoomContext" +import { ShowRoomSearchChatsContext } from "../ShowRoomSearchChats/ShowRoomSearchChatsContext" + +type IShowRoomSearchChatsProfilesContext = { + profilesQuery: UseQueryResult<{ + contacts: IMessageAreaContact[] + nonContacts: IMessageAreaContact[] + }> +} + +export const ShowRoomSearchChatsProfilesContext = createContext( + {} as IShowRoomSearchChatsProfilesContext +) + +export const ShowRoomSearchChatsProfilesContextProvider: FC = ({ + children +}) => { + const userId = useContextSelector(RoomAuthedContext, ({ userId }) => userId) + + const crawlerFetch = useContextSelector( + RoomContext, + ({ crawlerFetch }) => crawlerFetch + ) + + const inputSearchText = useContextSelector( + ShowRoomSearchChatsContext, + ({ searchText }) => searchText.trim() + ) + + const [debouncedSearchText] = useDebounce(inputSearchText, 1000) + + const profilesQuery = useQuery( + [userId, "search", debouncedSearchText, crawlerFetch], + async () => { + if (userId !== null && debouncedSearchText.length > 0) { + const { contacts, noncontacts: nonContacts } = await Search( + crawlerFetch + )({ userid: userId, search: debouncedSearchText }) + return { contacts, nonContacts } + } + return { contacts: [], nonContacts: [] } + } + ) + + const isLoading = + profilesQuery.isLoading || + (inputSearchText !== debouncedSearchText && inputSearchText.length > 0) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesFallbacks.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesFallbacks.tsx new file mode 100644 index 0000000..326c4bb --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchChatsProfiles/ShowRoomSearchChatsProfilesFallbacks.tsx @@ -0,0 +1,36 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Alert from "@mui/material/Alert" +import { FC } from "react" +import { useContextSelector } from "use-context-selector" +import Loading from "../../Components/Loading/Loading" +import PageSpacing from "../../Components/PageSpacing/PageSpacing" +import { ShowRoomSearchChatsProfilesContext } from "./ShowRoomSearchChatsProfilesContext" + +const ShowRoomSearchChatsProfilesFallbacks: FC = ({ children }) => { + const isLoading = useContextSelector( + ShowRoomSearchChatsProfilesContext, + ({ profilesQuery: { isLoading } }) => isLoading + ) + const error = useContextSelector( + ShowRoomSearchChatsProfilesContext, + ({ profilesQuery: { error } }) => error + ) + + if (isLoading) { + return + } + + if (error) { + return ( + + + {getMessage("feedback_somethingWentWrong")} + + + ) + } + + return <>{children} +} + +export default ShowRoomSearchChatsProfilesFallbacks diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCourses.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCourses.tsx new file mode 100644 index 0000000..e700100 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCourses.tsx @@ -0,0 +1,18 @@ +import Page from "../../Components/Page/Page" +import { RoomCachedCoursesContextProvider } from "../../Components/RoomCachedCoursesContext" +import ShowRoomSearchCoursesContent from "./ShowRoomSearchCoursesContent" +import { ShowRoomSearchCoursesContextProvider } from "./ShowRoomSearchCoursesContext" +import ShowRoomSearchCoursesHeader from "./ShowRoomSearchCoursesHeader" + +const ShowRoomSearchCourses = () => ( + + + + + + + + +) + +export default ShowRoomSearchCourses diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesContent.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesContent.tsx new file mode 100644 index 0000000..ec873fd --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesContent.tsx @@ -0,0 +1,38 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import Typography from "@mui/material/Typography" +import { useContextSelector } from "use-context-selector" +import CourseList from "../../Components/CourseList/CourseList" +import PageContent from "../../Components/PageContent/PageContent" +import { ShowRoomSearchCoursesContext } from "./ShowRoomSearchCoursesContext" + +const ShowRoomSearchCoursesContent = () => { + const searchText = useContextSelector( + ShowRoomSearchCoursesContext, + ({ searchText }) => searchText + ) + + const filteredCourses = useContextSelector( + ShowRoomSearchCoursesContext, + ({ filteredCourses }) => filteredCourses + ) + + if (!searchText) { + return {/* nothing to show :) */} + } + + if (filteredCourses.length === 0) { + return ( + + {getMessage("feedback_noResultsFound")} + + ) + } + + return ( + + + + ) +} + +export default ShowRoomSearchCoursesContent diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesContext.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesContext.tsx new file mode 100644 index 0000000..09edb8f --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesContext.tsx @@ -0,0 +1,42 @@ +import { IExtractedCourse } from "@ava-pro/shared/lib/Interfaces/IExtractedCourse" +import { Dispatch, FC, SetStateAction, useMemo, useState } from "react" +import { createContext, useContextSelector } from "use-context-selector" +import { RoomCoursesContext } from "../../Components/RoomCoursesContext" + +type IShowRoomSearchCoursesContext = { + searchText: string + setSearchText: Dispatch> + + filteredCourses: IExtractedCourse[] +} + +export const ShowRoomSearchCoursesContext = createContext( + {} as IShowRoomSearchCoursesContext +) + +export const ShowRoomSearchCoursesContextProvider: FC = ({ children }) => { + const [searchText, setSearchText] = useState("") + + const courses = useContextSelector( + RoomCoursesContext, + ({ courses }) => courses + ) + + const filteredCourses = useMemo( + () => + courses.filter((course) => + course.name + .toLocaleLowerCase() + .includes(searchText.trim().toLocaleLowerCase()) + ), + [courses, searchText] + ) + + return ( + + {children} + + ) +} diff --git a/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesHeader.tsx b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesHeader.tsx new file mode 100644 index 0000000..a0b1016 --- /dev/null +++ b/packages/webextension/src/BrowserAction/Pages/ShowRoomSearchCourses/ShowRoomSearchCoursesHeader.tsx @@ -0,0 +1,44 @@ +import { getMessage } from "@ava-pro/shared/lib/i18n/getMessage" +import ArrowBackIcon from "@mui/icons-material/ArrowBack" +import IconButton from "@mui/material/IconButton" +import { Link } from "react-router-dom" +import { useContextSelector } from "use-context-selector" +import PageHeader from "../../Components/PageHeader/PageHeader" +import SearchField from "../../Components/SearchField/SearchField" +import { ShowRoomSearchCoursesContext } from "./ShowRoomSearchCoursesContext" + +const ShowRoomSearchCoursesHeader = () => { + const searchText = useContextSelector( + ShowRoomSearchCoursesContext, + ({ searchText }) => searchText + ) + const setSearchText = useContextSelector( + ShowRoomSearchCoursesContext, + ({ setSearchText }) => setSearchText + ) + + return ( + + + + + + + + } + mainContent={ + + } + /> + ) +} + +export default ShowRoomSearchCoursesHeader diff --git a/packages/webextension/src/BrowserAction/index.html b/packages/webextension/src/BrowserAction/index.html new file mode 100644 index 0000000..ab6e8da --- /dev/null +++ b/packages/webextension/src/BrowserAction/index.html @@ -0,0 +1,11 @@ + + + + + AVA Pro + + + +
+ + diff --git a/packages/webextension/src/BrowserAction/main.tsx b/packages/webextension/src/BrowserAction/main.tsx new file mode 100644 index 0000000..7332ae9 --- /dev/null +++ b/packages/webextension/src/BrowserAction/main.tsx @@ -0,0 +1,32 @@ +import CssBaseline from "@mui/material/CssBaseline" +import { createTheme, ThemeProvider } from "@mui/material/styles" +import { SnackbarProvider } from "notistack" +import { StrictMode } from "react" +import { render } from "react-dom" +import { QueryClient, QueryClientProvider } from "react-query" +import { HashRouter as Router } from "react-router-dom" +import App from "./App" + +const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false, retry: false } } +}) + +const theme = createTheme({ palette: { mode: "light" } }) + +render( + + + + + + + + + + + + , + document.getElementById("root") +) diff --git a/packages/webextension/src/BrowserAction/modules.d.ts b/packages/webextension/src/BrowserAction/modules.d.ts new file mode 100644 index 0000000..2ab174f --- /dev/null +++ b/packages/webextension/src/BrowserAction/modules.d.ts @@ -0,0 +1 @@ +declare module "*.module.css" diff --git a/packages/webextension/src/ContentScript/CheckCurrentPage/CheckCurrentPage.ts b/packages/webextension/src/ContentScript/CheckCurrentPage/CheckCurrentPage.ts new file mode 100644 index 0000000..a681de9 --- /dev/null +++ b/packages/webextension/src/ContentScript/CheckCurrentPage/CheckCurrentPage.ts @@ -0,0 +1,33 @@ +import { isMoodleInstance } from "@ava-pro/crawlers/lib/Scrappers/DocumentScrappers/IsMoodleInstance/IsMoodleInstance" +import { data } from "@ava-pro/shared/lib/centralStorage" +import * as logger from "@ava-pro/shared/lib/logger" +import normalizeUrl from "normalize-url" +import { handleMoodleInstance } from "../HandleMoodleInstance/HandleMoodleInstance" +import { handleRegisteredRoom } from "../HandleRegisteredRoom/HandleRegisteredRoom" + +export const checkCurrentPage = async () => { + const currentPageUrl = normalizeUrl(window.location.href, { + removeQueryParameters: false + }) + + if (isMoodleInstance(window.document)) { + logger.info("the current page is a Moodle instance!") + await handleMoodleInstance(currentPageUrl, window.document) + } + + const { rooms } = await data + + const currentPageRoom = rooms.find((room) => + currentPageUrl.startsWith(room.url) + ) + + if (currentPageRoom) { + logger.debug( + "this instance was found in the Extension's Storage!", + "|", + `room id: ${currentPageRoom.id}` + ) + + await handleRegisteredRoom(currentPageUrl, currentPageRoom) + } +} diff --git a/packages/webextension/src/ContentScript/HandleMoodleInstance/HandleMoodleInstance.ts b/packages/webextension/src/ContentScript/HandleMoodleInstance/HandleMoodleInstance.ts new file mode 100644 index 0000000..0e6290d --- /dev/null +++ b/packages/webextension/src/ContentScript/HandleMoodleInstance/HandleMoodleInstance.ts @@ -0,0 +1,42 @@ +import { getMoodleInstanceHome } from "@ava-pro/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceHome/GetMoodleInstanceHome" +import { getMoodleInstanceName } from "@ava-pro/crawlers/lib/Scrappers/DocumentScrappers/GetMoodleInstanceName/GetMoodleInstanceName" +import { IDetectedRoom } from "@ava-pro/shared/lib/Interfaces/IDetectedRoom" +import { IDetectedRoomResponse } from "@ava-pro/shared/lib/Interfaces/IDetectedRoomResponse" +import { IRuntimeMessageType } from "@ava-pro/shared/lib/Interfaces/IRuntimeMessageType" +import * as logger from "@ava-pro/shared/lib/logger" +import { sendRuntimeMessage } from "@ava-pro/shared/lib/RuntimeMessage/sendRuntimeMessage" +import { applyStorageMutation } from "@ava-pro/shared/lib/Storage/applyStorageMutation" +import { getSlicedStorageData } from "@ava-pro/shared/lib/Storage/getSlicedStorageData" +import { createDetectedRoom } from "@ava-pro/shared/lib/Storage/Mutations/StorageDetectedRoomMutations" +import { getDetectedRoomByUrl } from "@ava-pro/shared/lib/Storage/Slicers/StorageDetectedRoomSlicers" +import normalizeUrl from "normalize-url" + +export const handleMoodleInstance = async ( + currentPageUrl: string, + document: Document +) => { + const instanceHome = normalizeUrl( + getMoodleInstanceHome(document) ?? currentPageUrl + ) + + const instanceDetectedRoom = await getSlicedStorageData( + getDetectedRoomByUrl(instanceHome) + ) + + if (instanceDetectedRoom === undefined) { + logger.info("a new moodle instance was detected!") + + const detectedRoom: IDetectedRoom = { + url: instanceHome, + name: getMoodleInstanceName(document), + response: IDetectedRoomResponse.NONE + } + + await applyStorageMutation(createDetectedRoom, detectedRoom) + + await sendRuntimeMessage( + IRuntimeMessageType.NOTIFY_DETECTED_ROOM, + detectedRoom + ) + } +} diff --git a/packages/webextension/src/ContentScript/HandleRegisteredRoom/HandleRegisteredRoom.ts b/packages/webextension/src/ContentScript/HandleRegisteredRoom/HandleRegisteredRoom.ts new file mode 100644 index 0000000..bdd6873 --- /dev/null +++ b/packages/webextension/src/ContentScript/HandleRegisteredRoom/HandleRegisteredRoom.ts @@ -0,0 +1,56 @@ +import { routes } from "@ava-pro/crawlers/lib/routes" +import { getCourseIdFromURL } from "@ava-pro/crawlers/lib/Scrappers/getCourseIdFromURL" +import { IRoom } from "@ava-pro/shared/lib/Interfaces/IRoom" +import * as logger from "@ava-pro/shared/lib/logger" +import { applyStorageMutation } from "@ava-pro/shared/lib/Storage/applyStorageMutation" +import { updateRoomCourse } from "@ava-pro/shared/lib/Storage/Mutations/StorageRoomsMutations" + +export const handleRegisteredRoom = async ( + currentPageUrl: string, + room: IRoom +) => { + const subPath = currentPageUrl.replace(room.url, "") + + if (subPath.startsWith(routes.course())) { + logger.debug("it's a course page!") + + const courseId = getCourseIdFromURL(currentPageUrl) + + if (courseId !== null) { + await applyStorageMutation(updateRoomCourse, { + courseId, + roomId: room.id, + recipe: (course) => { + course.lastVisit = Date.now() + } + }) + } + } + + if (subPath.startsWith(routes.login())) { + logger.debug("it's a login page!") + + const { + credentials: { username, password, autoLogin } + } = room + + const form = document.querySelector("#login") as HTMLFormElement + + const inputUsername = form.querySelector("#username") as HTMLInputElement + inputUsername.value = username + + const inputPassword = form.querySelector("#password") as HTMLInputElement + inputPassword.value = password + + const hasLoginErrors = document.querySelectorAll(".loginerrors").length > 0 + + if ( + autoLogin && + !hasLoginErrors && + username.length > 0 && + password.length > 0 + ) { + form.submit() + } + } +} diff --git a/packages/webextension/src/ContentScript/index.ts b/packages/webextension/src/ContentScript/index.ts new file mode 100644 index 0000000..f4ed260 --- /dev/null +++ b/packages/webextension/src/ContentScript/index.ts @@ -0,0 +1,5 @@ +import { checkCurrentPage } from "./CheckCurrentPage/CheckCurrentPage" + +window.addEventListener("popstate", () => void checkCurrentPage()) + +void checkCurrentPage() diff --git a/packages/webextension/src/_locales/pt_BR/messages.json b/packages/webextension/src/_locales/pt_BR/messages.json new file mode 100644 index 0000000..cdeb664 --- /dev/null +++ b/packages/webextension/src/_locales/pt_BR/messages.json @@ -0,0 +1,224 @@ +{ + "component_contactCard_lastMessage_sentBy_me": { + "message": "Você: " + }, + "component_contactCard_profilePicture_alt": { + "message": "Foto de perfil de $1." + }, + "component_courseList_subheader_notPinned": { + "message": "Soltos" + }, + "component_courseList_subheader_pinned": { + "message": "Fixados" + }, + "component_detectedRoomList_subheader": { + "message": "Ambientes Detectados" + }, + "component_roomForm_input_name": { + "message": "Nome" + }, + "component_roomForm_input_name_feedback_invalid": { + "message": "Insira um nome válido." + }, + "component_roomForm_input_url": { + "message": "URL" + }, + "component_roomForm_input_url_feedback_invalid": { + "message": "Insira uma URL válida." + }, + "component_roomGuardNeedsAuth_feedback_1_intro": { + "message": "Para acessar este recurso, você precisa estar autenticado. Faça o" + }, + "component_roomGuardNeedsAuth_feedback_2_manualLogin": { + "message": "Login Manual no Ambiente" + }, + "component_roomGuardNeedsAuth_feedback_3_orSetupThe": { + "message": "ou configure o" + }, + "component_roomGuardNeedsAuth_feedback_4_automaticLogin": { + "message": "Login Automático" + }, + "component_roomList_subheader": { + "message": "Ambientes Cadastrados" + }, + "feedback_loading": { + "message": "Carregando..." + }, + "feedback_noResultsFound": { + "message": "Nenhum resultado foi encontrado." + }, + "feedback_somethingWentWrong": { + "message": "Infelizmente alguma coisa deu errado." + }, + "feedback_tryAgain": { + "message": "Tentar novamente" + }, + "notification_newDetectedRoom_description": { + "message": "Conseguimos detectar o ambiente \"$1\". Abra o popup desta extensão para ter acesso a mais ações." + }, + "notification_newDetectedRoom_title": { + "message": "Um Ambiente Moodle Foi Detectado" + }, + "page_addRoom": { + "message": "Adicionar Ambiente" + }, + "page_addRoom_form_submit": { + "message": "Concluído" + }, + "page_error_notFound": { + "message": "Esta página não foi encontrada" + }, + "page_error_notFound_message": { + "message": "A rota requisitada não existe ou está indisponível." + }, + "page_listDetectedRooms": { + "message": "Histórico de Ambientes Detectados" + }, + "page_listDetectedRooms_feedback_noneDetected": { + "message": "Nenhum ambiente foi detectado até o momento." + }, + "page_listRooms": { + "message": "Ambientes" + }, + "page_listRooms_feedback_noRoomsTooltip": { + "message": "Ainda não há ambientes registrados. Clique no botão \"+\" ou utilize a Detecção Automática." + }, + "page_settings": { + "message": "Configurações" + }, + "page_settings_about": { + "message": "Sobre" + }, + "page_settings_about_author": { + "message": "Autor" + }, + "page_settings_about_source_code": { + "message": "Código Fonte" + }, + "page_settings_about_source_code_license": { + "message": "Licença" + }, + "page_settings_about_version": { + "message": "Versão" + }, + "page_settings_contribute": { + "message": "Contribuir" + }, + "page_settings_contribute_message_help_donate_byPix": { + "message": "O projeto aceita contribuições no repositório do GitHub. Eu, Gabriel, também aceito doações no Pix." + }, + "page_settings_contribute_message_help_share": { + "message": "Você pode contribuir com o projeto compartilhando às pessoas que possam se beneficiar com essa extensão." + }, + "page_showRoom_chat_input_typeMessage": { + "message": "Digite uma mensagem" + }, + "page_showRoom_chat_submit": { + "message": "Enviar Mensagem" + }, + "page_showRoom_chats_fallback_notFound": { + "message": "Não foi possível carregar este chat." + }, + "page_showRoom_chats_fallback_notFound_message": { + "message": "Infelizmente não foi possível carregar esta conversa." + }, + "page_showRoom_credentials": { + "message": "Credenciais" + }, + "page_showRoom_credentials_form_input_autoLogin": { + "message": "Habilitar o Login Automático" + }, + "page_showRoom_credentials_form_input_autoLogin_feedback_autoDisable": { + "message": "Nota: Caso a autenticação falhe, o Login Automático será desabilitado." + }, + "page_showRoom_credentials_form_input_autoLogin_feedback_fillAll": { + "message": "Informe o usuário e senha para configurar o Login Automático." + }, + "page_showRoom_credentials_form_input_password": { + "message": "Senha" + }, + "page_showRoom_credentials_form_input_username": { + "message": "Usuário" + }, + "page_showRoom_credentials_form_submit": { + "message": "Salvar" + }, + "page_showRoom_edit": { + "message": "Editar Ambiente" + }, + "page_showRoom_edit_form_submit": { + "message": "Concluído" + }, + "page_showRoom_fallback_notFound": { + "message": "Ambiente Não Encontrado" + }, + "page_showRoom_fallback_notFound_message": { + "message": "O ambiente requisitado não foi encontrado." + }, + "page_showRoom_guard_needsAuth": { + "message": "Autenticação Necessária" + }, + "page_showRoom_guard_needsAuth_message": { + "message": "Você precisa estar autenticado para acessar este recurso." + }, + "page_showRoom_overviewChats_feedback_error": { + "message": "Não foi possível carregar a lista de chats." + }, + "page_showRoom_overviewCourses_feedback_error": { + "message": "Não foi possível carregar a lista de cursos. Certifique que você está conectado à Internet e autenticado no Ambiente." + }, + "page_showRoom_overviewCourses_feedback_loadingCache": { + "message": "Carregando a lista de cursos..." + }, + "page_showRoom_overviewCourses_feedback_noCourses": { + "message": "Nenhum curso foi encontrado." + }, + "page_showRoom_overview_courses_query_filterBy": { + "message": "Mostrar Cursos:" + }, + "page_showRoom_overview_courses_query_filterBy_future": { + "message": "Futuros" + }, + "page_showRoom_overview_courses_query_filterBy_inProgress": { + "message": "Em Progresso" + }, + "page_showRoom_overview_courses_query_filterBy_past": { + "message": "Concluídos" + }, + "page_showRoom_overview_courses_query_sortBy": { + "message": "Ordenar Cursos Por:" + }, + "page_showRoom_overview_courses_query_sortBy_lastAccess": { + "message": "Último Acesso" + }, + "page_showRoom_overview_courses_query_sortBy_name": { + "message": "Nome" + }, + "page_showRoom_overview_header_options_credentials": { + "message": "Credenciais" + }, + "page_showRoom_overview_header_options_delete": { + "message": "Excluir Ambiente" + }, + "page_showRoom_overview_header_options_edit": { + "message": "Editar" + }, + "page_showRoom_overview_tab_chats": { + "message": "Chats" + }, + "page_showRoom_overview_tab_courses": { + "message": "Cursos" + }, + "page_showRoom_searchChats_form_input_search": { + "message": "Buscar chat..." + }, + "page_showRoom_searchChats_form_tab_all": { + "message": "Todos os Perfis" + }, + "page_showRoom_searchChats_form_tab_onlyAlreadyStarted": { + "message": "Conversas Já Iniciadas" + }, + "page_showRoom_searchCourses_form_input_search": { + "message": "Buscar curso..." + } +} diff --git a/packages/webextension/src/icons/icon-128.png b/packages/webextension/src/icons/icon-128.png new file mode 100644 index 0000000..ab3065e Binary files /dev/null and b/packages/webextension/src/icons/icon-128.png differ diff --git a/packages/webextension/src/icons/icon-256.png b/packages/webextension/src/icons/icon-256.png new file mode 100644 index 0000000..e8c7697 Binary files /dev/null and b/packages/webextension/src/icons/icon-256.png differ diff --git a/packages/webextension/src/icons/icon.png b/packages/webextension/src/icons/icon.png new file mode 100644 index 0000000..5a27546 Binary files /dev/null and b/packages/webextension/src/icons/icon.png differ diff --git a/packages/webextension/src/manifest.json b/packages/webextension/src/manifest.json new file mode 100644 index 0000000..1623fe3 --- /dev/null +++ b/packages/webextension/src/manifest.json @@ -0,0 +1,33 @@ +{ + "manifest_version": 2, + "version": "0.0.2", + "name": "AVA Pro", + "description": "Extensão do navegador projetada para aprimorar a experiência do usuário nas plataformas educacionais Moodle™.", + "homepage_url": "https://github.com/guesant/ava-pro", + "web_accessible_resources": ["icons/*"], + "default_locale": "pt_BR", + "icons": { + "96": "./icons/icon.png", + "128": "./icons/icon-128.png", + "256": "./icons/icon-256.png" + }, + "content_scripts": [ + { + "matches": [""], + "js": ["ContentScript/index.ts"] + } + ], + "browser_action": { + "default_icon": { + "96": "./icons/icon.png", + "128": "./icons/icon-128.png", + "256": "./icons/icon-256.png" + }, + "default_popup": "BrowserAction/index.html", + "default_title": "AVA Pro" + }, + "background": { + "scripts": ["BackgroundScript/index.ts"] + }, + "permissions": ["storage", "", "notifications"] +} diff --git a/packages/webextension/tsconfig.json b/packages/webextension/tsconfig.json new file mode 100755 index 0000000..c26abed --- /dev/null +++ b/packages/webextension/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "skipLibCheck": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..3902570 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6979 @@ +lockfileVersion: 5.3 + +importers: + + .: + specifiers: + '@typescript-eslint/eslint-plugin': ^5.4.0 + '@typescript-eslint/parser': ^5.4.0 + eslint: ^7.12.1 + eslint-config-prettier: ^8.3.0 + eslint-config-standard: ^16.0.3 + eslint-plugin-import: ^2.22.1 + eslint-plugin-node: ^11.1.0 + eslint-plugin-promise: ^4.2.1 + eslint-plugin-react: ^7.27.1 + husky: ^7.0.4 + lint-staged: ^12.1.2 + prettier: ^2.4.1 + prettier-config-standard: ^4.0.0 + devDependencies: + '@typescript-eslint/eslint-plugin': 5.4.0_e70a9477f5985665eda5342254cbd97d + '@typescript-eslint/parser': 5.4.0_eslint@7.32.0 + eslint: 7.32.0 + eslint-config-prettier: 8.3.0_eslint@7.32.0 + eslint-config-standard: 16.0.3_4756bc88b0f26b24b04bf0a729e3a9a6 + eslint-plugin-import: 2.25.3_eslint@7.32.0 + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 4.3.1 + eslint-plugin-react: 7.27.1_eslint@7.32.0 + husky: 7.0.4 + lint-staged: 12.1.2 + prettier: 2.5.0 + prettier-config-standard: 4.0.0 + + packages/crawlers: + specifiers: + '@ava-pro/shared': workspace:* + css-select: ^4.1.3 + domhandler: ^4.2.2 + domutils: ^2.8.0 + htmlparser2: ^7.2.0 + normalize-url: ^7.0.2 + ruty: ^1.2.3 + dependencies: + '@ava-pro/shared': link:../shared + css-select: 4.1.3 + domhandler: 4.2.2 + domutils: 2.8.0 + htmlparser2: 7.2.0 + normalize-url: 7.0.2 + ruty: 1.2.3 + + packages/shared: + specifiers: + '@types/webextension-polyfill': ^0.8.2 + fast-deep-equal: ^3.1.3 + immer: ^9.0.7 + nanoid: ^3.1.30 + normalize-url: ^7.0.2 + react: ^17.0.2 + superstruct: ^0.15.3 + typescript: ^4.5.2 + webextension-polyfill: ^0.8.0 + dependencies: + fast-deep-equal: 3.1.3 + immer: 9.0.7 + nanoid: 3.1.30 + normalize-url: 7.0.2 + react: 17.0.2 + superstruct: 0.15.3 + webextension-polyfill: 0.8.0 + devDependencies: + '@types/webextension-polyfill': 0.8.2 + typescript: 4.5.2 + + packages/webextension: + specifiers: + '@ava-pro/crawlers': workspace:* + '@ava-pro/shared': workspace:* + '@emotion/react': ^11.7.0 + '@emotion/styled': ^11.6.0 + '@hookform/error-message': ^2.0.0 + '@mui/icons-material': ^5.2.0 + '@mui/material': ^5.2.1 + '@parcel/config-default': ^2.0.1 + '@parcel/config-webextension': ^2.0.1 + '@parcel/transformer-typescript-tsc': ^2.0.1 + '@types/dompurify': ^2.3.1 + '@types/react': ^17.0.37 + '@types/react-dom': ^17.0.11 + dompurify: ^2.3.3 + normalize-url: ^7.0.2 + notistack: ^2.0.3 + parcel: ^2.0.1 + parcel-resolver-pnpm: ^0.0.1 + postcss: ^8.4.4 + react: ^17.0.2 + react-async: ^10.0.1 + react-dom: ^17.0.2 + react-hook-form: ^7.20.4 + react-query: ^3.33.7 + react-router: ^6.0.2 + react-router-dom: ^6.0.2 + react-virtuoso: ^2.2.8 + typescript: ^4.5.2 + use-context-selector: ^1.3.9 + use-debounce: ^7.0.1 + webextension-polyfill: ^0.8.0 + dependencies: + '@ava-pro/crawlers': link:../crawlers + '@ava-pro/shared': link:../shared + '@emotion/react': 11.7.0_cfedea9b3ed0faf0dded75c187406c5e + '@emotion/styled': 11.6.0_49e498e16442da1a9ae75db6f4430d97 + '@hookform/error-message': 2.0.0_39b32a31a624e7e0cee4922f35328d94 + '@mui/icons-material': 5.2.0_bd85fa67ae23f5646c1b449150c08cb7 + '@mui/material': 5.2.1_e775e23d77fc33f172a6bda0cd692c92 + dompurify: 2.3.3 + normalize-url: 7.0.2 + notistack: 2.0.3_a971fc2901e017cb84f6db81d8e99c7c + postcss: 8.4.4 + react: 17.0.2 + react-async: 10.0.1_react@17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-hook-form: 7.20.4_react@17.0.2 + react-query: 3.33.7_react-dom@17.0.2+react@17.0.2 + react-router: 6.0.2_react@17.0.2 + react-router-dom: 6.0.2_react-dom@17.0.2+react@17.0.2 + react-virtuoso: 2.2.8_react@17.0.2 + typescript: 4.5.2 + use-context-selector: 1.3.9_react-dom@17.0.2+react@17.0.2 + use-debounce: 7.0.1_react@17.0.2 + webextension-polyfill: 0.8.0 + devDependencies: + '@parcel/config-default': 2.0.1 + '@parcel/config-webextension': 2.0.1 + '@parcel/transformer-typescript-tsc': 2.0.1_typescript@4.5.2 + '@types/dompurify': 2.3.1 + '@types/react': 17.0.37 + '@types/react-dom': 17.0.11 + parcel: 2.0.1 + parcel-resolver-pnpm: 0.0.1 + +packages: + + /@babel/code-frame/7.12.11: + resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} + dependencies: + '@babel/highlight': 7.16.0 + dev: true + + /@babel/code-frame/7.16.0: + resolution: {integrity: sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.16.0 + + /@babel/compat-data/7.16.4: + resolution: {integrity: sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.16.0: + resolution: {integrity: sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.16.0 + '@babel/generator': 7.16.0 + '@babel/helper-compilation-targets': 7.16.3_@babel+core@7.16.0 + '@babel/helper-module-transforms': 7.16.0 + '@babel/helpers': 7.16.3 + '@babel/parser': 7.16.4 + '@babel/template': 7.16.0 + '@babel/traverse': 7.16.3 + '@babel/types': 7.16.0 + convert-source-map: 1.8.0 + debug: 4.3.2 + gensync: 1.0.0-beta.2 + json5: 2.2.0 + semver: 6.3.0 + source-map: 0.5.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator/7.16.0: + resolution: {integrity: sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + jsesc: 2.5.2 + source-map: 0.5.7 + dev: true + + /@babel/helper-compilation-targets/7.16.3_@babel+core@7.16.0: + resolution: {integrity: sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.16.4 + '@babel/core': 7.16.0 + '@babel/helper-validator-option': 7.14.5 + browserslist: 4.18.1 + semver: 6.3.0 + dev: true + + /@babel/helper-function-name/7.16.0: + resolution: {integrity: sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-get-function-arity': 7.16.0 + '@babel/template': 7.16.0 + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-get-function-arity/7.16.0: + resolution: {integrity: sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-hoist-variables/7.16.0: + resolution: {integrity: sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-member-expression-to-functions/7.16.0: + resolution: {integrity: sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-module-imports/7.16.0: + resolution: {integrity: sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + + /@babel/helper-module-transforms/7.16.0: + resolution: {integrity: sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-module-imports': 7.16.0 + '@babel/helper-replace-supers': 7.16.0 + '@babel/helper-simple-access': 7.16.0 + '@babel/helper-split-export-declaration': 7.16.0 + '@babel/helper-validator-identifier': 7.15.7 + '@babel/template': 7.16.0 + '@babel/traverse': 7.16.3 + '@babel/types': 7.16.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-optimise-call-expression/7.16.0: + resolution: {integrity: sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-plugin-utils/7.14.5: + resolution: {integrity: sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==} + engines: {node: '>=6.9.0'} + + /@babel/helper-replace-supers/7.16.0: + resolution: {integrity: sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-member-expression-to-functions': 7.16.0 + '@babel/helper-optimise-call-expression': 7.16.0 + '@babel/traverse': 7.16.3 + '@babel/types': 7.16.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-simple-access/7.16.0: + resolution: {integrity: sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-split-export-declaration/7.16.0: + resolution: {integrity: sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.16.0 + dev: true + + /@babel/helper-validator-identifier/7.15.7: + resolution: {integrity: sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-option/7.14.5: + resolution: {integrity: sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers/7.16.3: + resolution: {integrity: sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.16.0 + '@babel/traverse': 7.16.3 + '@babel/types': 7.16.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight/7.16.0: + resolution: {integrity: sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.15.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + + /@babel/parser/7.16.4: + resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==} + engines: {node: '>=6.0.0'} + hasBin: true + dev: true + + /@babel/plugin-syntax-flow/7.16.0_@babel+core@7.16.0: + resolution: {integrity: sha512-dH91yCo0RyqfzWgoM5Ji9ir8fQ+uFbt9KHM3d2x4jZOuHS6wNA+CRmRUP/BWCsHG2bjc7A2Way6AvH1eQk0wig==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.16.0 + '@babel/helper-plugin-utils': 7.14.5 + dev: true + + /@babel/plugin-syntax-jsx/7.16.0: + resolution: {integrity: sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: false + + /@babel/plugin-transform-flow-strip-types/7.16.0_@babel+core@7.16.0: + resolution: {integrity: sha512-vs/F5roOaO/+WxKfp9PkvLsAyj0G+Q0zbFimHm9X2KDgabN2XmNFoAafmeGEYspUlIF9+MvVmyek9UyHiqeG/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.16.0 + '@babel/helper-plugin-utils': 7.14.5 + '@babel/plugin-syntax-flow': 7.16.0_@babel+core@7.16.0 + dev: true + + /@babel/runtime/7.16.3: + resolution: {integrity: sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.9 + dev: false + + /@babel/template/7.16.0: + resolution: {integrity: sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.16.0 + '@babel/parser': 7.16.4 + '@babel/types': 7.16.0 + dev: true + + /@babel/traverse/7.16.3: + resolution: {integrity: sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.16.0 + '@babel/generator': 7.16.0 + '@babel/helper-function-name': 7.16.0 + '@babel/helper-hoist-variables': 7.16.0 + '@babel/helper-split-export-declaration': 7.16.0 + '@babel/parser': 7.16.4 + '@babel/types': 7.16.0 + debug: 4.3.2 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types/7.16.0: + resolution: {integrity: sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.15.7 + to-fast-properties: 2.0.0 + + /@emotion/babel-plugin/11.3.0: + resolution: {integrity: sha512-UZKwBV2rADuhRp+ZOGgNWg2eYgbzKzQXfQPtJbu/PLy8onurxlNCLvxMQEvlr1/GudguPI5IU9qIY1+2z1M5bA==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/helper-module-imports': 7.16.0 + '@babel/plugin-syntax-jsx': 7.16.0 + '@babel/runtime': 7.16.3 + '@emotion/hash': 0.8.0 + '@emotion/memoize': 0.7.5 + '@emotion/serialize': 1.0.2 + babel-plugin-macros: 2.8.0 + convert-source-map: 1.8.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.0.10 + dev: false + + /@emotion/cache/11.6.0: + resolution: {integrity: sha512-ElbsWY1KMwEowkv42vGo0UPuLgtPYfIs9BxxVrmvsaJVvktknsHYYlx5NQ5g6zLDcOTyamlDc7FkRg2TAcQDKQ==} + dependencies: + '@emotion/memoize': 0.7.5 + '@emotion/sheet': 1.1.0 + '@emotion/utils': 1.0.0 + '@emotion/weak-memoize': 0.2.5 + stylis: 4.0.10 + dev: false + + /@emotion/hash/0.8.0: + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + dev: false + + /@emotion/is-prop-valid/1.1.1: + resolution: {integrity: sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==} + dependencies: + '@emotion/memoize': 0.7.5 + dev: false + + /@emotion/memoize/0.7.5: + resolution: {integrity: sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==} + dev: false + + /@emotion/react/11.7.0_cfedea9b3ed0faf0dded75c187406c5e: + resolution: {integrity: sha512-WL93hf9+/2s3cA1JVJlz8+Uy6p6QWukqQFOm2OZO5ki51hfucHMOmbSjiyC3t2Y4RI8XUmBoepoc/24ny/VBbA==} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@babel/core': + optional: true + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@emotion/cache': 11.6.0 + '@emotion/serialize': 1.0.2 + '@emotion/sheet': 1.1.0 + '@emotion/utils': 1.0.0 + '@emotion/weak-memoize': 0.2.5 + '@types/react': 17.0.37 + hoist-non-react-statics: 3.3.2 + react: 17.0.2 + dev: false + + /@emotion/serialize/1.0.2: + resolution: {integrity: sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==} + dependencies: + '@emotion/hash': 0.8.0 + '@emotion/memoize': 0.7.5 + '@emotion/unitless': 0.7.5 + '@emotion/utils': 1.0.0 + csstype: 3.0.10 + dev: false + + /@emotion/sheet/1.1.0: + resolution: {integrity: sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==} + dev: false + + /@emotion/styled/11.6.0_49e498e16442da1a9ae75db6f4430d97: + resolution: {integrity: sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw==} + peerDependencies: + '@babel/core': ^7.0.0 + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@babel/core': + optional: true + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@emotion/babel-plugin': 11.3.0 + '@emotion/is-prop-valid': 1.1.1 + '@emotion/react': 11.7.0_cfedea9b3ed0faf0dded75c187406c5e + '@emotion/serialize': 1.0.2 + '@emotion/utils': 1.0.0 + '@types/react': 17.0.37 + react: 17.0.2 + dev: false + + /@emotion/unitless/0.7.5: + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + dev: false + + /@emotion/utils/1.0.0: + resolution: {integrity: sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==} + dev: false + + /@emotion/weak-memoize/0.2.5: + resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==} + dev: false + + /@eslint/eslintrc/0.4.3: + resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.2 + espree: 7.3.1 + globals: 13.12.0 + ignore: 4.0.6 + import-fresh: 3.3.0 + js-yaml: 3.14.1 + minimatch: 3.0.4 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@hookform/error-message/2.0.0_39b32a31a624e7e0cee4922f35328d94: + resolution: {integrity: sha512-Y90nHzjgL2MP7GFy75kscdvxrCTjtyxGmOLLxX14nd08OXRIh9lMH/y9Kpdo0p1IPowJBiZMHyueg7p+yrqynQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + react-hook-form: ^7.0.0 + dependencies: + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-hook-form: 7.20.4_react@17.0.2 + dev: false + + /@humanwhocodes/config-array/0.5.0: + resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.2 + minimatch: 3.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@iarna/toml/2.2.5: + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + dev: true + + /@mui/base/5.0.0-alpha.57_5539cae010396b202b015f3568914e95: + resolution: {integrity: sha512-UCJthNc4LGttoD/CxdCh8AaEu2B2uWNRW96J6PjlQ125+FEqO7+wuIGT98BNCGguVwetK/jTmo/fiHYDoW9gUA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^16.8.6 || ^17.0.0 + react: ^17.0.2 + react-dom: ^17.0.2 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@emotion/is-prop-valid': 1.1.1 + '@mui/utils': 5.2.1_react@17.0.2 + '@popperjs/core': 2.10.2 + '@types/react': 17.0.37 + clsx: 1.1.1 + prop-types: 15.7.2 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-is: 17.0.2 + dev: false + + /@mui/icons-material/5.2.0_bd85fa67ae23f5646c1b449150c08cb7: + resolution: {integrity: sha512-NvyrVaGKpP4R1yFw8BCnE0QcsQ67RtpgxPr4FtH8q60MDYPuPVczLOn5Ash5CFavoDWur/NfM/4DpT54yf3InA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@mui/material': ^5.0.0 + '@types/react': ^16.8.6 || ^17.0.0 + react: ^17.0.2 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@mui/material': 5.2.1_e775e23d77fc33f172a6bda0cd692c92 + '@types/react': 17.0.37 + react: 17.0.2 + dev: false + + /@mui/material/5.2.1_e775e23d77fc33f172a6bda0cd692c92: + resolution: {integrity: sha512-y38+e1Qf95rVQ4lK8knYj4o1kB/WwJU0f/lMNmzlaenqGpyhd1M/e3BNwuYEDOLSPWUVeP2LvX2mL/IhKytA9A==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^16.8.6 || ^17.0.0 + react: ^17.0.2 + react-dom: ^17.0.2 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@emotion/react': 11.7.0_cfedea9b3ed0faf0dded75c187406c5e + '@emotion/styled': 11.6.0_49e498e16442da1a9ae75db6f4430d97 + '@mui/base': 5.0.0-alpha.57_5539cae010396b202b015f3568914e95 + '@mui/system': 5.2.1_25200db5b8d56be7ab25a1ad35fc9f93 + '@mui/types': 7.1.0_@types+react@17.0.37 + '@mui/utils': 5.2.1_react@17.0.2 + '@types/react': 17.0.37 + '@types/react-transition-group': 4.4.4 + clsx: 1.1.1 + csstype: 3.0.10 + hoist-non-react-statics: 3.3.2 + prop-types: 15.7.2 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-is: 17.0.2 + react-transition-group: 4.4.2_react-dom@17.0.2+react@17.0.2 + dev: false + + /@mui/private-theming/5.2.1_cfedea9b3ed0faf0dded75c187406c5e: + resolution: {integrity: sha512-+OfgeZzEjqwd7Vo1kYISJyLHM+3yUO8UoKhLMtZ1DAlZlqovN6jetPtT6o4BnHEAsc3YC3DET+KicwkRtuvxbw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^16.8.6 || ^17.0.0 + react: ^17.0.2 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@mui/utils': 5.2.1_react@17.0.2 + '@types/react': 17.0.37 + prop-types: 15.7.2 + react: 17.0.2 + dev: false + + /@mui/styled-engine/5.2.0_c295042f06038c5042b55c2330454282: + resolution: {integrity: sha512-NZ4pWYQcM5wreUfiXRd7IMFRF+Nq1vMzsIdXtXNjgctJTKHunrofasoBqv+cqevO+hqT75ezSbNHyaXzOXp6Mg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.2 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@emotion/cache': 11.6.0 + '@emotion/react': 11.7.0_cfedea9b3ed0faf0dded75c187406c5e + '@emotion/styled': 11.6.0_49e498e16442da1a9ae75db6f4430d97 + prop-types: 15.7.2 + react: 17.0.2 + dev: false + + /@mui/system/5.2.1_25200db5b8d56be7ab25a1ad35fc9f93: + resolution: {integrity: sha512-C1mva6Uyk2bGCaa/FiaFseSt2iJymxgA8KnJJyKAz8ZQZzuetUV8JbY1qtV9CG1VlJb+Ldm7pc6Px8t59lGfZw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^16.8.6 || ^17.0.0 + react: ^17.0.2 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.16.3 + '@emotion/react': 11.7.0_cfedea9b3ed0faf0dded75c187406c5e + '@emotion/styled': 11.6.0_49e498e16442da1a9ae75db6f4430d97 + '@mui/private-theming': 5.2.1_cfedea9b3ed0faf0dded75c187406c5e + '@mui/styled-engine': 5.2.0_c295042f06038c5042b55c2330454282 + '@mui/types': 7.1.0_@types+react@17.0.37 + '@mui/utils': 5.2.1_react@17.0.2 + '@types/react': 17.0.37 + clsx: 1.1.1 + csstype: 3.0.10 + prop-types: 15.7.2 + react: 17.0.2 + dev: false + + /@mui/types/7.1.0_@types+react@17.0.37: + resolution: {integrity: sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ==} + peerDependencies: + '@types/react': '*' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 17.0.37 + dev: false + + /@mui/utils/5.2.1_react@17.0.2: + resolution: {integrity: sha512-JQH5ucBxBrubntrN2mvDcwkXlWaHuZGz5goxg9ixnZXRhlZ9Ed5knfsafrX4OFyXNT48DiZXaTRAEkqjyfCExQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + react: ^17.0.2 + dependencies: + '@babel/runtime': 7.16.3 + '@types/prop-types': 15.7.4 + '@types/react-is': 17.0.3 + prop-types: 15.7.2 + react: 17.0.2 + react-is: 17.0.2 + dev: false + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@parcel/babel-ast-utils/2.0.1: + resolution: {integrity: sha512-adBHMaPAj+w1NjO+oq6SUgtOpO7wmyNIgsiHDsf8cpLf2gT0GcC/afcaC07WhIq1PJvL2hkWQpT/8sj1m/QZSw==} + engines: {node: '>= 12.0.0'} + dependencies: + '@babel/parser': 7.16.4 + '@parcel/babylon-walk': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + astring: 1.8.1 + dev: true + + /@parcel/babylon-walk/2.0.1: + resolution: {integrity: sha512-eXlfG7ZGUuRF81mStZGeaYj4uH7Mgd8yfWB+c/Y13sxdacml+0vinCyZ9BjY7rYuxvKTlVSjp9BJW0Q0DS6THg==} + engines: {node: '>= 12.0.0'} + dependencies: + '@babel/types': 7.16.0 + lodash.clone: 4.5.0 + dev: true + + /@parcel/bundler-default/2.0.1: + resolution: {integrity: sha512-4BE86Z26gr7VHeIOCWkaucl5SNntCGS9ltk1ed65mqbZaZloZP8YD/YINxxgPtx9moTWNqQO8Y3bvCAD+VY8mQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/bundler-default/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-4BE86Z26gr7VHeIOCWkaucl5SNntCGS9ltk1ed65mqbZaZloZP8YD/YINxxgPtx9moTWNqQO8Y3bvCAD+VY8mQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/cache/2.0.1: + resolution: {integrity: sha512-aXWkx6ySwHBdPWvCJ1x6aHGFWlfu9X89iKuN4X/quMHyUDqA2PkKBR0kAvcs47ZnmUAXlKI2J9BR+lEOSAJazA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/logger': 2.0.1 + '@parcel/utils': 2.0.1 + lmdb-store: 1.6.14 + dev: true + + /@parcel/cache/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-aXWkx6ySwHBdPWvCJ1x6aHGFWlfu9X89iKuN4X/quMHyUDqA2PkKBR0kAvcs47ZnmUAXlKI2J9BR+lEOSAJazA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/core': 2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/utils': 2.0.1 + lmdb-store: 1.6.14 + dev: true + + /@parcel/codeframe/2.0.1: + resolution: {integrity: sha512-NfquLg7qt8TfPmmfXVPlcq5mtEM3CvYjc+s5HLt1w0H461NiZOq7qhAaSS1N/3E+3d3eXOT/2AlCxoGm7KQ8hg==} + engines: {node: '>= 12.0.0'} + dependencies: + chalk: 4.1.2 + emphasize: 4.2.0 + slice-ansi: 4.0.0 + string-width: 4.2.3 + dev: true + + /@parcel/compressor-raw/2.0.1: + resolution: {integrity: sha512-0VNadPUIIpgx2MCjt7PGOwcz0OXN0BFxCmWzy+ocyEWj0KQ79OBr8ni7I3Be78OxNhE8luTEC22kVJwM0rtP1g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/compressor-raw/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-0VNadPUIIpgx2MCjt7PGOwcz0OXN0BFxCmWzy+ocyEWj0KQ79OBr8ni7I3Be78OxNhE8luTEC22kVJwM0rtP1g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/config-default/2.0.1: + resolution: {integrity: sha512-LavQo5+81wYARmDW+GsgPIV6GPG/rskR73oGHWV1oDr9k3UD2RYdGaH1GDcwqXyUEWVCw3K+nglaZdWFpOEdRQ==} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/bundler-default': 2.0.1 + '@parcel/compressor-raw': 2.0.1 + '@parcel/namer-default': 2.0.1 + '@parcel/optimizer-cssnano': 2.0.1 + '@parcel/optimizer-htmlnano': 2.0.1 + '@parcel/optimizer-image': 2.0.1 + '@parcel/optimizer-svgo': 2.0.1 + '@parcel/optimizer-terser': 2.0.1 + '@parcel/packager-css': 2.0.1 + '@parcel/packager-html': 2.0.1 + '@parcel/packager-js': 2.0.1 + '@parcel/packager-raw': 2.0.1 + '@parcel/packager-svg': 2.0.1 + '@parcel/reporter-dev-server': 2.0.1 + '@parcel/resolver-default': 2.0.1 + '@parcel/runtime-browser-hmr': 2.0.1 + '@parcel/runtime-js': 2.0.1 + '@parcel/runtime-react-refresh': 2.0.1 + '@parcel/runtime-service-worker': 2.0.1 + '@parcel/transformer-babel': 2.0.1 + '@parcel/transformer-css': 2.0.1 + '@parcel/transformer-html': 2.0.1 + '@parcel/transformer-image': 2.0.1 + '@parcel/transformer-js': 2.0.1 + '@parcel/transformer-json': 2.0.1 + '@parcel/transformer-postcss': 2.0.1 + '@parcel/transformer-posthtml': 2.0.1 + '@parcel/transformer-raw': 2.0.1 + '@parcel/transformer-react-refresh-wrap': 2.0.1 + '@parcel/transformer-svg': 2.0.1 + transitivePeerDependencies: + - acorn + - bufferutil + - debug + - supports-color + - utf-8-validate + dev: true + + /@parcel/config-default/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-LavQo5+81wYARmDW+GsgPIV6GPG/rskR73oGHWV1oDr9k3UD2RYdGaH1GDcwqXyUEWVCw3K+nglaZdWFpOEdRQ==} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/bundler-default': 2.0.1_@parcel+core@2.0.1 + '@parcel/compressor-raw': 2.0.1_@parcel+core@2.0.1 + '@parcel/core': 2.0.1 + '@parcel/namer-default': 2.0.1_@parcel+core@2.0.1 + '@parcel/optimizer-cssnano': 2.0.1_@parcel+core@2.0.1 + '@parcel/optimizer-htmlnano': 2.0.1_@parcel+core@2.0.1 + '@parcel/optimizer-image': 2.0.1_@parcel+core@2.0.1 + '@parcel/optimizer-svgo': 2.0.1_@parcel+core@2.0.1 + '@parcel/optimizer-terser': 2.0.1_@parcel+core@2.0.1 + '@parcel/packager-css': 2.0.1_@parcel+core@2.0.1 + '@parcel/packager-html': 2.0.1_@parcel+core@2.0.1 + '@parcel/packager-js': 2.0.1_@parcel+core@2.0.1 + '@parcel/packager-raw': 2.0.1_@parcel+core@2.0.1 + '@parcel/packager-svg': 2.0.1_@parcel+core@2.0.1 + '@parcel/reporter-dev-server': 2.0.1_@parcel+core@2.0.1 + '@parcel/resolver-default': 2.0.1_@parcel+core@2.0.1 + '@parcel/runtime-browser-hmr': 2.0.1_@parcel+core@2.0.1 + '@parcel/runtime-js': 2.0.1_@parcel+core@2.0.1 + '@parcel/runtime-react-refresh': 2.0.1_@parcel+core@2.0.1 + '@parcel/runtime-service-worker': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-babel': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-css': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-html': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-image': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-js': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-json': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-postcss': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-posthtml': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-raw': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-react-refresh-wrap': 2.0.1_@parcel+core@2.0.1 + '@parcel/transformer-svg': 2.0.1_@parcel+core@2.0.1 + transitivePeerDependencies: + - acorn + - bufferutil + - debug + - supports-color + - utf-8-validate + dev: true + + /@parcel/config-webextension/2.0.1: + resolution: {integrity: sha512-atO9LmJcg/FFv6q7ZbVqi8DZ+K11qPDCGveJyyHlTzU9vs+inbZ7/Mb7unSTobP2UsgYl389+6zFnBhq6l+NKA==} + dependencies: + '@parcel/config-default': 2.0.1 + '@parcel/packager-raw-url': 2.0.1 + '@parcel/transformer-raw': 2.0.1 + '@parcel/transformer-webextension': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + - acorn + - bufferutil + - debug + - supports-color + - utf-8-validate + dev: true + + /@parcel/core/2.0.1: + resolution: {integrity: sha512-Iy5FgUAquc5HjQGiyKbWK0WaaVXerrzWD7cNBTIUOlk1xNeUtOeGu80Kc5xu0qT0/Mc+nsDfPhWcN8p4RVF+PQ==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/cache': 2.0.1_@parcel+core@2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/events': 2.0.1 + '@parcel/fs': 2.0.1_@parcel+core@2.0.1 + '@parcel/graph': 2.0.1 + '@parcel/hash': 2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/package-manager': 2.0.1_@parcel+core@2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + '@parcel/workers': 2.0.1_@parcel+core@2.0.1 + abortcontroller-polyfill: 1.7.3 + base-x: 3.0.9 + browserslist: 4.18.1 + clone: 2.1.2 + dotenv: 7.0.0 + dotenv-expand: 5.1.0 + json-source-map: 0.6.1 + json5: 1.0.1 + micromatch: 4.0.4 + nullthrows: 1.1.1 + semver: 5.7.1 + dev: true + + /@parcel/diagnostic/2.0.1: + resolution: {integrity: sha512-pC9GmEUUB2UQ9epvE/H2wn0rb6hyF68QlpxppHZ9fxib/RxqGWDG1I3axR0cxZifRRZiMNnbk7HfmUB19KNTtA==} + engines: {node: '>= 12.0.0'} + dependencies: + json-source-map: 0.6.1 + nullthrows: 1.1.1 + dev: true + + /@parcel/events/2.0.1: + resolution: {integrity: sha512-JRt5SkFS8/8r37o1DRKVtrWR1OZNN2pL548YsXVKBLN1b2ys36/+yKNObDuGB7DcOcIRngVs7xxv6+oodGyMlQ==} + engines: {node: '>= 12.0.0'} + dev: true + + /@parcel/fs-search/2.0.1: + resolution: {integrity: sha512-Zyo1PY4opLMunes5YZ2+Q0cMCgdAuepznVvUY+dK3WjW5OzO09G/L8cfNBhgeYA84wu0yyzNohZogvFjS10TZg==} + engines: {node: '>= 12.0.0'} + dependencies: + detect-libc: 1.0.3 + dev: true + + /@parcel/fs-write-stream-atomic/2.0.1: + resolution: {integrity: sha512-+CSeXRCnI9f9K4jeBOYzZiOf+qw6t3TvhEstR/zeXenzx0nBMzPv28mjUMZ33vRMy8bQOHAim8qy/AMSIMolEg==} + dependencies: + graceful-fs: 4.2.8 + iferr: 1.0.2 + imurmurhash: 0.1.4 + readable-stream: 2.3.7 + dev: true + + /@parcel/fs/2.0.1: + resolution: {integrity: sha512-zl8aV9Qp4lB4cQGyBfz3LQM+JkL7WHGoSlj8PjBamT8VmPlr57BUtp3Gc/IvRCCX8B7izNx3X8vCvr5BrziL+g==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/fs-search': 2.0.1 + '@parcel/fs-write-stream-atomic': 2.0.1 + '@parcel/types': 2.0.1 + '@parcel/utils': 2.0.1 + '@parcel/watcher': 2.0.3 + '@parcel/workers': 2.0.1 + graceful-fs: 4.2.8 + mkdirp: 0.5.5 + ncp: 2.0.0 + nullthrows: 1.1.1 + rimraf: 3.0.2 + utility-types: 3.10.0 + dev: true + + /@parcel/fs/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-zl8aV9Qp4lB4cQGyBfz3LQM+JkL7WHGoSlj8PjBamT8VmPlr57BUtp3Gc/IvRCCX8B7izNx3X8vCvr5BrziL+g==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/core': 2.0.1 + '@parcel/fs-search': 2.0.1 + '@parcel/fs-write-stream-atomic': 2.0.1 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + '@parcel/watcher': 2.0.3 + '@parcel/workers': 2.0.1_@parcel+core@2.0.1 + graceful-fs: 4.2.8 + mkdirp: 0.5.5 + ncp: 2.0.0 + nullthrows: 1.1.1 + rimraf: 3.0.2 + utility-types: 3.10.0 + dev: true + + /@parcel/graph/2.0.1: + resolution: {integrity: sha512-LESQVWy/Oln1CqTgWTjvm99btNSqHxOcIKEIL7k6Pq2d6vhO6oyAAmMe5sqf6Sr1nNCVjZW7oHRzyIG0kYTgWw==} + engines: {node: '>= 12.0.0'} + dependencies: + nullthrows: 1.1.1 + dev: true + + /@parcel/hash/2.0.1: + resolution: {integrity: sha512-Zng4i5HhcmOr6NMzQlnCf12ED9isL+HmcFC3XSLc6VYFcCnVg6cEIwJ7KrB/s5wRHLU2TfSZAaLIJlhcPKPPog==} + engines: {node: '>= 12.0.0'} + dependencies: + detect-libc: 1.0.3 + xxhash-wasm: 0.4.2 + dev: true + + /@parcel/logger/2.0.1: + resolution: {integrity: sha512-gN2mdDnUkbN11hUIDBU+zlREsgp7zm42ZAsc0xwIdmlnsZY7wu2G3lNtkXSMlIPJPdRi6oE6vmaArQJfXjaAOg==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/events': 2.0.1 + dev: true + + /@parcel/markdown-ansi/2.0.1: + resolution: {integrity: sha512-KFUvJoGncCwOml+RSyJl0KfQgle42YC8VJwQrHUqKMR5acyC3KaDNWAx96xkPf3k/hKv+VVEhIsH7SRJ63qwwQ==} + engines: {node: '>= 12.0.0'} + dependencies: + chalk: 4.1.2 + dev: true + + /@parcel/namer-default/2.0.1: + resolution: {integrity: sha512-wF948WojfksHutz023T2lC3b1BWRyOa9KaCh9caYtZ1Lq26kG3X2eaWVjOzw65SUQRLzAAxu3ujRhKEg0N0Ntw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/namer-default/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-wF948WojfksHutz023T2lC3b1BWRyOa9KaCh9caYtZ1Lq26kG3X2eaWVjOzw65SUQRLzAAxu3ujRhKEg0N0Ntw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/node-libs-browser/2.0.1: + resolution: {integrity: sha512-EK6hndQMtW0DJMU4FeDmbDwdIus/IAXz/YjR2kdQ0fLRAvcNWC/34R5bqlLmWdX2NXWVS+1tcDhPa2oEnUzzHA==} + engines: {node: '>= 12.0.0'} + dependencies: + assert: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + crypto-browserify: 3.12.0 + domain-browser: 3.5.0 + events: 3.3.0 + https-browserify: 1.0.0 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.0 + util: 0.12.4 + vm-browserify: 1.1.2 + dev: true + + /@parcel/node-resolver-core/2.0.1: + resolution: {integrity: sha512-bZqot9TZKuBpojo9i4LQ/mc+iKKuurcWDy481E/Z9Xp3zfDEZaNzj2f+0MSwv3pbqB134/PIMMtN92tewJ7Piw==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/node-libs-browser': 2.0.1 + '@parcel/utils': 2.0.1 + micromatch: 4.0.4 + nullthrows: 1.1.1 + dev: true + + /@parcel/optimizer-cssnano/2.0.1: + resolution: {integrity: sha512-yhuSUyTa4IKsFX+k2K8J6fsClpIWAu0Ng6HcW/fwDSfssZMm+Lfe33+sRo1fwqr8vd/okFrm3vOBQ+NhncsVVw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + cssnano: 5.0.11_postcss@8.4.4 + postcss: 8.4.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-cssnano/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-yhuSUyTa4IKsFX+k2K8J6fsClpIWAu0Ng6HcW/fwDSfssZMm+Lfe33+sRo1fwqr8vd/okFrm3vOBQ+NhncsVVw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + cssnano: 5.0.11_postcss@8.4.4 + postcss: 8.4.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-htmlnano/2.0.1: + resolution: {integrity: sha512-Q2YQt4YnRNGth6RtRw6Q/IanhboKhD2QfrDpUsDwcpBbP3nEirvLcOmVfzuNXDqvYaQG7720ulCRt8jWErZ2WQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + htmlnano: 1.1.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + - acorn + dev: true + + /@parcel/optimizer-htmlnano/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-Q2YQt4YnRNGth6RtRw6Q/IanhboKhD2QfrDpUsDwcpBbP3nEirvLcOmVfzuNXDqvYaQG7720ulCRt8jWErZ2WQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + htmlnano: 1.1.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + - acorn + dev: true + + /@parcel/optimizer-image/2.0.1: + resolution: {integrity: sha512-tXqrAoFoGT6R2nY88OMj6DxHctyewOA3RW6VFksolX+/eWjy9MsQMUWFJmc1TlsVJCu4xGVvcHM3+6Q3XF8VSA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + detect-libc: 1.0.3 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-image/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-tXqrAoFoGT6R2nY88OMj6DxHctyewOA3RW6VFksolX+/eWjy9MsQMUWFJmc1TlsVJCu4xGVvcHM3+6Q3XF8VSA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + detect-libc: 1.0.3 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-svgo/2.0.1: + resolution: {integrity: sha512-vdTXQrYjNd7s9ye8NYi7IrcS/oa1Rn1cI9pFeQCocEuL3eoesnFBtkeW0bbA7tNaIBkkR0x9NagRVtWgZJW4uQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-svgo/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-vdTXQrYjNd7s9ye8NYi7IrcS/oa1Rn1cI9pFeQCocEuL3eoesnFBtkeW0bbA7tNaIBkkR0x9NagRVtWgZJW4uQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/optimizer-terser/2.0.1: + resolution: {integrity: sha512-iT3gvkZsUKW4PJHRwWn4xqQlIIsrkr4gO2X5XQtPEXkYUn3UlHTE1lguJd1Pj6L3A0dS+ubI6wIfYk/Z59WAjw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + terser: 5.10.0 + transitivePeerDependencies: + - '@parcel/core' + - acorn + dev: true + + /@parcel/optimizer-terser/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-iT3gvkZsUKW4PJHRwWn4xqQlIIsrkr4gO2X5XQtPEXkYUn3UlHTE1lguJd1Pj6L3A0dS+ubI6wIfYk/Z59WAjw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + terser: 5.10.0 + transitivePeerDependencies: + - '@parcel/core' + - acorn + dev: true + + /@parcel/package-manager/2.0.1: + resolution: {integrity: sha512-I8pMP18zIAYIfwnFOhi4Pt+6grKysMxFqNTXAdfobszk4PvoOzbUIjzTk+3Z2IXT2FEdH/R/3Jej70OxpPf0CQ==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/fs': 2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/types': 2.0.1 + '@parcel/utils': 2.0.1 + '@parcel/workers': 2.0.1 + command-exists: 1.2.9 + cross-spawn: 6.0.5 + nullthrows: 1.1.1 + semver: 5.7.1 + split2: 3.2.2 + dev: true + + /@parcel/package-manager/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-I8pMP18zIAYIfwnFOhi4Pt+6grKysMxFqNTXAdfobszk4PvoOzbUIjzTk+3Z2IXT2FEdH/R/3Jej70OxpPf0CQ==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/core': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/fs': 2.0.1_@parcel+core@2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + '@parcel/workers': 2.0.1_@parcel+core@2.0.1 + command-exists: 1.2.9 + cross-spawn: 6.0.5 + nullthrows: 1.1.1 + semver: 5.7.1 + split2: 3.2.2 + dev: true + + /@parcel/packager-css/2.0.1: + resolution: {integrity: sha512-oPyouH+6+by3s68xxwYaaePPtrcRhNJ1Tia51eIVagBxp3kAOpB7F4S1Ou8w2qlipk9Wq6HJx2n1u4aZISbkAg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + postcss: 8.4.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-css/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-oPyouH+6+by3s68xxwYaaePPtrcRhNJ1Tia51eIVagBxp3kAOpB7F4S1Ou8w2qlipk9Wq6HJx2n1u4aZISbkAg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + postcss: 8.4.4 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-html/2.0.1: + resolution: {integrity: sha512-uGQYjspjz/VF4v+kVWAmPfXoGKCmos8rgTZ7XtXnhuRT4SH/OYHlRKVxzC4sb4zRoeO6Bj82yVw65Xj2gz9K4Q==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/types': 2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-html/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-uGQYjspjz/VF4v+kVWAmPfXoGKCmos8rgTZ7XtXnhuRT4SH/OYHlRKVxzC4sb4zRoeO6Bj82yVw65Xj2gz9K4Q==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-js/2.0.1: + resolution: {integrity: sha512-eN7BQITwTj2KeYMkW/9KRMBw1SoR7qlFhfX2+hbFA6Kl/b0bKEx33Gm21JJBl8wqqo3QVr9Rhg0JruwkQX1JHg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + globals: 13.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-js/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-eN7BQITwTj2KeYMkW/9KRMBw1SoR7qlFhfX2+hbFA6Kl/b0bKEx33Gm21JJBl8wqqo3QVr9Rhg0JruwkQX1JHg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + globals: 13.12.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-raw-url/2.0.1: + resolution: {integrity: sha512-m8ixkDjJ0RqY2U3Q+ASnphTG1n46jZ/CnRkvT8Kj5MyxtpDX69ri3/C1/b56r6daeemj0laM5lqkr9MrKsdacw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-raw/2.0.1: + resolution: {integrity: sha512-Cr9we+Pf9jl9AhKsZPKg7Da6xzNFxUqPDBRIZmO9GjTm1NZOeddmRPrtporPPZxtTmtQzRuyStRNKe5zBZtg3w==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-raw/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-Cr9we+Pf9jl9AhKsZPKg7Da6xzNFxUqPDBRIZmO9GjTm1NZOeddmRPrtporPPZxtTmtQzRuyStRNKe5zBZtg3w==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-svg/2.0.1: + resolution: {integrity: sha512-UqMYNxoaxLdJN+R3rOAACeMdkT/ONcMNQ+OzEowpt6lWZJyLRRF63akk2KhMVjYNQpV6y4wJZV6H/TWV6eRMjg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/types': 2.0.1 + '@parcel/utils': 2.0.1 + posthtml: 0.16.5 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/packager-svg/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-UqMYNxoaxLdJN+R3rOAACeMdkT/ONcMNQ+OzEowpt6lWZJyLRRF63akk2KhMVjYNQpV6y4wJZV6H/TWV6eRMjg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + posthtml: 0.16.5 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/plugin/2.0.1: + resolution: {integrity: sha512-zg9LdUk1fh8UApo9q79ZbG+QCwMioSlBP0+LKYLQqcNketzmjPuhe3rCialR0s2/6QsM1EQbuMUpCmZLSQZ4tA==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/types': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/plugin/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-zg9LdUk1fh8UApo9q79ZbG+QCwMioSlBP0+LKYLQqcNketzmjPuhe3rCialR0s2/6QsM1EQbuMUpCmZLSQZ4tA==} + engines: {node: '>= 12.0.0'} + dependencies: + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/reporter-cli/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-R4gmEhXH6vQMoSEobVolyCIJWBRV9z9Ju5y4gheUv7X0u3e2tpsHpDq835o8jqNIBG75Dm8Q5f3EE8BdhPzTEg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + chalk: 4.1.2 + filesize: 6.4.0 + nullthrows: 1.1.1 + ora: 5.4.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + term-size: 2.2.1 + wrap-ansi: 7.0.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/reporter-dev-server/2.0.1: + resolution: {integrity: sha512-dm2zgE8mPgLD5Nkmw9WQENZunrBN29fDRkNZhqnQyq4BBXF7e6Q/J/uamUjdtxAp7Qzobw1ZjybqlFuEh0z2tg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + connect: 3.7.0 + ejs: 3.1.6 + http-proxy-middleware: 1.3.1 + nullthrows: 1.1.1 + serve-handler: 6.1.3 + ws: 7.5.6 + transitivePeerDependencies: + - '@parcel/core' + - bufferutil + - debug + - utf-8-validate + dev: true + + /@parcel/reporter-dev-server/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-dm2zgE8mPgLD5Nkmw9WQENZunrBN29fDRkNZhqnQyq4BBXF7e6Q/J/uamUjdtxAp7Qzobw1ZjybqlFuEh0z2tg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + connect: 3.7.0 + ejs: 3.1.6 + http-proxy-middleware: 1.3.1 + nullthrows: 1.1.1 + serve-handler: 6.1.3 + ws: 7.5.6 + transitivePeerDependencies: + - '@parcel/core' + - bufferutil + - debug + - utf-8-validate + dev: true + + /@parcel/resolver-default/2.0.1: + resolution: {integrity: sha512-8+dMgb6pJGaepGAb+44ORLamFv8Ik7T1MyyexI3d9KfWXolU4lhSoFrNGeSEqm4VtPHH0xMYQo2cyIYKZSzuyA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/node-resolver-core': 2.0.1 + '@parcel/plugin': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/resolver-default/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-8+dMgb6pJGaepGAb+44ORLamFv8Ik7T1MyyexI3d9KfWXolU4lhSoFrNGeSEqm4VtPHH0xMYQo2cyIYKZSzuyA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/node-resolver-core': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-browser-hmr/2.0.1: + resolution: {integrity: sha512-fHuK3tzfJdDhCuNab7aB0RGrfyPlpmV7l0YJJ6Hvv2FiJ1EP2f0mMYF3/T6BXacL4/HLVo58K/XLYhTb6jU2cA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-browser-hmr/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-fHuK3tzfJdDhCuNab7aB0RGrfyPlpmV7l0YJJ6Hvv2FiJ1EP2f0mMYF3/T6BXacL4/HLVo58K/XLYhTb6jU2cA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-js/2.0.1: + resolution: {integrity: sha512-5syJTEWY4uw+GH8AYwL55fqRgcBjL/tb95FSYHfABKMHSkaU6KbeUzCv88oj2wE5szWHX793LuqjppO465XYvQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-js/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-5syJTEWY4uw+GH8AYwL55fqRgcBjL/tb95FSYHfABKMHSkaU6KbeUzCv88oj2wE5szWHX793LuqjppO465XYvQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-react-refresh/2.0.1: + resolution: {integrity: sha512-7j8cmIaoGP0etC2SLrWO1RdxQp+IealRAyZsLODRU22EQxCobGh5uq7Bjdv+m1wZrAdolR00lZe5p+dGrD2QGw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-react-refresh/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-7j8cmIaoGP0etC2SLrWO1RdxQp+IealRAyZsLODRU22EQxCobGh5uq7Bjdv+m1wZrAdolR00lZe5p+dGrD2QGw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-service-worker/2.0.1: + resolution: {integrity: sha512-B12lgz5LYLhhvjnTryg38R0PryAbq1+GCJE8Inidzr/IYLROUZANokPcUYUxwVB6QJVzYRhkx3lEf9VziAot2g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/runtime-service-worker/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-B12lgz5LYLhhvjnTryg38R0PryAbq1+GCJE8Inidzr/IYLROUZANokPcUYUxwVB6QJVzYRhkx3lEf9VziAot2g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/source-map/2.0.0: + resolution: {integrity: sha512-njoUJpj2646NebfHp5zKJeYD1KwhsfQIoU9TnCTHmF9fGOaPbClmeq12G6/4ZqGASftRq+YhhukFBi/ncWKGvw==} + engines: {node: ^12.18.3 || >=14} + dependencies: + detect-libc: 1.0.3 + globby: 11.0.4 + dev: true + + /@parcel/transformer-babel/2.0.1: + resolution: {integrity: sha512-TUCTdZi3V7z0WzyFPe3A1dQ0kLxPS8bEa0KgW7sueo9D0LXFvxpwh3Mf93q2H56KGb96o/QOXkz4HY8og+Wy4g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@babel/core': 7.16.0 + '@babel/generator': 7.16.0 + '@babel/helper-compilation-targets': 7.16.3_@babel+core@7.16.0 + '@babel/plugin-transform-flow-strip-types': 7.16.0_@babel+core@7.16.0 + '@babel/traverse': 7.16.3 + '@parcel/babel-ast-utils': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + browserslist: 4.18.1 + core-js: 3.19.1 + json5: 2.2.0 + nullthrows: 1.1.1 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + - supports-color + dev: true + + /@parcel/transformer-babel/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-TUCTdZi3V7z0WzyFPe3A1dQ0kLxPS8bEa0KgW7sueo9D0LXFvxpwh3Mf93q2H56KGb96o/QOXkz4HY8og+Wy4g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@babel/core': 7.16.0 + '@babel/generator': 7.16.0 + '@babel/helper-compilation-targets': 7.16.3_@babel+core@7.16.0 + '@babel/plugin-transform-flow-strip-types': 7.16.0_@babel+core@7.16.0 + '@babel/traverse': 7.16.3 + '@parcel/babel-ast-utils': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + browserslist: 4.18.1 + core-js: 3.19.1 + json5: 2.2.0 + nullthrows: 1.1.1 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + - supports-color + dev: true + + /@parcel/transformer-css/2.0.1: + resolution: {integrity: sha512-sSe8elt3ejTkmZmGk3ahhimGwVoxQL0hUYSjmsgK24a4kUoJWby2hvV8BEZWDZ8zJz5ZOWUw+34fM1frEn87dQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-css/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-sSe8elt3ejTkmZmGk3ahhimGwVoxQL0hUYSjmsgK24a4kUoJWby2hvV8BEZWDZ8zJz5ZOWUw+34fM1frEn87dQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-html/2.0.1: + resolution: {integrity: sha512-UkRtBHPnuedSX5UPzrZDzNb5pxWCVqvE5/xTPlxWEtN4een9Aixl4RSOZiJxMp4dxxVtw/fo9Lnx0z1wYxbWRw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-html/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-UkRtBHPnuedSX5UPzrZDzNb5pxWCVqvE5/xTPlxWEtN4een9Aixl4RSOZiJxMp4dxxVtw/fo9Lnx0z1wYxbWRw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-image/2.0.1: + resolution: {integrity: sha512-1xHPdE4W8jzsI0AWi4XWYioG2sDZvxJHprlTYNGK8GE+A2U7bOi7T2aoa44fEfK1pRa+N5GTkoNVTYiv4hza0g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/workers': 2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-image/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-1xHPdE4W8jzsI0AWi4XWYioG2sDZvxJHprlTYNGK8GE+A2U7bOi7T2aoa44fEfK1pRa+N5GTkoNVTYiv4hza0g==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/workers': 2.0.1_@parcel+core@2.0.1 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-js/2.0.1: + resolution: {integrity: sha512-c55qVfPU+jKoFFLV2GhME7CCqBO4Il34lW1EEv0RdYlBivPQQf+8vdcrrRX2FSjlI9cpvw9E4l298HyQDpVyng==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + '@swc/helpers': 0.2.14 + browserslist: 4.18.1 + detect-libc: 1.0.3 + micromatch: 4.0.4 + nullthrows: 1.1.1 + regenerator-runtime: 0.13.9 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-js/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-c55qVfPU+jKoFFLV2GhME7CCqBO4Il34lW1EEv0RdYlBivPQQf+8vdcrrRX2FSjlI9cpvw9E4l298HyQDpVyng==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/utils': 2.0.1 + '@swc/helpers': 0.2.14 + browserslist: 4.18.1 + detect-libc: 1.0.3 + micromatch: 4.0.4 + nullthrows: 1.1.1 + regenerator-runtime: 0.13.9 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-json/2.0.1: + resolution: {integrity: sha512-Nx22PQY5InJdqLKppC+Rq0zwH7mpE2MUvgdyhGBzbwB3qwo+us1uupj+3TGYtBQ8tsUypTZVQ1kWGyQkkGWqHg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + json5: 2.2.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-json/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-Nx22PQY5InJdqLKppC+Rq0zwH7mpE2MUvgdyhGBzbwB3qwo+us1uupj+3TGYtBQ8tsUypTZVQ1kWGyQkkGWqHg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + json5: 2.2.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-postcss/2.0.1: + resolution: {integrity: sha512-bSmOl1CxE5VD7FoNMz9G5ndh3vkYMJl84nbY2t91lUtGcY/ROJ1LKvZrglCCEEE13j9orFsPproQgCcYG7m1eA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + clone: 2.1.2 + css-modules-loader-core: 1.1.0 + nullthrows: 1.1.1 + postcss-modules: 3.2.2 + postcss-value-parser: 4.1.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-postcss/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-bSmOl1CxE5VD7FoNMz9G5ndh3vkYMJl84nbY2t91lUtGcY/ROJ1LKvZrglCCEEE13j9orFsPproQgCcYG7m1eA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + clone: 2.1.2 + css-modules-loader-core: 1.1.0 + nullthrows: 1.1.1 + postcss-modules: 3.2.2 + postcss-value-parser: 4.1.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-posthtml/2.0.1: + resolution: {integrity: sha512-UKGZO5vAZCxnTDF5fT8DzNrUdzahpCnFCrFOa0MFKi0DLKrVrxXmgIgLtoLS+mgwd3WuOW3Vx3KgyVovP5n2JQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-posthtml/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-UKGZO5vAZCxnTDF5fT8DzNrUdzahpCnFCrFOa0MFKi0DLKrVrxXmgIgLtoLS+mgwd3WuOW3Vx3KgyVovP5n2JQ==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-raw/2.0.1: + resolution: {integrity: sha512-NkwOp2lZX5bNxSj6tMNTEledWZvpIperCMOERm4raToDkdjBH1pDrxDLUBy8VzQ8M08CLz+2KJaF5wRMvj/eQw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-raw/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-NkwOp2lZX5bNxSj6tMNTEledWZvpIperCMOERm4raToDkdjBH1pDrxDLUBy8VzQ8M08CLz+2KJaF5wRMvj/eQw==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-react-refresh-wrap/2.0.1: + resolution: {integrity: sha512-zZj2Leh39ODh3C2xDh3eVvp1VnfVqeY5PrNdIcNfWw2DMBli13azcwYmF4Uim8natRqMFIsWsfKNesEY+mGLfA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-react-refresh-wrap/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-zZj2Leh39ODh3C2xDh3eVvp1VnfVqeY5PrNdIcNfWw2DMBli13azcwYmF4Uim8natRqMFIsWsfKNesEY+mGLfA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-svg/2.0.1: + resolution: {integrity: sha512-ZctnwpSomOZoh2FdfETLU4WnIr2t5P9W7QX5USATTlq62uD404Qsj1gr93wQgjLjzy9ID6T1Ua4iIdYNSkScNA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-svg/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-ZctnwpSomOZoh2FdfETLU4WnIr2t5P9W7QX5USATTlq62uD404Qsj1gr93wQgjLjzy9ID6T1Ua4iIdYNSkScNA==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/hash': 2.0.1 + '@parcel/plugin': 2.0.1_@parcel+core@2.0.1 + nullthrows: 1.1.1 + posthtml: 0.16.5 + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + semver: 5.7.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-typescript-tsc/2.0.1_typescript@4.5.2: + resolution: {integrity: sha512-bAlYYFhQFJWx/ij2CjHyzQbz5mcVI6HQRLgpCrZcL7C+z4VozBEjlo313akImU9rIKKCqA5Y3NjduWTySFYfLg==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + peerDependencies: + typescript: '>=3.0.0' + dependencies: + '@parcel/plugin': 2.0.1 + '@parcel/ts-utils': 2.0.1_typescript@4.5.2 + typescript: 4.5.2 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/transformer-webextension/2.0.1: + resolution: {integrity: sha512-R0ZyvcZTVa9AVkNqvqIRJNF9fpr1Bov6DahOpBnW7gSj6jG8r8/A6Z27gbs7Y2/bHbtQ0RP7dipIAgoVnaU9sA==} + engines: {parcel: ^2.0.1} + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/plugin': 2.0.1 + '@parcel/utils': 2.0.1 + content-security-policy-parser: 0.3.0 + json-source-map: 0.6.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/ts-utils/2.0.1_typescript@4.5.2: + resolution: {integrity: sha512-Zu3p+1q3YxSZTuAa26KpQOGmdjNfrbBgyTpncg9+Q+EIGtkhFHWiEpx/fok9qyqFxiTfe1jAUKzTUeqPjt+F0Q==} + engines: {node: '>= 12.0.0'} + peerDependencies: + typescript: '>=3.0.0' + dependencies: + nullthrows: 1.1.1 + typescript: 4.5.2 + dev: true + + /@parcel/types/2.0.1: + resolution: {integrity: sha512-em8/GgC7uzkUyEA2ogkzeVDmjaKYQhjf/4EIiC7jXWr22NlSXRQOawhc0CB2o97J9EV2rVXVkWTj0drHTpN2Bw==} + dependencies: + '@parcel/cache': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/fs': 2.0.1 + '@parcel/package-manager': 2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/workers': 2.0.1 + utility-types: 3.10.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/types/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-em8/GgC7uzkUyEA2ogkzeVDmjaKYQhjf/4EIiC7jXWr22NlSXRQOawhc0CB2o97J9EV2rVXVkWTj0drHTpN2Bw==} + dependencies: + '@parcel/cache': 2.0.1_@parcel+core@2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/fs': 2.0.1_@parcel+core@2.0.1 + '@parcel/package-manager': 2.0.1_@parcel+core@2.0.1 + '@parcel/source-map': 2.0.0 + '@parcel/workers': 2.0.1_@parcel+core@2.0.1 + utility-types: 3.10.0 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /@parcel/utils/2.0.1: + resolution: {integrity: sha512-+XD+LYDq+VKAUfRPzcsOjq9LefeX6tiQ2zH2uCWYAwA+s+sTHIrvWkKoF3QfFOQpPgj2QqnAZMOS6F/xY2phPg==} + engines: {node: '>= 12.0.0'} + dependencies: + '@iarna/toml': 2.2.5 + '@parcel/codeframe': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/hash': 2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/markdown-ansi': 2.0.1 + '@parcel/source-map': 2.0.0 + ansi-html-community: 0.0.8 + chalk: 4.1.2 + clone: 2.1.2 + fast-glob: 3.1.1 + fastest-levenshtein: 1.0.12 + is-glob: 4.0.3 + is-url: 1.2.4 + json5: 1.0.1 + lru-cache: 6.0.0 + micromatch: 4.0.4 + node-forge: 0.10.0 + nullthrows: 1.1.1 + open: 7.4.2 + terminal-link: 2.1.1 + dev: true + + /@parcel/watcher/2.0.3: + resolution: {integrity: sha512-PHh5PArr3nYGYVj9z/NSfDmmKEBNrg2bzoFgxzjTRBBxPUKx039x3HF6VGLFIfrghjJxcYn/IeSpdVwfob7KFA==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + node-addon-api: 3.2.1 + node-gyp-build: 4.3.0 + dev: true + + /@parcel/workers/2.0.1: + resolution: {integrity: sha512-nBBK5QeoWM0l8khyStDiEd432UXaF6mkUa8n2D4Ee6XOFgUCiXWV7VROqA4nhf6OJr5K+trtNaNVGq9oHRuPHw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/diagnostic': 2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/types': 2.0.1 + '@parcel/utils': 2.0.1 + chrome-trace-event: 1.0.3 + nullthrows: 1.1.1 + dev: true + + /@parcel/workers/2.0.1_@parcel+core@2.0.1: + resolution: {integrity: sha512-nBBK5QeoWM0l8khyStDiEd432UXaF6mkUa8n2D4Ee6XOFgUCiXWV7VROqA4nhf6OJr5K+trtNaNVGq9oHRuPHw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.0.0 + dependencies: + '@parcel/core': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/types': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + chrome-trace-event: 1.0.3 + nullthrows: 1.1.1 + dev: true + + /@popperjs/core/2.10.2: + resolution: {integrity: sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==} + dev: false + + /@swc/helpers/0.2.14: + resolution: {integrity: sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA==} + dev: true + + /@trysound/sax/0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: true + + /@types/dompurify/2.3.1: + resolution: {integrity: sha512-YJth9qa0V/E6/XPH1Jq4BC8uCMmO8V1fKWn8PCvuZcAhMn7q0ez9LW6naQT04UZzjFfAPhyRMZmI2a2rbMlEFA==} + dependencies: + '@types/trusted-types': 2.0.2 + dev: true + + /@types/http-proxy/1.17.7: + resolution: {integrity: sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==} + dependencies: + '@types/node': 16.11.10 + dev: true + + /@types/json-schema/7.0.9: + resolution: {integrity: sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==} + dev: true + + /@types/json5/0.0.29: + resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=} + dev: true + + /@types/node/16.11.10: + resolution: {integrity: sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==} + dev: true + + /@types/parse-json/4.0.0: + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + + /@types/prop-types/15.7.4: + resolution: {integrity: sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==} + + /@types/react-dom/17.0.11: + resolution: {integrity: sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==} + dependencies: + '@types/react': 17.0.37 + dev: true + + /@types/react-is/17.0.3: + resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} + dependencies: + '@types/react': 17.0.37 + dev: false + + /@types/react-transition-group/4.4.4: + resolution: {integrity: sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==} + dependencies: + '@types/react': 17.0.37 + dev: false + + /@types/react/17.0.37: + resolution: {integrity: sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg==} + dependencies: + '@types/prop-types': 15.7.4 + '@types/scheduler': 0.16.2 + csstype: 3.0.10 + + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + + /@types/trusted-types/2.0.2: + resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} + dev: true + + /@types/webextension-polyfill/0.8.2: + resolution: {integrity: sha512-Pd+p5AQx6s78jr4gDC2p3vbw+uxP2DLLqE9iAJJpZ+OUqDm3txr6uN2wUBEwjVgZTNdBHPsxawGvPCTipF2s6w==} + dev: true + + /@typescript-eslint/eslint-plugin/5.4.0_e70a9477f5985665eda5342254cbd97d: + resolution: {integrity: sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/experimental-utils': 5.4.0_eslint@7.32.0 + '@typescript-eslint/parser': 5.4.0_eslint@7.32.0 + '@typescript-eslint/scope-manager': 5.4.0 + debug: 4.3.2 + eslint: 7.32.0 + functional-red-black-tree: 1.0.1 + ignore: 5.1.9 + regexpp: 3.2.0 + semver: 7.3.5 + tsutils: 3.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/experimental-utils/5.4.0_eslint@7.32.0: + resolution: {integrity: sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + dependencies: + '@types/json-schema': 7.0.9 + '@typescript-eslint/scope-manager': 5.4.0 + '@typescript-eslint/types': 5.4.0 + '@typescript-eslint/typescript-estree': 5.4.0 + eslint: 7.32.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@7.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/parser/5.4.0_eslint@7.32.0: + resolution: {integrity: sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.4.0 + '@typescript-eslint/types': 5.4.0 + '@typescript-eslint/typescript-estree': 5.4.0 + debug: 4.3.2 + eslint: 7.32.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.4.0: + resolution: {integrity: sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.4.0 + '@typescript-eslint/visitor-keys': 5.4.0 + dev: true + + /@typescript-eslint/types/5.4.0: + resolution: {integrity: sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.4.0: + resolution: {integrity: sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.4.0 + '@typescript-eslint/visitor-keys': 5.4.0 + debug: 4.3.2 + globby: 11.0.4 + is-glob: 4.0.3 + semver: 7.3.5 + tsutils: 3.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/visitor-keys/5.4.0: + resolution: {integrity: sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.4.0 + eslint-visitor-keys: 3.1.0 + dev: true + + /@virtuoso.dev/react-urx/0.2.10_react@17.0.2: + resolution: {integrity: sha512-ihs17eovHF9ZZM3OoKm4anKp7XLcyLaz6+iNzchimCWBTNBvA/MF4wS8MtqYZlELPj+s4+1nzgNCWGW5dL+fxw==} + peerDependencies: + react: '>=16' + dependencies: + '@virtuoso.dev/urx': 0.2.10 + react: 17.0.2 + dev: false + + /@virtuoso.dev/urx/0.2.10: + resolution: {integrity: sha512-Eop6vdPnVomXi+uFF5Jz/I2/MTo3dZMm+XttV5yXNAKX0dYys2d3RctzJmONtOxc7kAaxnaKB8QYnMFN/mFITA==} + dev: false + + /abab/2.0.5: + resolution: {integrity: sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==} + dev: true + + /abortcontroller-polyfill/1.7.3: + resolution: {integrity: sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==} + dev: true + + /acorn-globals/4.3.4: + resolution: {integrity: sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==} + dependencies: + acorn: 6.4.2 + acorn-walk: 6.2.0 + dev: true + + /acorn-jsx/5.3.2_acorn@7.4.1: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 7.4.1 + dev: true + + /acorn-walk/6.2.0: + resolution: {integrity: sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/6.4.2: + resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn/7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /aggregate-error/3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv/8.8.2: + resolution: {integrity: sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /alphanum-sort/1.0.2: + resolution: {integrity: sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=} + dev: true + + /ansi-colors/4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + dev: true + + /ansi-escapes/4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-html-community/0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + dev: true + + /ansi-regex/2.1.1: + resolution: {integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8=} + engines: {node: '>=0.10.0'} + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex/6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles/2.2.1: + resolution: {integrity: sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=} + engines: {node: '>=0.10.0'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles/6.1.0: + resolution: {integrity: sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==} + engines: {node: '>=12'} + dev: true + + /argparse/1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /array-equal/1.0.0: + resolution: {integrity: sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=} + dev: true + + /array-includes/3.1.4: + resolution: {integrity: sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + get-intrinsic: 1.1.1 + is-string: 1.0.7 + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat/1.2.5: + resolution: {integrity: sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + dev: true + + /array.prototype.flatmap/1.2.5: + resolution: {integrity: sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + dev: true + + /asn1.js/5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + dev: true + + /asn1/0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /assert-plus/1.0.0: + resolution: {integrity: sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=} + engines: {node: '>=0.8'} + dev: true + + /assert/2.0.0: + resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==} + dependencies: + es6-object-assign: 1.1.0 + is-nan: 1.3.2 + object-is: 1.1.5 + util: 0.12.4 + dev: true + + /astral-regex/2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /astring/1.8.1: + resolution: {integrity: sha512-Aj3mbwVzj7Vve4I/v2JYOPFkCGM2YS7OqQTNSxmUR+LECRpokuPgAYghePgr6SALDo5bD5DlfbSaYjOzGJZOLQ==} + hasBin: true + dev: true + + /async-limiter/1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + dev: true + + /async/0.9.2: + resolution: {integrity: sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=} + dev: true + + /asynckit/0.4.0: + resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=} + dev: true + + /available-typed-arrays/1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /aws-sign2/0.7.0: + resolution: {integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=} + dev: true + + /aws4/1.11.0: + resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==} + dev: true + + /babel-plugin-macros/2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + dependencies: + '@babel/runtime': 7.16.3 + cosmiconfig: 6.0.0 + resolve: 1.20.0 + dev: false + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /base-x/3.0.9: + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /base64-js/1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /bcrypt-pbkdf/1.0.2: + resolution: {integrity: sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=} + dependencies: + tweetnacl: 0.14.5 + dev: true + + /big-integer/1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + dev: false + + /big.js/5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + dev: true + + /bl/4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: true + + /bn.js/4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + dev: true + + /bn.js/5.2.0: + resolution: {integrity: sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==} + dev: true + + /boolbase/1.0.0: + resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=} + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /broadcast-channel/3.7.0: + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} + dependencies: + '@babel/runtime': 7.16.3 + 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 + dev: false + + /brorand/1.1.0: + resolution: {integrity: sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=} + dev: true + + /browser-process-hrtime/1.0.0: + resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + dev: true + + /browserify-aes/1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /browserify-cipher/1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + dev: true + + /browserify-des/1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + dependencies: + cipher-base: 1.0.4 + des.js: 1.0.1 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /browserify-rsa/4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + dependencies: + bn.js: 5.2.0 + randombytes: 2.1.0 + dev: true + + /browserify-sign/4.2.1: + resolution: {integrity: sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==} + dependencies: + bn.js: 5.2.0 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.5.4 + inherits: 2.0.4 + parse-asn1: 5.1.6 + readable-stream: 3.6.0 + safe-buffer: 5.2.1 + dev: true + + /browserify-zlib/0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + dependencies: + pako: 1.0.11 + dev: true + + /browserslist/4.18.1: + resolution: {integrity: sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001283 + electron-to-chromium: 1.4.3 + escalade: 3.1.1 + node-releases: 2.0.1 + picocolors: 1.0.0 + dev: true + + /buffer-from/1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer-xor/1.0.3: + resolution: {integrity: sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=} + dev: true + + /buffer/5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /builtin-status-codes/3.0.0: + resolution: {integrity: sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=} + dev: true + + /bytes/3.0.0: + resolution: {integrity: sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=} + engines: {node: '>= 0.8'} + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.1 + dev: true + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + /caniuse-api/3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + dependencies: + browserslist: 4.18.1 + caniuse-lite: 1.0.30001283 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + dev: true + + /caniuse-lite/1.0.30001283: + resolution: {integrity: sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==} + dev: true + + /caseless/0.12.0: + resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=} + dev: true + + /chalk/1.1.3: + resolution: {integrity: sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=} + engines: {node: '>=0.10.0'} + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chrome-trace-event/1.0.3: + resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + engines: {node: '>=6.0'} + dev: true + + /cipher-base/1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /clean-stack/2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + + /cli-cursor/3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners/2.6.1: + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} + dev: true + + /cli-truncate/2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + + /cli-truncate/3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + slice-ansi: 5.0.0 + string-width: 5.0.1 + dev: true + + /clone/1.0.4: + resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=} + engines: {node: '>=0.8'} + dev: true + + /clone/2.1.2: + resolution: {integrity: sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=} + engines: {node: '>=0.8'} + dev: true + + /clsx/1.1.1: + resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==} + engines: {node: '>=6'} + dev: false + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=} + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /colord/2.9.1: + resolution: {integrity: sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==} + dev: true + + /colorette/2.0.16: + resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==} + dev: true + + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /command-exists/1.2.9: + resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} + dev: true + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander/6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + dev: true + + /commander/7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + + /commander/8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + + /connect/3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + dev: true + + /console-browserify/1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + dev: true + + /constants-browserify/1.0.0: + resolution: {integrity: sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=} + dev: true + + /content-disposition/0.5.2: + resolution: {integrity: sha1-DPaLud318r55YcOoUXjLhdunjLQ=} + engines: {node: '>= 0.6'} + dev: true + + /content-security-policy-parser/0.3.0: + resolution: {integrity: sha512-ub90B4t9EfDPv3DCH7vEwGe4tVMkSm4Ow1HsmvmEQwinDfpTEDmkuJVa5WpzHDTt2bUirNRZuzL6S0msASlJhg==} + engines: {node: '>=8.0.0'} + dev: true + + /convert-source-map/1.8.0: + resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} + dependencies: + safe-buffer: 5.1.2 + + /core-js/3.19.1: + resolution: {integrity: sha512-Tnc7E9iKd/b/ff7GFbhwPVzJzPztGrChB8X8GLqoYGdEOG8IpLnK1xPyo3ZoO3HsK6TodJS58VGPOxA+hLHQMg==} + requiresBuild: true + dev: true + + /core-util-is/1.0.2: + resolution: {integrity: sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=} + dev: true + + /core-util-is/1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /cosmiconfig/6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: false + + /cosmiconfig/7.0.1: + resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /create-ecdh/4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + dependencies: + bn.js: 4.12.0 + elliptic: 6.5.4 + dev: true + + /create-hash/1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: true + + /create-hmac/1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: true + + /cross-spawn/6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.1 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /crypto-browserify/3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.1 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + inherits: 2.0.4 + pbkdf2: 3.1.2 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + dev: true + + /css-declaration-sorter/6.1.3_postcss@8.4.4: + resolution: {integrity: sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==} + engines: {node: '>= 10'} + peerDependencies: + postcss: ^8.0.9 + dependencies: + postcss: 8.4.4 + timsort: 0.3.0 + dev: true + + /css-modules-loader-core/1.1.0: + resolution: {integrity: sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=} + dependencies: + icss-replace-symbols: 1.1.0 + postcss: 6.0.1 + postcss-modules-extract-imports: 1.1.0 + postcss-modules-local-by-default: 1.2.0 + postcss-modules-scope: 1.1.0 + postcss-modules-values: 1.3.0 + dev: true + + /css-select/4.1.3: + resolution: {integrity: sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==} + dependencies: + boolbase: 1.0.0 + css-what: 5.1.0 + domhandler: 4.2.2 + domutils: 2.8.0 + nth-check: 2.0.1 + + /css-selector-tokenizer/0.7.3: + resolution: {integrity: sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==} + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + dev: true + + /css-tree/1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + dev: true + + /css-what/5.1.0: + resolution: {integrity: sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==} + engines: {node: '>= 6'} + + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /cssnano-preset-default/5.1.7_postcss@8.4.4: + resolution: {integrity: sha512-bWDjtTY+BOqrqBtsSQIbN0RLGD2Yr2CnecpP0ydHNafh9ZUEre8c8VYTaH9FEbyOt0eIfEUAYYk5zj92ioO8LA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + css-declaration-sorter: 6.1.3_postcss@8.4.4 + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-calc: 8.0.0_postcss@8.4.4 + postcss-colormin: 5.2.1_postcss@8.4.4 + postcss-convert-values: 5.0.2_postcss@8.4.4 + postcss-discard-comments: 5.0.1_postcss@8.4.4 + postcss-discard-duplicates: 5.0.1_postcss@8.4.4 + postcss-discard-empty: 5.0.1_postcss@8.4.4 + postcss-discard-overridden: 5.0.1_postcss@8.4.4 + postcss-merge-longhand: 5.0.4_postcss@8.4.4 + postcss-merge-rules: 5.0.3_postcss@8.4.4 + postcss-minify-font-values: 5.0.1_postcss@8.4.4 + postcss-minify-gradients: 5.0.3_postcss@8.4.4 + postcss-minify-params: 5.0.2_postcss@8.4.4 + postcss-minify-selectors: 5.1.0_postcss@8.4.4 + postcss-normalize-charset: 5.0.1_postcss@8.4.4 + postcss-normalize-display-values: 5.0.1_postcss@8.4.4 + postcss-normalize-positions: 5.0.1_postcss@8.4.4 + postcss-normalize-repeat-style: 5.0.1_postcss@8.4.4 + postcss-normalize-string: 5.0.1_postcss@8.4.4 + postcss-normalize-timing-functions: 5.0.1_postcss@8.4.4 + postcss-normalize-unicode: 5.0.1_postcss@8.4.4 + postcss-normalize-url: 5.0.3_postcss@8.4.4 + postcss-normalize-whitespace: 5.0.1_postcss@8.4.4 + postcss-ordered-values: 5.0.2_postcss@8.4.4 + postcss-reduce-initial: 5.0.1_postcss@8.4.4 + postcss-reduce-transforms: 5.0.1_postcss@8.4.4 + postcss-svgo: 5.0.3_postcss@8.4.4 + postcss-unique-selectors: 5.0.2_postcss@8.4.4 + dev: true + + /cssnano-utils/2.0.1_postcss@8.4.4: + resolution: {integrity: sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + dev: true + + /cssnano/5.0.11_postcss@8.4.4: + resolution: {integrity: sha512-5SHM31NAAe29jvy0MJqK40zZ/8dGlnlzcfHKw00bWMVFp8LWqtuyPSFwbaoIoxvt71KWJOfg8HMRGrBR3PExCg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-preset-default: 5.1.7_postcss@8.4.4 + is-resolvable: 1.1.0 + lilconfig: 2.0.4 + postcss: 8.4.4 + yaml: 1.10.2 + dev: true + + /csso/4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + dependencies: + css-tree: 1.1.3 + dev: true + + /cssom/0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + dev: true + + /cssstyle/1.4.0: + resolution: {integrity: sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==} + dependencies: + cssom: 0.3.8 + dev: true + + /csstype/3.0.10: + resolution: {integrity: sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==} + + /dashdash/1.14.1: + resolution: {integrity: sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=} + engines: {node: '>=0.10'} + dependencies: + assert-plus: 1.0.0 + dev: true + + /data-urls/1.1.0: + resolution: {integrity: sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==} + dependencies: + abab: 2.0.5 + whatwg-mimetype: 2.3.0 + whatwg-url: 7.1.0 + dev: true + + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + dependencies: + ms: 2.0.0 + dev: true + + /debug/3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + dependencies: + ms: 2.1.3 + dev: true + + /debug/4.3.2: + resolution: {integrity: sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /debug/4.3.2_supports-color@9.2.1: + resolution: {integrity: sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + supports-color: 9.2.1 + dev: true + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /defaults/1.0.3: + resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=} + dependencies: + clone: 1.0.4 + dev: true + + /define-properties/1.1.3: + resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} + engines: {node: '>= 0.4'} + dependencies: + object-keys: 1.1.1 + dev: true + + /delayed-stream/1.0.0: + resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=} + engines: {node: '>=0.4.0'} + dev: true + + /des.js/1.0.1: + resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: true + + /detect-libc/1.0.3: + resolution: {integrity: sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /detect-node/2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + dev: false + + /diffie-hellman/5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + dependencies: + bn.js: 4.12.0 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine/2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-helpers/5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dependencies: + '@babel/runtime': 7.16.3 + csstype: 3.0.10 + dev: false + + /dom-serializer/1.3.2: + resolution: {integrity: sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==} + dependencies: + domelementtype: 2.2.0 + domhandler: 4.2.2 + entities: 2.2.0 + + /domain-browser/3.5.0: + resolution: {integrity: sha512-zrzUu6auyZWRexjCEPJnfWc30Hupxh2lJZOJAF3qa2bCuD4O/55t0FvQt3ZMhEw++gjNkwdkOVZh8yA32w/Vfw==} + engines: {node: '>=8'} + dev: true + + /domelementtype/2.2.0: + resolution: {integrity: sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==} + + /domexception/1.0.1: + resolution: {integrity: sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==} + dependencies: + webidl-conversions: 4.0.2 + dev: true + + /domhandler/4.2.2: + resolution: {integrity: sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.2.0 + + /dompurify/2.3.3: + resolution: {integrity: sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==} + dev: false + + /domutils/2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.3.2 + domelementtype: 2.2.0 + domhandler: 4.2.2 + + /dotenv-expand/5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dev: true + + /dotenv/7.0.0: + resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} + engines: {node: '>=6'} + dev: true + + /ecc-jsbn/0.1.2: + resolution: {integrity: sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=} + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + dev: true + + /ee-first/1.1.1: + resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} + dev: true + + /ejs/3.1.6: + resolution: {integrity: sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.2 + dev: true + + /electron-to-chromium/1.4.3: + resolution: {integrity: sha512-hfpppjYhqIZB8jrNb0rNceQRkSnBN7QJl3W26O1jUv3F3BkQknqy1YTqVXkFnIcFtBc3Qnv5M7r5Lez2iOLgZA==} + dev: true + + /elliptic/6.5.4: + resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: true + + /emoji-regex/8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex/9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /emojis-list/3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + dev: true + + /emphasize/4.2.0: + resolution: {integrity: sha512-yGKvcFUHlBsUPwlxTlzKLR8+zhpbitkFOMCUxN8fTJng9bdH3WNzUGkhdaGdjndSUgqmMPBN7umfwnUdLz5Axg==} + dependencies: + chalk: 4.1.2 + highlight.js: 10.4.1 + lowlight: 1.17.0 + dev: true + + /encodeurl/1.0.2: + resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} + engines: {node: '>= 0.8'} + dev: true + + /enquirer/2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + dependencies: + ansi-colors: 4.1.1 + dev: true + + /entities/2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + /entities/3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + + /error-ex/1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + + /es-abstract/1.19.1: + resolution: {integrity: sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + get-intrinsic: 1.1.1 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-symbols: 1.0.2 + internal-slot: 1.0.3 + is-callable: 1.2.4 + is-negative-zero: 2.0.1 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.1 + is-string: 1.0.7 + is-weakref: 1.0.1 + object-inspect: 1.11.0 + object-keys: 1.1.1 + object.assign: 4.1.2 + string.prototype.trimend: 1.0.4 + string.prototype.trimstart: 1.0.4 + unbox-primitive: 1.0.1 + dev: true + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.4 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /es6-object-assign/1.1.0: + resolution: {integrity: sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=} + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-html/1.0.3: + resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=} + dev: true + + /escape-string-regexp/1.0.5: + resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} + engines: {node: '>=0.8.0'} + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + /escodegen/1.14.3: + resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} + engines: {node: '>=4.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + + /eslint-config-prettier/8.3.0_eslint@7.32.0: + resolution: {integrity: sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 7.32.0 + dev: true + + /eslint-config-standard/16.0.3_4756bc88b0f26b24b04bf0a729e3a9a6: + resolution: {integrity: sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==} + peerDependencies: + eslint: ^7.12.1 + eslint-plugin-import: ^2.22.1 + eslint-plugin-node: ^11.1.0 + eslint-plugin-promise: ^4.2.1 || ^5.0.0 + dependencies: + eslint: 7.32.0 + eslint-plugin-import: 2.25.3_eslint@7.32.0 + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 4.3.1 + dev: true + + /eslint-import-resolver-node/0.3.6: + resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} + dependencies: + debug: 3.2.7 + resolve: 1.20.0 + dev: true + + /eslint-module-utils/2.7.1: + resolution: {integrity: sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==} + engines: {node: '>=4'} + dependencies: + debug: 3.2.7 + find-up: 2.1.0 + pkg-dir: 2.0.0 + dev: true + + /eslint-plugin-es/3.0.1_eslint@7.32.0: + resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + eslint: 7.32.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + dev: true + + /eslint-plugin-import/2.25.3_eslint@7.32.0: + resolution: {integrity: sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + dependencies: + array-includes: 3.1.4 + array.prototype.flat: 1.2.5 + debug: 2.6.9 + doctrine: 2.1.0 + eslint: 7.32.0 + eslint-import-resolver-node: 0.3.6 + eslint-module-utils: 2.7.1 + has: 1.0.3 + is-core-module: 2.8.0 + is-glob: 4.0.3 + minimatch: 3.0.4 + object.values: 1.1.5 + resolve: 1.20.0 + tsconfig-paths: 3.12.0 + dev: true + + /eslint-plugin-node/11.1.0_eslint@7.32.0: + resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=5.16.0' + dependencies: + eslint: 7.32.0 + eslint-plugin-es: 3.0.1_eslint@7.32.0 + eslint-utils: 2.1.0 + ignore: 5.1.9 + minimatch: 3.0.4 + resolve: 1.20.0 + semver: 6.3.0 + dev: true + + /eslint-plugin-promise/4.3.1: + resolution: {integrity: sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==} + engines: {node: '>=6'} + dev: true + + /eslint-plugin-react/7.27.1_eslint@7.32.0: + resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.4 + array.prototype.flatmap: 1.2.5 + doctrine: 2.1.0 + eslint: 7.32.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.2.1 + minimatch: 3.0.4 + object.entries: 1.1.5 + object.fromentries: 2.0.5 + object.hasown: 1.1.0 + object.values: 1.1.5 + prop-types: 15.7.2 + resolve: 2.0.0-next.3 + semver: 6.3.0 + string.prototype.matchall: 4.0.6 + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-utils/2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@7.32.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 7.32.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.1.0: + resolution: {integrity: sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/7.32.0: + resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} + engines: {node: ^10.12.0 || >=12.0.0} + hasBin: true + dependencies: + '@babel/code-frame': 7.12.11 + '@eslint/eslintrc': 0.4.3 + '@humanwhocodes/config-array': 0.5.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.2 + doctrine: 3.0.0 + enquirer: 2.3.6 + escape-string-regexp: 4.0.0 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 2.1.0 + espree: 7.3.1 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 13.12.0 + ignore: 4.0.6 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.0.4 + natural-compare: 1.4.0 + optionator: 0.9.1 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.3.5 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 6.7.3 + text-table: 0.2.0 + v8-compile-cache: 2.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree/7.3.1: + resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + acorn: 7.4.1 + acorn-jsx: 5.3.2_acorn@7.4.1 + eslint-visitor-keys: 1.3.0 + dev: true + + /esprima/4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery/1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /eventemitter3/4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: true + + /events/3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + + /evp_bytestokey/1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + dev: true + + /execa/5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.6 + strip-final-newline: 2.0.0 + dev: true + + /extend/3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: true + + /extsprintf/1.3.0: + resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=} + engines: {'0': node >=0.6.0} + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + /fast-glob/3.1.1: + resolution: {integrity: sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==} + engines: {node: '>=8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.4 + dev: true + + /fast-glob/3.2.7: + resolution: {integrity: sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==} + engines: {node: '>=8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.4 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} + dev: true + + /fast-url-parser/1.1.3: + resolution: {integrity: sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=} + dependencies: + punycode: 1.4.1 + dev: true + + /fastest-levenshtein/1.0.12: + resolution: {integrity: sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==} + dev: true + + /fastparse/1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + dev: true + + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fault/1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + dependencies: + format: 0.2.2 + dev: true + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /filelist/1.0.2: + resolution: {integrity: sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==} + dependencies: + minimatch: 3.0.4 + dev: true + + /filesize/6.4.0: + resolution: {integrity: sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ==} + engines: {node: '>= 0.4.0'} + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /finalhandler/1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + dev: true + + /find-root/1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + dev: false + + /find-up/2.1.0: + resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=} + engines: {node: '>=4'} + dependencies: + locate-path: 2.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.4 + rimraf: 3.0.2 + dev: true + + /flatted/3.2.4: + resolution: {integrity: sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==} + dev: true + + /follow-redirects/1.14.5: + resolution: {integrity: sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: true + + /foreach/2.0.5: + resolution: {integrity: sha1-C+4AUBiusmDQo6865ljdATbsG5k=} + dev: true + + /forever-agent/0.6.1: + resolution: {integrity: sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=} + dev: true + + /form-data/2.3.3: + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.34 + dev: true + + /format/0.2.2: + resolution: {integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=} + engines: {node: '>=0.4.x'} + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /functional-red-black-tree/1.0.1: + resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=} + dev: true + + /generic-names/2.0.1: + resolution: {integrity: sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==} + dependencies: + loader-utils: 1.4.0 + dev: true + + /gensync/1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-intrinsic/1.1.1: + resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.2 + dev: true + + /get-port/4.2.0: + resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==} + engines: {node: '>=6'} + dev: true + + /get-stream/6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-symbol-description/1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + dev: true + + /getpass/0.1.7: + resolution: {integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=} + dependencies: + assert-plus: 1.0.0 + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.0.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + + /globals/11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals/13.12.0: + resolution: {integrity: sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby/11.0.4: + resolution: {integrity: sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.7 + ignore: 5.1.9 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /graceful-fs/4.2.8: + resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==} + dev: true + + /har-schema/2.0.0: + resolution: {integrity: sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=} + engines: {node: '>=4'} + dev: true + + /har-validator/5.1.5: + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} + deprecated: this library is no longer supported + dependencies: + ajv: 6.12.6 + har-schema: 2.0.0 + dev: true + + /has-ansi/2.0.0: + resolution: {integrity: sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=} + engines: {node: '>=0.10.0'} + dependencies: + ansi-regex: 2.1.1 + dev: true + + /has-bigints/1.0.1: + resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} + dev: true + + /has-flag/1.0.0: + resolution: {integrity: sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=} + engines: {node: '>=0.10.0'} + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=} + engines: {node: '>=4'} + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-symbols/1.0.2: + resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.2 + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + + /hash-base/3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.0 + safe-buffer: 5.2.1 + dev: true + + /hash.js/1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: true + + /highlight.js/10.4.1: + resolution: {integrity: sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==} + dev: true + + /history/5.1.0: + resolution: {integrity: sha512-zPuQgPacm2vH2xdORvGGz1wQMuHSIB56yNAy5FnLuwOwgSYyPKptJtcMm6Ev+hRGeS+GzhbmRacHzvlESbFwDg==} + dependencies: + '@babel/runtime': 7.16.3 + dev: false + + /hmac-drbg/1.0.1: + resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=} + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: true + + /hoist-non-react-statics/3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: false + + /html-encoding-sniffer/1.0.2: + resolution: {integrity: sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==} + dependencies: + whatwg-encoding: 1.0.5 + dev: true + + /html-tags/1.2.0: + resolution: {integrity: sha1-x43mW1Zjqll5id0rerSSANfk25g=} + engines: {node: '>=0.10.0'} + dev: true + + /htmlnano/1.1.1: + resolution: {integrity: sha512-diMNyqTPx4uGwlxrTs0beZCy8L/GxGIFGHWv20OYhthLcdYkDOP/d4Ja5MbGgVJZMakZUM21KpMk5qWZrBGSdw==} + dependencies: + cosmiconfig: 7.0.1 + cssnano: 5.0.11_postcss@8.4.4 + postcss: 8.4.4 + posthtml: 0.16.5 + purgecss: 4.0.3 + relateurl: 0.2.7 + srcset: 4.0.0 + svgo: 2.8.0 + terser: 5.10.0 + timsort: 0.3.0 + uncss: 0.17.3 + transitivePeerDependencies: + - acorn + dev: true + + /htmlparser2/7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + dependencies: + domelementtype: 2.2.0 + domhandler: 4.2.2 + domutils: 2.8.0 + entities: 3.0.1 + + /http-proxy-middleware/1.3.1: + resolution: {integrity: sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==} + engines: {node: '>=8.0.0'} + dependencies: + '@types/http-proxy': 1.17.7 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.4 + transitivePeerDependencies: + - debug + dev: true + + /http-proxy/1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.14.5 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + dev: true + + /http-signature/1.2.0: + resolution: {integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=} + engines: {node: '>=0.8', npm: '>=1.3.7'} + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.1 + sshpk: 1.16.1 + dev: true + + /https-browserify/1.0.0: + resolution: {integrity: sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=} + dev: true + + /human-signals/2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /husky/7.0.4: + resolution: {integrity: sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==} + engines: {node: '>=12'} + hasBin: true + dev: true + + /iconv-lite/0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /icss-replace-symbols/1.1.0: + resolution: {integrity: sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=} + dev: true + + /icss-utils/4.1.1: + resolution: {integrity: sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==} + engines: {node: '>= 6'} + dependencies: + postcss: 7.0.39 + dev: true + + /ieee754/1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /iferr/1.0.2: + resolution: {integrity: sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==} + engines: {node: '>=6.0.0'} + dev: true + + /ignore/4.0.6: + resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} + engines: {node: '>= 4'} + dev: true + + /ignore/5.1.9: + resolution: {integrity: sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==} + engines: {node: '>= 4'} + dev: true + + /immer/9.0.7: + resolution: {integrity: sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==} + dev: false + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + /imurmurhash/0.1.4: + resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string/4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /indexes-of/1.0.1: + resolution: {integrity: sha1-8w9xbI4r00bHtn0985FVZqfAVgc=} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /internal-slot/1.0.3: + resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-absolute-url/3.0.3: + resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} + engines: {node: '>=8'} + dev: true + + /is-arguments/1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-arrayish/0.2.1: + resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} + + /is-bigint/1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.1 + dev: true + + /is-boolean-object/1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable/1.2.4: + resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module/2.8.0: + resolution: {integrity: sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==} + dependencies: + has: 1.0.3 + + /is-date-object/1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-docker/2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point/3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-fullwidth-code-point/4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true + + /is-generator-function/1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-html/1.1.0: + resolution: {integrity: sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=} + engines: {node: '>=0.10.0'} + dependencies: + html-tags: 1.2.0 + dev: true + + /is-interactive/1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + + /is-json/2.0.1: + resolution: {integrity: sha1-a+Fm0USCihMdaGiRuYPfYsOUkf8=} + dev: true + + /is-nan/1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: true + + /is-negative-zero/2.0.1: + resolution: {integrity: sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object/1.0.6: + resolution: {integrity: sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-plain-obj/3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + dev: true + + /is-regex/1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-resolvable/1.1.0: + resolution: {integrity: sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==} + dev: true + + /is-shared-array-buffer/1.0.1: + resolution: {integrity: sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==} + dev: true + + /is-stream/2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-string/1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol/1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.2 + dev: true + + /is-typed-array/1.1.8: + resolution: {integrity: sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-abstract: 1.19.1 + foreach: 2.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-typedarray/1.0.0: + resolution: {integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=} + dev: true + + /is-unicode-supported/0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-url/1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + dev: true + + /is-weakref/1.0.1: + resolution: {integrity: sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-wsl/2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isarray/1.0.0: + resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} + dev: true + + /isstream/0.1.2: + resolution: {integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=} + dev: true + + /jake/10.8.2: + resolution: {integrity: sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==} + hasBin: true + dependencies: + async: 0.9.2 + chalk: 2.4.2 + filelist: 1.0.2 + minimatch: 3.0.4 + dev: true + + /js-sha3/0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + dev: false + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml/3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /jsbn/0.1.1: + resolution: {integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM=} + dev: true + + /jsdom/14.1.0: + resolution: {integrity: sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==} + engines: {node: '>=8'} + dependencies: + abab: 2.0.5 + acorn: 6.4.2 + acorn-globals: 4.3.4 + array-equal: 1.0.0 + cssom: 0.3.8 + cssstyle: 1.4.0 + data-urls: 1.1.0 + domexception: 1.0.1 + escodegen: 1.14.3 + html-encoding-sniffer: 1.0.2 + nwsapi: 2.2.0 + parse5: 5.1.0 + pn: 1.1.0 + request: 2.88.2 + request-promise-native: 1.0.9_request@2.88.2 + saxes: 3.1.11 + symbol-tree: 3.2.4 + tough-cookie: 2.5.0 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 1.1.2 + webidl-conversions: 4.0.2 + whatwg-encoding: 1.0.5 + whatwg-mimetype: 2.3.0 + whatwg-url: 7.1.0 + ws: 6.2.2 + xml-name-validator: 3.0.0 + dev: true + + /jsesc/2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-parse-even-better-errors/2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse/1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-schema/0.2.3: + resolution: {integrity: sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=} + dev: true + + /json-source-map/0.6.1: + resolution: {integrity: sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=} + dev: true + + /json-stringify-safe/5.0.1: + resolution: {integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=} + dev: true + + /json5/1.0.1: + resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} + hasBin: true + dependencies: + minimist: 1.2.5 + dev: true + + /json5/2.2.0: + resolution: {integrity: sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==} + engines: {node: '>=6'} + hasBin: true + dependencies: + minimist: 1.2.5 + dev: true + + /jsprim/1.4.1: + resolution: {integrity: sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.2.3 + verror: 1.10.0 + dev: true + + /jsx-ast-utils/3.2.1: + resolution: {integrity: sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.4 + object.assign: 4.1.2 + dev: true + + /levn/0.3.0: + resolution: {integrity: sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig/2.0.4: + resolution: {integrity: sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==} + engines: {node: '>=10'} + dev: true + + /lines-and-columns/1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + /lint-staged/12.1.2: + resolution: {integrity: sha512-bSMcQVqMW98HLLLR2c2tZ+vnDCnx4fd+0QJBQgN/4XkdspGRPc8DGp7UuOEBe1ApCfJ+wXXumYnJmU+wDo7j9A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + cli-truncate: 3.1.0 + colorette: 2.0.16 + commander: 8.3.0 + debug: 4.3.2_supports-color@9.2.1 + enquirer: 2.3.6 + execa: 5.1.1 + lilconfig: 2.0.4 + listr2: 3.13.5_enquirer@2.3.6 + micromatch: 4.0.4 + normalize-path: 3.0.0 + object-inspect: 1.11.0 + string-argv: 0.3.1 + supports-color: 9.2.1 + yaml: 1.10.2 + dev: true + + /listr2/3.13.5_enquirer@2.3.6: + resolution: {integrity: sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA==} + engines: {node: '>=10.0.0'} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.16 + enquirer: 2.3.6 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.4.0 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /lmdb-store/1.6.14: + resolution: {integrity: sha512-4woZfvfgolMEngjoMJrwePjdLotr3QKGJsDWURlJmKBed5JtE00IfAKo7ryPowl4ksGcs21pcdLkwrPnKomIuA==} + requiresBuild: true + dependencies: + msgpackr: 1.5.1 + nan: 2.15.0 + node-gyp-build: 4.3.0 + ordered-binary: 1.2.1 + weak-lru-cache: 1.1.3 + dev: true + + /loader-utils/1.4.0: + resolution: {integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==} + engines: {node: '>=4.0.0'} + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 1.0.1 + dev: true + + /locate-path/2.0.0: + resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=} + engines: {node: '>=4'} + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + dev: true + + /lodash.camelcase/4.3.0: + resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} + dev: true + + /lodash.clone/4.5.0: + resolution: {integrity: sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=} + dev: true + + /lodash.memoize/4.1.2: + resolution: {integrity: sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=} + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.sortby/4.7.0: + resolution: {integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=} + dev: true + + /lodash.truncate/4.4.2: + resolution: {integrity: sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=} + dev: true + + /lodash.uniq/4.5.0: + resolution: {integrity: sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=} + dev: true + + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-symbols/4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /log-update/4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: true + + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /lowlight/1.17.0: + resolution: {integrity: sha512-vmtBgYKD+QVNy7tIa7ulz5d//Il9R4MooOVh4nkOf9R9Cb/Dk5TXMSTieg/vDulkBkIWj59/BIlyFQxT9X1oAQ==} + dependencies: + fault: 1.0.4 + highlight.js: 10.4.1 + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /match-sorter/6.3.1: + resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==} + dependencies: + '@babel/runtime': 7.16.3 + remove-accents: 0.4.2 + dev: false + + /md5.js/1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /mdn-data/2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + dev: true + + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.4: + resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.0 + dev: true + + /microseconds/0.2.0: + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} + dev: false + + /miller-rabin/4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + dev: true + + /mime-db/1.33.0: + resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} + engines: {node: '>= 0.6'} + dev: true + + /mime-db/1.51.0: + resolution: {integrity: sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==} + engines: {node: '>= 0.6'} + dev: true + + /mime-types/2.1.18: + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.33.0 + dev: true + + /mime-types/2.1.34: + resolution: {integrity: sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.51.0 + dev: true + + /mimic-fn/2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /minimalistic-assert/1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + dev: true + + /minimalistic-crypto-utils/1.0.1: + resolution: {integrity: sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=} + dev: true + + /minimatch/3.0.4: + resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} + dependencies: + brace-expansion: 1.1.11 + + /minimist/1.2.5: + resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} + dev: true + + /mkdirp/0.5.5: + resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==} + hasBin: true + dependencies: + minimist: 1.2.5 + dev: true + + /ms/2.0.0: + resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /msgpackr-extract/1.0.15: + resolution: {integrity: sha512-vgJgzFva0/4/mt84wXf3CRCDPHKqiqk5t7/kVSjk/V2IvwSjoStHhxyq/b2+VrWcch3sxiNQOJEWXgI86Fm7AQ==} + requiresBuild: true + dependencies: + nan: 2.15.0 + node-gyp-build: 4.3.0 + dev: true + optional: true + + /msgpackr/1.5.1: + resolution: {integrity: sha512-I1CXFG8BYYSeIhtDlHpUVMsdDiyvP9JAh1d9QoBnkPx3ETPeH/1lR14hweM9GETs09wCWlaOyhtXxIc9boxAAA==} + optionalDependencies: + msgpackr-extract: 1.0.15 + dev: true + + /nan/2.15.0: + resolution: {integrity: sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==} + dev: true + + /nano-time/1.0.0: + resolution: {integrity: sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=} + dependencies: + big-integer: 1.6.51 + dev: false + + /nanoid/3.1.30: + resolution: {integrity: sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /natural-compare/1.4.0: + resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} + dev: true + + /ncp/2.0.0: + resolution: {integrity: sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=} + hasBin: true + dev: true + + /nice-try/1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: true + + /node-addon-api/3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + dev: true + + /node-forge/0.10.0: + resolution: {integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==} + engines: {node: '>= 6.0.0'} + dev: true + + /node-gyp-build/4.3.0: + resolution: {integrity: sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==} + hasBin: true + dev: true + + /node-releases/2.0.1: + resolution: {integrity: sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-url/6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: true + + /normalize-url/7.0.2: + resolution: {integrity: sha512-HC9c6eHqxmiR6sL9DKt9ttLkiLaI1jytdkJMGAEvkLAAdlOi99kR7UMWWWRrwjucuFabaau4ZuvP1Zv+6PpDjA==} + engines: {node: '>=12.20'} + dev: false + + /notistack/2.0.3_a971fc2901e017cb84f6db81d8e99c7c: + resolution: {integrity: sha512-krmVFtTO9kEY1Pa4NrbyexrjiRcV6TqBM2xLx8nuDea1g96Z/OZfkvVLmYKkTvoSJ3jyQntWK16z86ssW5kt4A==} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + '@mui/material': ^5.0.0 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + dependencies: + '@emotion/react': 11.7.0_cfedea9b3ed0faf0dded75c187406c5e + '@emotion/styled': 11.6.0_49e498e16442da1a9ae75db6f4430d97 + '@mui/material': 5.2.1_e775e23d77fc33f172a6bda0cd692c92 + clsx: 1.1.1 + hoist-non-react-statics: 3.3.2 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + dev: false + + /npm-run-path/4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /nth-check/2.0.1: + resolution: {integrity: sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==} + dependencies: + boolbase: 1.0.0 + + /nullthrows/1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + dev: true + + /nwsapi/2.2.0: + resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==} + dev: true + + /oauth-sign/0.9.0: + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + dev: true + + /object-assign/4.1.1: + resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} + engines: {node: '>=0.10.0'} + + /object-inspect/1.11.0: + resolution: {integrity: sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==} + dev: true + + /object-is/1.1.5: + resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: true + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign/4.1.2: + resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + has-symbols: 1.0.2 + object-keys: 1.1.1 + dev: true + + /object.entries/1.1.5: + resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + dev: true + + /object.fromentries/2.0.5: + resolution: {integrity: sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + dev: true + + /object.hasown/1.1.0: + resolution: {integrity: sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==} + dependencies: + define-properties: 1.1.3 + es-abstract: 1.19.1 + dev: true + + /object.values/1.1.5: + resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + dev: true + + /oblivious-set/1.0.0: + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} + dev: false + + /on-finished/2.3.0: + resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: true + + /once/1.4.0: + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + dependencies: + wrappy: 1.0.2 + + /onetime/5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /open/7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + + /optionator/0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.3 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /ora/5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + + /ordered-binary/1.2.1: + resolution: {integrity: sha512-Zl2RCcj/wRCakW9/yI83gutgNf7JFOPEHrCK72z+boIrU+PWAnIt6HADd1w+3keDQ90GCKbp1BduKZgkeNbz7A==} + dev: true + + /os-browserify/0.3.0: + resolution: {integrity: sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=} + dev: true + + /p-limit/1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + dependencies: + p-try: 1.0.0 + dev: true + + /p-locate/2.0.0: + resolution: {integrity: sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=} + engines: {node: '>=4'} + dependencies: + p-limit: 1.3.0 + dev: true + + /p-map/4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + + /p-try/1.0.0: + resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=} + engines: {node: '>=4'} + dev: true + + /pako/1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: true + + /parcel-resolver-pnpm/0.0.1: + resolution: {integrity: sha512-mqSQZ6thntoKjkiqQLF/Kfifmp2ako2Pl4q+xGtD4VDXVVxEo/2lGyOdVfQ8BLA3CEWTndFu3U7x9l9a+oj9Ig==} + engines: {node: '>= 12.0.0', parcel: ^2.0.1} + dependencies: + '@parcel/node-resolver-core': 2.0.1 + '@parcel/plugin': 2.0.1 + transitivePeerDependencies: + - '@parcel/core' + dev: true + + /parcel/2.0.1: + resolution: {integrity: sha512-tGc7p3CbltlxYiu5u8NmFc2T5G3JZQDbKjlxDtjDpsNvLS6twX0Wu/D/HufaUKXyZr7O9JV7by3E+6DIo5cr5Q==} + engines: {node: '>= 12.0.0'} + hasBin: true + dependencies: + '@parcel/config-default': 2.0.1_@parcel+core@2.0.1 + '@parcel/core': 2.0.1 + '@parcel/diagnostic': 2.0.1 + '@parcel/events': 2.0.1 + '@parcel/fs': 2.0.1_@parcel+core@2.0.1 + '@parcel/logger': 2.0.1 + '@parcel/package-manager': 2.0.1_@parcel+core@2.0.1 + '@parcel/reporter-cli': 2.0.1_@parcel+core@2.0.1 + '@parcel/reporter-dev-server': 2.0.1_@parcel+core@2.0.1 + '@parcel/utils': 2.0.1 + chalk: 4.1.2 + commander: 7.2.0 + get-port: 4.2.0 + v8-compile-cache: 2.3.0 + transitivePeerDependencies: + - acorn + - bufferutil + - debug + - supports-color + - utf-8-validate + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + + /parse-asn1/5.1.6: + resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==} + dependencies: + asn1.js: 5.4.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + pbkdf2: 3.1.2 + safe-buffer: 5.2.1 + dev: true + + /parse-json/5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.16.0 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + /parse5/5.1.0: + resolution: {integrity: sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==} + dev: true + + /parseurl/1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: true + + /path-browserify/1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true + + /path-exists/3.0.0: + resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=} + engines: {node: '>=4'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} + engines: {node: '>=0.10.0'} + + /path-is-inside/1.0.2: + resolution: {integrity: sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=} + dev: true + + /path-key/2.0.1: + resolution: {integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=} + engines: {node: '>=4'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + /path-to-regexp/2.2.1: + resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + /pbkdf2/3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: true + + /performance-now/2.1.0: + resolution: {integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=} + dev: true + + /picocolors/0.2.1: + resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch/2.3.0: + resolution: {integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==} + engines: {node: '>=8.6'} + dev: true + + /pkg-dir/2.0.0: + resolution: {integrity: sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=} + engines: {node: '>=4'} + dependencies: + find-up: 2.1.0 + dev: true + + /pn/1.1.0: + resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==} + dev: true + + /postcss-calc/8.0.0_postcss@8.4.4: + resolution: {integrity: sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==} + peerDependencies: + postcss: ^8.2.2 + dependencies: + postcss: 8.4.4 + postcss-selector-parser: 6.0.6 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-colormin/5.2.1_postcss@8.4.4: + resolution: {integrity: sha512-VVwMrEYLcHYePUYV99Ymuoi7WhKrMGy/V9/kTS0DkCoJYmmjdOMneyhzYUxcNgteKDVbrewOkSM7Wje/MFwxzA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.18.1 + caniuse-api: 3.0.0 + colord: 2.9.1 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-convert-values/5.0.2_postcss@8.4.4: + resolution: {integrity: sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-discard-comments/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + dev: true + + /postcss-discard-duplicates/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + dev: true + + /postcss-discard-empty/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + dev: true + + /postcss-discard-overridden/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + dev: true + + /postcss-merge-longhand/5.0.4_postcss@8.4.4: + resolution: {integrity: sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + stylehacks: 5.0.1_postcss@8.4.4 + dev: true + + /postcss-merge-rules/5.0.3_postcss@8.4.4: + resolution: {integrity: sha512-cEKTMEbWazVa5NXd8deLdCnXl+6cYG7m2am+1HzqH0EnTdy8fRysatkaXb2dEnR+fdaDxTvuZ5zoBdv6efF6hg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.18.1 + caniuse-api: 3.0.0 + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-selector-parser: 6.0.6 + dev: true + + /postcss-minify-font-values/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-minify-gradients/5.0.3_postcss@8.4.4: + resolution: {integrity: sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + colord: 2.9.1 + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-minify-params/5.0.2_postcss@8.4.4: + resolution: {integrity: sha512-qJAPuBzxO1yhLad7h2Dzk/F7n1vPyfHfCCh5grjGfjhi1ttCnq4ZXGIW77GSrEbh9Hus9Lc/e/+tB4vh3/GpDg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + alphanum-sort: 1.0.2 + browserslist: 4.18.1 + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-minify-selectors/5.1.0_postcss@8.4.4: + resolution: {integrity: sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + alphanum-sort: 1.0.2 + postcss: 8.4.4 + postcss-selector-parser: 6.0.6 + dev: true + + /postcss-modules-extract-imports/1.1.0: + resolution: {integrity: sha1-thTJcgvmgW6u41+zpfqh26agXds=} + dependencies: + postcss: 6.0.1 + dev: true + + /postcss-modules-extract-imports/2.0.0: + resolution: {integrity: sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==} + engines: {node: '>= 6'} + dependencies: + postcss: 7.0.39 + dev: true + + /postcss-modules-local-by-default/1.2.0: + resolution: {integrity: sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=} + dependencies: + css-selector-tokenizer: 0.7.3 + postcss: 6.0.1 + dev: true + + /postcss-modules-local-by-default/3.0.3: + resolution: {integrity: sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==} + engines: {node: '>= 6'} + dependencies: + icss-utils: 4.1.1 + postcss: 7.0.39 + postcss-selector-parser: 6.0.6 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-modules-scope/1.1.0: + resolution: {integrity: sha1-1upkmUx5+XtipytCb75gVqGUu5A=} + dependencies: + css-selector-tokenizer: 0.7.3 + postcss: 6.0.1 + dev: true + + /postcss-modules-scope/2.2.0: + resolution: {integrity: sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==} + engines: {node: '>= 6'} + dependencies: + postcss: 7.0.39 + postcss-selector-parser: 6.0.6 + dev: true + + /postcss-modules-values/1.3.0: + resolution: {integrity: sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=} + dependencies: + icss-replace-symbols: 1.1.0 + postcss: 6.0.1 + dev: true + + /postcss-modules-values/3.0.0: + resolution: {integrity: sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==} + dependencies: + icss-utils: 4.1.1 + postcss: 7.0.39 + dev: true + + /postcss-modules/3.2.2: + resolution: {integrity: sha512-JQ8IAqHELxC0N6tyCg2UF40pACY5oiL6UpiqqcIFRWqgDYO8B0jnxzoQ0EOpPrWXvcpu6BSbQU/3vSiq7w8Nhw==} + dependencies: + generic-names: 2.0.1 + icss-replace-symbols: 1.1.0 + lodash.camelcase: 4.3.0 + postcss: 7.0.39 + postcss-modules-extract-imports: 2.0.0 + postcss-modules-local-by-default: 3.0.3 + postcss-modules-scope: 2.2.0 + postcss-modules-values: 3.0.0 + string-hash: 1.1.3 + dev: true + + /postcss-normalize-charset/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + dev: true + + /postcss-normalize-display-values/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-positions/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-repeat-style/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-string/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-timing-functions/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-unicode/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.18.1 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-url/5.0.3_postcss@8.4.4: + resolution: {integrity: sha512-qWiUMbvkRx3kc1Dp5opzUwc7MBWZcSDK2yofCmdvFBCpx+zFPkxBC1FASQ59Pt+flYfj/nTZSkmF56+XG5elSg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + is-absolute-url: 3.0.3 + normalize-url: 6.1.0 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-normalize-whitespace/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-ordered-values/5.0.2_postcss@8.4.4: + resolution: {integrity: sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-reduce-initial/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.18.1 + caniuse-api: 3.0.0 + postcss: 8.4.4 + dev: true + + /postcss-reduce-transforms/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 2.0.1_postcss@8.4.4 + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + dev: true + + /postcss-selector-parser/6.0.2: + resolution: {integrity: sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + indexes-of: 1.0.1 + uniq: 1.0.1 + dev: true + + /postcss-selector-parser/6.0.6: + resolution: {integrity: sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-svgo/5.0.3_postcss@8.4.4: + resolution: {integrity: sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.4 + postcss-value-parser: 4.1.0 + svgo: 2.8.0 + dev: true + + /postcss-unique-selectors/5.0.2_postcss@8.4.4: + resolution: {integrity: sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + alphanum-sort: 1.0.2 + postcss: 8.4.4 + postcss-selector-parser: 6.0.6 + dev: true + + /postcss-value-parser/4.1.0: + resolution: {integrity: sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==} + dev: true + + /postcss/6.0.1: + resolution: {integrity: sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=} + engines: {node: '>=4.0.0'} + dependencies: + chalk: 1.1.3 + source-map: 0.5.7 + supports-color: 3.2.3 + dev: true + + /postcss/7.0.39: + resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} + engines: {node: '>=6.0.0'} + dependencies: + picocolors: 0.2.1 + source-map: 0.6.1 + dev: true + + /postcss/8.4.4: + resolution: {integrity: sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.1.30 + picocolors: 1.0.0 + source-map-js: 1.0.1 + + /posthtml-parser/0.10.1: + resolution: {integrity: sha512-i7w2QEHqiGtsvNNPty0Mt/+ERch7wkgnFh3+JnBI2VgDbGlBqKW9eDVd3ENUhE1ujGFe3e3E/odf7eKhvLUyDg==} + engines: {node: '>=12'} + dependencies: + htmlparser2: 7.2.0 + dev: true + + /posthtml-render/3.0.0: + resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} + engines: {node: '>=12'} + dependencies: + is-json: 2.0.1 + dev: true + + /posthtml/0.16.5: + resolution: {integrity: sha512-1qOuPsywVlvymhTFIBniDXwUDwvlDri5KUQuBqjmCc8Jj4b/HDSVWU//P6rTWke5rzrk+vj7mms2w8e1vD0nnw==} + engines: {node: '>=12.0.0'} + dependencies: + posthtml-parser: 0.10.1 + posthtml-render: 3.0.0 + dev: true + + /prelude-ls/1.1.2: + resolution: {integrity: sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=} + engines: {node: '>= 0.8.0'} + dev: true + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-config-standard/4.0.0: + resolution: {integrity: sha512-K5J34NKeGyJvVVHUg4cAWDhtVYzT7UIRE9GbFyqHNZd61Pr0DHNTMsgoID7IVDmKc0QaLswYwKaNR2apbUJvkg==} + dev: true + + /prettier/2.5.0: + resolution: {integrity: sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /process-nextick-args/2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /process/0.11.10: + resolution: {integrity: sha1-czIwDoQBYb2j5podHZGn1LwW8YI=} + engines: {node: '>= 0.6.0'} + dev: true + + /progress/2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true + + /prop-types/15.7.2: + resolution: {integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + /psl/1.8.0: + resolution: {integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==} + dev: true + + /public-encrypt/4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + dependencies: + bn.js: 4.12.0 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + parse-asn1: 5.1.6 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: true + + /punycode/1.3.2: + resolution: {integrity: sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=} + dev: true + + /punycode/1.4.1: + resolution: {integrity: sha1-wNWmOycYgArY4esPpSachN1BhF4=} + dev: true + + /punycode/2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /purgecss/4.0.3: + resolution: {integrity: sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==} + hasBin: true + dependencies: + commander: 6.2.1 + glob: 7.2.0 + postcss: 8.4.4 + postcss-selector-parser: 6.0.6 + dev: true + + /qs/6.5.2: + resolution: {integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==} + engines: {node: '>=0.6'} + dev: true + + /querystring-es3/0.2.1: + resolution: {integrity: sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=} + engines: {node: '>=0.4.x'} + dev: true + + /querystring/0.2.0: + resolution: {integrity: sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=} + engines: {node: '>=0.4.x'} + deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes/2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /randomfill/1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: true + + /range-parser/1.2.0: + resolution: {integrity: sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=} + engines: {node: '>= 0.6'} + dev: true + + /react-async/10.0.1_react@17.0.2: + resolution: {integrity: sha512-ORUz5ca0B57QgBIzEZM5SuhJ6xFjkvEEs0gylLNlWf06vuVcLZsjIw3wx58jJkZG38p+0nUAxRgFW2b7mnVZzA==} + peerDependencies: + react: '>=16.3.1' + dependencies: + react: 17.0.2 + dev: false + + /react-dom/17.0.2_react@17.0.2: + resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} + peerDependencies: + react: 17.0.2 + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react: 17.0.2 + scheduler: 0.20.2 + dev: false + + /react-hook-form/7.20.4_react@17.0.2: + resolution: {integrity: sha512-Nvy6TnNMlBoR+qS8FpA8lrqtGJ4uoi/MRYEgMEdBMY0HwHVuC7wB1sk6wTjg7FjOUt7QqMAP2W/vOhTWbKrtkQ==} + engines: {node: '>=12.0'} + peerDependencies: + react: ^16.8.0 || ^17 + dependencies: + react: 17.0.2 + dev: false + + /react-is/16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + /react-is/17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: false + + /react-query/3.33.7_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-uL/1ZatxcRyvfhbNPeuOlLVbcdMQKzQ2t+SQLJMrMM0plfa/Al3buaPbGsbsmIwvyWeI4CVkkSBKzQIkU4p9MQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.16.3 + broadcast-channel: 3.7.0 + match-sorter: 6.3.1 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + dev: false + + /react-refresh/0.9.0: + resolution: {integrity: sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react-router-dom/6.0.2_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-cOpJ4B6raFutr0EG8O/M2fEoyQmwvZWomf1c6W2YXBZuFBx8oTk/zqjXghwScyhfrtnt0lANXV2182NQblRxFA==} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + history: 5.1.0 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-router: 6.0.2_react@17.0.2 + dev: false + + /react-router/6.0.2_react@17.0.2: + resolution: {integrity: sha512-8/Wm3Ed8t7TuedXjAvV39+c8j0vwrI5qVsYqjFr5WkJjsJpEvNSoLRUbtqSEYzqaTUj1IV+sbPJxvO+accvU0Q==} + peerDependencies: + react: '>=16.8' + dependencies: + history: 5.1.0 + react: 17.0.2 + dev: false + + /react-transition-group/4.4.2_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + dependencies: + '@babel/runtime': 7.16.3 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.7.2 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + dev: false + + /react-virtuoso/2.2.8_react@17.0.2: + resolution: {integrity: sha512-DArJ29ZrhdygdLKdXs/GB3VNDxaAF7YjdgIjlWPuK6UsV848hNmtw9px3LPdcQBErxaz7XCtHNfc7yInX+/M7g==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + dependencies: + '@virtuoso.dev/react-urx': 0.2.10_react@17.0.2 + '@virtuoso.dev/urx': 0.2.10 + react: 17.0.2 + dev: false + + /react/17.0.2: + resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: false + + /readable-stream/2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readable-stream/3.6.0: + resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /regenerator-runtime/0.13.9: + resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} + + /regexp.prototype.flags/1.3.1: + resolution: {integrity: sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: true + + /regexpp/3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /relateurl/0.2.7: + resolution: {integrity: sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=} + engines: {node: '>= 0.10'} + dev: true + + /remove-accents/0.4.2: + resolution: {integrity: sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=} + dev: false + + /request-promise-core/1.1.4_request@2.88.2: + resolution: {integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==} + engines: {node: '>=0.10.0'} + peerDependencies: + request: ^2.34 + dependencies: + lodash: 4.17.21 + request: 2.88.2 + dev: true + + /request-promise-native/1.0.9_request@2.88.2: + resolution: {integrity: sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==} + engines: {node: '>=0.12.0'} + deprecated: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142 + peerDependencies: + request: ^2.34 + dependencies: + request: 2.88.2 + request-promise-core: 1.1.4_request@2.88.2 + stealthy-require: 1.1.1 + tough-cookie: 2.5.0 + dev: true + + /request/2.88.2: + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + dependencies: + aws-sign2: 0.7.0 + aws4: 1.11.0 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.5 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.34 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.2 + safe-buffer: 5.2.1 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 3.4.0 + dev: true + + /require-from-string/2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /requires-port/1.0.0: + resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=} + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + /resolve/1.20.0: + resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} + dependencies: + is-core-module: 2.8.0 + path-parse: 1.0.7 + + /resolve/2.0.0-next.3: + resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==} + dependencies: + is-core-module: 2.8.0 + path-parse: 1.0.7 + dev: true + + /restore-cursor/3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.6 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rfdc/1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.0 + + /ripemd160/2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /ruty/1.2.3: + resolution: {integrity: sha512-gxAQwRMs8ybDWKipqAwCSiJ4u7frdSXl8/eHJy87cGo9ljdhNyk8xM9ZyMwKWhh4/7G5xUxEUeH8AE1Ydexjng==} + dev: false + + /rxjs/7.4.0: + resolution: {integrity: sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==} + dependencies: + tslib: 2.1.0 + dev: true + + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /saxes/3.1.11: + resolution: {integrity: sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==} + engines: {node: '>=8'} + dependencies: + xmlchars: 2.2.0 + dev: true + + /scheduler/0.20.2: + resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: false + + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + + /semver/6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver/7.3.5: + resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /serve-handler/6.1.3: + resolution: {integrity: sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==} + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + fast-url-parser: 1.1.3 + mime-types: 2.1.18 + minimatch: 3.0.4 + path-is-inside: 1.0.2 + path-to-regexp: 2.2.1 + range-parser: 1.2.0 + dev: true + + /setimmediate/1.0.5: + resolution: {integrity: sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=} + dev: true + + /sha.js/2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /shebang-command/1.2.0: + resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/1.0.0: + resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} + engines: {node: '>=0.10.0'} + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + object-inspect: 1.11.0 + dev: true + + /signal-exit/3.0.6: + resolution: {integrity: sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==} + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi/3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi/4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi/5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.1.0 + is-fullwidth-code-point: 4.0.0 + dev: true + + /source-map-js/1.0.1: + resolution: {integrity: sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==} + engines: {node: '>=0.10.0'} + + /source-map-support/0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map/0.5.7: + resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} + engines: {node: '>=0.10.0'} + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map/0.7.3: + resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} + engines: {node: '>= 8'} + dev: true + + /split2/3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + dependencies: + readable-stream: 3.6.0 + dev: true + + /sprintf-js/1.0.3: + resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} + dev: true + + /srcset/4.0.0: + resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} + engines: {node: '>=12'} + dev: true + + /sshpk/1.16.1: + resolution: {integrity: sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + dev: true + + /stable/0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + dev: true + + /statuses/1.5.0: + resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=} + engines: {node: '>= 0.6'} + dev: true + + /stealthy-require/1.1.1: + resolution: {integrity: sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=} + engines: {node: '>=0.10.0'} + dev: true + + /stream-browserify/3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: true + + /stream-http/3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + xtend: 4.0.2 + dev: true + + /string-argv/0.3.1: + resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} + engines: {node: '>=0.6.19'} + dev: true + + /string-hash/1.1.3: + resolution: {integrity: sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=} + dev: true + + /string-width/4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width/5.0.1: + resolution: {integrity: sha512-5ohWO/M4//8lErlUUtrFy3b11GtNOuMOU0ysKCDXFcfXuuvUXu95akgj/i8ofmaGdN0hCqyl6uu9i8dS/mQp5g==} + engines: {node: '>=12'} + dependencies: + emoji-regex: 9.2.2 + is-fullwidth-code-point: 4.0.0 + strip-ansi: 7.0.1 + dev: true + + /string.prototype.matchall/4.0.6: + resolution: {integrity: sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + get-intrinsic: 1.1.1 + has-symbols: 1.0.2 + internal-slot: 1.0.3 + regexp.prototype.flags: 1.3.1 + side-channel: 1.0.4 + dev: true + + /string.prototype.trimend/1.0.4: + resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: true + + /string.prototype.trimstart/1.0.4: + resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: true + + /string_decoder/1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /string_decoder/1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /strip-ansi/3.0.1: + resolution: {integrity: sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=} + engines: {node: '>=0.10.0'} + dependencies: + ansi-regex: 2.1.1 + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi/7.0.1: + resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} + engines: {node: '>=4'} + dev: true + + /strip-final-newline/2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /stylehacks/5.0.1_postcss@8.4.4: + resolution: {integrity: sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.18.1 + postcss: 8.4.4 + postcss-selector-parser: 6.0.6 + dev: true + + /stylis/4.0.10: + resolution: {integrity: sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==} + dev: false + + /superstruct/0.15.3: + resolution: {integrity: sha512-wilec1Rg3FtKuRjRyCt70g5W29YUEuaLnybdVQUI+VQ7m0bw8k7TzrRv5iYmo6IpjLVrwxP5t3RgjAVqhYh4Fg==} + dev: false + + /supports-color/2.0.0: + resolution: {integrity: sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=} + engines: {node: '>=0.8.0'} + dev: true + + /supports-color/3.2.3: + resolution: {integrity: sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=} + engines: {node: '>=0.8.0'} + dependencies: + has-flag: 1.0.0 + dev: true + + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color/9.2.1: + resolution: {integrity: sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==} + engines: {node: '>=12'} + dev: true + + /supports-hyperlinks/2.2.0: + resolution: {integrity: sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + + /svgo/2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.1.3 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.0 + stable: 0.1.8 + dev: true + + /symbol-tree/3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + + /table/6.7.3: + resolution: {integrity: sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==} + engines: {node: '>=10.0.0'} + dependencies: + ajv: 8.8.2 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /term-size/2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true + + /terminal-link/2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.2.0 + dev: true + + /terser/5.10.0: + resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==} + engines: {node: '>=10'} + hasBin: true + peerDependencies: + acorn: ^8.5.0 + peerDependenciesMeta: + acorn: + optional: true + dependencies: + commander: 2.20.3 + source-map: 0.7.3 + source-map-support: 0.5.21 + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} + dev: true + + /through/2.3.8: + resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} + dev: true + + /timers-browserify/2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + dependencies: + setimmediate: 1.0.5 + dev: true + + /timsort/0.3.0: + resolution: {integrity: sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=} + dev: true + + /to-fast-properties/2.0.0: + resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=} + engines: {node: '>=4'} + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tough-cookie/2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + dependencies: + psl: 1.8.0 + punycode: 2.1.1 + dev: true + + /tr46/1.0.1: + resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=} + dependencies: + punycode: 2.1.1 + dev: true + + /tsconfig-paths/3.12.0: + resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.1 + minimist: 1.2.5 + strip-bom: 3.0.0 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.1.0: + resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==} + dev: true + + /tsutils/3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + 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' + dependencies: + tslib: 1.14.1 + dev: true + + /tty-browserify/0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + dev: true + + /tunnel-agent/0.6.0: + resolution: {integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /tweetnacl/0.14.5: + resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=} + dev: true + + /type-check/0.3.2: + resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /typescript/4.5.2: + resolution: {integrity: sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==} + engines: {node: '>=4.2.0'} + hasBin: true + + /unbox-primitive/1.0.1: + resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} + dependencies: + function-bind: 1.1.1 + has-bigints: 1.0.1 + has-symbols: 1.0.2 + which-boxed-primitive: 1.0.2 + dev: true + + /uncss/0.17.3: + resolution: {integrity: sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + commander: 2.20.3 + glob: 7.2.0 + is-absolute-url: 3.0.3 + is-html: 1.1.0 + jsdom: 14.1.0 + lodash: 4.17.21 + postcss: 7.0.39 + postcss-selector-parser: 6.0.2 + request: 2.88.2 + dev: true + + /uniq/1.0.1: + resolution: {integrity: sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=} + dev: true + + /unload/2.2.0: + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} + dependencies: + '@babel/runtime': 7.16.3 + detect-node: 2.1.0 + dev: false + + /unpipe/1.0.0: + resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} + engines: {node: '>= 0.8'} + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /url/0.11.0: + resolution: {integrity: sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=} + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + dev: true + + /use-context-selector/1.3.9_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-YgzRyeFjoJXwFn2qLVAuIbV6EQ8DOuzu3SS/eiCxyAyvBhcn02jYSz8c5v22QQU3LW6Ez/Iyo62kKvS7Kdqt3A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '*' + react-native: '*' + scheduler: '>=0.19.0' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + dev: false + + /use-debounce/7.0.1_react@17.0.2: + resolution: {integrity: sha512-fOrzIw2wstbAJuv8PC9Vg4XgwyTLEOdq4y/Z3IhVl8DAE4svRcgyEUvrEXu+BMNgMoc3YND6qLT61kkgEKXh7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 17.0.2 + dev: false + + /util-deprecate/1.0.2: + resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + dev: true + + /util/0.12.4: + resolution: {integrity: sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.8 + safe-buffer: 5.2.1 + which-typed-array: 1.1.7 + dev: true + + /utility-types/3.10.0: + resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==} + engines: {node: '>= 4'} + dev: true + + /utils-merge/1.0.1: + resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} + engines: {node: '>= 0.4.0'} + dev: true + + /uuid/3.4.0: + resolution: {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. + hasBin: true + dev: true + + /v8-compile-cache/2.3.0: + resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} + dev: true + + /verror/1.10.0: + resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + dev: true + + /vm-browserify/1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + dev: true + + /w3c-hr-time/1.0.2: + resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + dependencies: + browser-process-hrtime: 1.0.0 + dev: true + + /w3c-xmlserializer/1.1.2: + resolution: {integrity: sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==} + dependencies: + domexception: 1.0.1 + webidl-conversions: 4.0.2 + xml-name-validator: 3.0.0 + dev: true + + /wcwidth/1.0.1: + resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=} + dependencies: + defaults: 1.0.3 + dev: true + + /weak-lru-cache/1.1.3: + resolution: {integrity: sha512-5LDIv+sr6uzT94Hhcq7Qv7gt3jxol4iMWUqOgJSLYbB5oO7bTSMqIBtKsytm8N2BufYOdJw86/qu+SDfbo/wKQ==} + dev: true + + /webextension-polyfill/0.8.0: + resolution: {integrity: sha512-a19+DzlT6Kp9/UI+mF9XQopeZ+n2ussjhxHJ4/pmIGge9ijCDz7Gn93mNnjpZAk95T4Tae8iHZ6sSf869txqiQ==} + dev: false + + /webidl-conversions/4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + + /whatwg-encoding/1.0.5: + resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + dependencies: + iconv-lite: 0.4.24 + dev: true + + /whatwg-mimetype/2.3.0: + resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} + dev: true + + /whatwg-url/7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.6 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array/1.1.7: + resolution: {integrity: sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-abstract: 1.19.1 + foreach: 2.0.5 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.8 + dev: true + + /which/1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /wrap-ansi/6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi/7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + + /ws/6.2.2: + resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} + dependencies: + async-limiter: 1.0.1 + dev: true + + /ws/7.5.6: + resolution: {integrity: sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /xml-name-validator/3.0.0: + resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} + dev: true + + /xmlchars/2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /xxhash-wasm/0.4.2: + resolution: {integrity: sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..9c9be4b --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - "packages/**"