From c0330acd838b12f63ef1a9f62f3ef1e32031d9a3 Mon Sep 17 00:00:00 2001 From: Chris <76668588+bytesbuffer@users.noreply.github.com> Date: Tue, 21 Sep 2021 05:29:20 -0400 Subject: [PATCH] Add two-factor authentication (#692) Co-authored-by: Bailey Pumfleet --- components/Settings.tsx | 6 +- components/security/ChangePasswordSection.tsx | 122 +++++++++ components/security/DisableTwoFactorModal.tsx | 101 +++++++ components/security/EnableTwoFactorModal.tsx | 211 +++++++++++++++ components/security/TwoFactorAuthAPI.ts | 33 +++ components/security/TwoFactorAuthSection.tsx | 54 ++++ components/security/TwoFactorModalHeader.tsx | 20 ++ environment.d.ts | 5 + lib/auth.ts | 13 + package.json | 3 + pages/api/auth/[...nextauth].tsx | 46 +++- pages/api/auth/changepw.ts | 15 +- pages/api/auth/two-factor/totp/disable.ts | 50 ++++ pages/api/auth/two-factor/totp/enable.ts | 64 +++++ pages/api/auth/two-factor/totp/setup.ts | 66 +++++ pages/auth/login.tsx | 89 ++++++- pages/settings/password.tsx | 127 --------- pages/settings/security.tsx | 49 ++++ .../migration.sql | 3 + prisma/schema.prisma | 2 + yarn.lock | 250 +++++++++++++++++- 21 files changed, 1177 insertions(+), 152 deletions(-) create mode 100644 components/security/ChangePasswordSection.tsx create mode 100644 components/security/DisableTwoFactorModal.tsx create mode 100644 components/security/EnableTwoFactorModal.tsx create mode 100644 components/security/TwoFactorAuthAPI.ts create mode 100644 components/security/TwoFactorAuthSection.tsx create mode 100644 components/security/TwoFactorModalHeader.tsx create mode 100644 environment.d.ts create mode 100644 pages/api/auth/two-factor/totp/disable.ts create mode 100644 pages/api/auth/two-factor/totp/enable.ts create mode 100644 pages/api/auth/two-factor/totp/setup.ts delete mode 100644 pages/settings/password.tsx create mode 100644 pages/settings/security.tsx create mode 100644 prisma/migrations/20210918013258_add_two_factor_fields/migration.sql diff --git a/components/Settings.tsx b/components/Settings.tsx index 4d4354ef5a..a0555e52da 100644 --- a/components/Settings.tsx +++ b/components/Settings.tsx @@ -14,10 +14,10 @@ export default function SettingsShell(props) { current: router.pathname == "/settings/profile", }, { - name: "Password", - href: "/settings/password", + name: "Security", + href: "/settings/security", icon: KeyIcon, - current: router.pathname == "/settings/password", + current: router.pathname == "/settings/security", }, { name: "Embed", href: "/settings/embed", icon: CodeIcon, current: router.pathname == "/settings/embed" }, { diff --git a/components/security/ChangePasswordSection.tsx b/components/security/ChangePasswordSection.tsx new file mode 100644 index 0000000000..68ed469792 --- /dev/null +++ b/components/security/ChangePasswordSection.tsx @@ -0,0 +1,122 @@ +import React, { SyntheticEvent, useState } from "react"; +import Modal from "@components/Modal"; +import { ErrorCode } from "@lib/auth"; + +const errorMessages: { [key: string]: string } = { + [ErrorCode.IncorrectPassword]: "Current password is incorrect", + [ErrorCode.NewPasswordMatchesOld]: + "New password matches your old password. Please choose a different password.", +}; + +const ChangePasswordSection = () => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [successModalOpen, setSuccessModalOpen] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + const closeSuccessModal = () => { + setSuccessModalOpen(false); + }; + + async function changePasswordHandler(e: SyntheticEvent) { + e.preventDefault(); + + if (isSubmitting) { + return; + } + + setIsSubmitting(true); + setErrorMessage(null); + + try { + const response = await fetch("/api/auth/changepw", { + method: "PATCH", + body: JSON.stringify({ oldPassword, newPassword }), + headers: { + "Content-Type": "application/json", + }, + }); + + if (response.status === 200) { + setOldPassword(""); + setNewPassword(""); + setSuccessModalOpen(true); + return; + } + + const body = await response.json(); + setErrorMessage(errorMessages[body.error] || "Something went wrong. Please try again"); + } catch (err) { + console.error("Error changing password", err); + setErrorMessage("Something went wrong. Please try again"); + } finally { + setIsSubmitting(false); + } + } + + return ( + <> +
+

Change Password

+
+
+
+
+
+ +
+ setOldPassword(e.currentTarget.value)} + name="current_password" + id="current_password" + required + className="shadow-sm focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-sm" + placeholder="Your old password" + /> +
+
+
+ +
+ setNewPassword(e.currentTarget.value)} + className="shadow-sm focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-sm" + placeholder="Your super secure new password" + /> +
+
+
+ {errorMessage &&

{errorMessage}

} +
+ +
+
+
+
+ + + ); +}; + +export default ChangePasswordSection; diff --git a/components/security/DisableTwoFactorModal.tsx b/components/security/DisableTwoFactorModal.tsx new file mode 100644 index 0000000000..e64e9b0cd2 --- /dev/null +++ b/components/security/DisableTwoFactorModal.tsx @@ -0,0 +1,101 @@ +import { SyntheticEvent, useState } from "react"; +import Button from "@components/ui/Button"; +import { Dialog, DialogContent } from "@components/Dialog"; +import { ErrorCode } from "@lib/auth"; +import TwoFactorAuthAPI from "./TwoFactorAuthAPI"; +import TwoFactorModalHeader from "./TwoFactorModalHeader"; + +interface DisableTwoFactorAuthModalProps { + /** + * Called when the user closes the modal without disabling two-factor auth + */ + onCancel: () => void; + + /** + * Called when the user disables two-factor auth + */ + onDisable: () => void; +} + +const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuthModalProps) => { + const [password, setPassword] = useState(""); + const [isDisabling, setIsDisabling] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + + async function handleDisable(e: SyntheticEvent) { + e.preventDefault(); + + if (isDisabling) { + return; + } + setIsDisabling(true); + setErrorMessage(null); + + try { + const response = await TwoFactorAuthAPI.disable(password); + if (response.status === 200) { + onDisable(); + return; + } + + const body = await response.json(); + if (body.error === ErrorCode.IncorrectPassword) { + setErrorMessage("Password is incorrect."); + } else { + setErrorMessage("Something went wrong."); + } + } catch (e) { + setErrorMessage("Something went wrong."); + console.error("Error disabling two-factor authentication", e); + } finally { + setIsDisabling(false); + } + } + + return ( + + + + +
+
+ +
+ setPassword(e.currentTarget.value)} + className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" + /> +
+ + {errorMessage &&

{errorMessage}

} +
+
+ +
+ + +
+
+
+ ); +}; + +export default DisableTwoFactorAuthModal; diff --git a/components/security/EnableTwoFactorModal.tsx b/components/security/EnableTwoFactorModal.tsx new file mode 100644 index 0000000000..99594aeb67 --- /dev/null +++ b/components/security/EnableTwoFactorModal.tsx @@ -0,0 +1,211 @@ +import React, { SyntheticEvent, useState } from "react"; +import Button from "@components/ui/Button"; +import { Dialog, DialogContent } from "@components/Dialog"; +import { ErrorCode } from "@lib/auth"; +import TwoFactorAuthAPI from "./TwoFactorAuthAPI"; +import TwoFactorModalHeader from "./TwoFactorModalHeader"; + +interface EnableTwoFactorModalProps { + /** + * Called when the user closes the modal without disabling two-factor auth + */ + onCancel: () => void; + + /** + * Called when the user enables two-factor auth + */ + onEnable: () => void; +} + +enum SetupStep { + ConfirmPassword, + DisplayQrCode, + EnterTotpCode, +} + +const setupDescriptions = { + [SetupStep.ConfirmPassword]: "Confirm your current password to get started.", + [SetupStep.DisplayQrCode]: "Scan the image below with the authenticator app on your phone.", + [SetupStep.EnterTotpCode]: "Enter the six-digit code from your authenticator app below.", +}; + +const WithStep = ({ + step, + current, + children, +}: { + step: SetupStep; + current: SetupStep; + children: JSX.Element; +}) => { + return step === current ? children : null; +}; + +const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) => { + const [step, setStep] = useState(SetupStep.ConfirmPassword); + const [password, setPassword] = useState(""); + const [totpCode, setTotpCode] = useState(""); + const [dataUri, setDataUri] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + + async function handleSetup(e: SyntheticEvent) { + e.preventDefault(); + + if (isSubmitting) { + return; + } + + setIsSubmitting(true); + setErrorMessage(null); + + try { + const response = await TwoFactorAuthAPI.setup(password); + const body = await response.json(); + + if (response.status === 200) { + setDataUri(body.dataUri); + setStep(SetupStep.DisplayQrCode); + return; + } + + if (body.error === ErrorCode.IncorrectPassword) { + setErrorMessage("Password is incorrect."); + } else { + setErrorMessage("Something went wrong."); + } + } catch (e) { + setErrorMessage("Something went wrong."); + console.error("Error setting up two-factor authentication", e); + } finally { + setIsSubmitting(false); + } + } + + async function handleEnable(e: SyntheticEvent) { + e.preventDefault(); + + if (isSubmitting || totpCode.length !== 6) { + return; + } + + setIsSubmitting(true); + setErrorMessage(null); + + try { + const response = await TwoFactorAuthAPI.enable(totpCode); + const body = await response.json(); + + if (response.status === 200) { + onEnable(); + return; + } + + if (body.error === ErrorCode.IncorrectTwoFactorCode) { + setErrorMessage("Code is incorrect. Please try again."); + } else { + setErrorMessage("Something went wrong."); + } + } catch (e) { + setErrorMessage("Something went wrong."); + console.error("Error enabling up two-factor authentication", e); + } finally { + setIsSubmitting(false); + } + } + + return ( + + + + + +
+
+ +
+ setPassword(e.currentTarget.value)} + className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" + /> +
+ + {errorMessage &&

{errorMessage}

} +
+
+
+ +
+ +
+
+ +
+
+ +
+ setTotpCode(e.currentTarget.value)} + className="block w-full border-gray-300 rounded-sm shadow-sm focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" + /> +
+ + {errorMessage &&

{errorMessage}

} +
+
+
+ +
+ + + + + + + + + + +
+
+
+ ); +}; + +export default EnableTwoFactorModal; diff --git a/components/security/TwoFactorAuthAPI.ts b/components/security/TwoFactorAuthAPI.ts new file mode 100644 index 0000000000..eb01d59c4c --- /dev/null +++ b/components/security/TwoFactorAuthAPI.ts @@ -0,0 +1,33 @@ +const TwoFactorAuthAPI = { + async setup(password: string) { + return fetch("/api/auth/two-factor/totp/setup", { + method: "POST", + body: JSON.stringify({ password }), + headers: { + "Content-Type": "application/json", + }, + }); + }, + + async enable(code: string) { + return fetch("/api/auth/two-factor/totp/enable", { + method: "POST", + body: JSON.stringify({ code }), + headers: { + "Content-Type": "application/json", + }, + }); + }, + + async disable(password: string) { + return fetch("/api/auth/two-factor/totp/disable", { + method: "POST", + body: JSON.stringify({ password }), + headers: { + "Content-Type": "application/json", + }, + }); + }, +}; + +export default TwoFactorAuthAPI; diff --git a/components/security/TwoFactorAuthSection.tsx b/components/security/TwoFactorAuthSection.tsx new file mode 100644 index 0000000000..fa7b6045ad --- /dev/null +++ b/components/security/TwoFactorAuthSection.tsx @@ -0,0 +1,54 @@ +import { useState } from "react"; +import Button from "@components/ui/Button"; +import Badge from "@components/ui/Badge"; +import EnableTwoFactorModal from "./EnableTwoFactorModal"; +import DisableTwoFactorModal from "./DisableTwoFactorModal"; + +const TwoFactorAuthSection = ({ twoFactorEnabled }: { twoFactorEnabled: boolean }) => { + const [enabled, setEnabled] = useState(twoFactorEnabled); + const [enableModalOpen, setEnableModalOpen] = useState(false); + const [disableModalOpen, setDisableModalOpen] = useState(false); + + return ( + <> +
+

Two-Factor Authentication

+ + {enabled ? "Enabled" : "Disabled"} + +
+

+ Add an extra layer of security to your account in case your password is stolen. +

+ + + + {enableModalOpen && ( + { + setEnabled(true); + setEnableModalOpen(false); + }} + onCancel={() => setEnableModalOpen(false)} + /> + )} + + {disableModalOpen && ( + { + setEnabled(false); + setDisableModalOpen(false); + }} + onCancel={() => setDisableModalOpen(false)} + /> + )} + + ); +}; + +export default TwoFactorAuthSection; diff --git a/components/security/TwoFactorModalHeader.tsx b/components/security/TwoFactorModalHeader.tsx new file mode 100644 index 0000000000..5953749b5a --- /dev/null +++ b/components/security/TwoFactorModalHeader.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import { ShieldCheckIcon } from "@heroicons/react/solid"; + +const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => { + return ( +
+
+ +
+
+ +

{description}

+
+
+ ); +}; + +export default TwoFactorModalHeader; diff --git a/environment.d.ts b/environment.d.ts new file mode 100644 index 0000000000..2ffddde23f --- /dev/null +++ b/environment.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + interface ProcessEnv { + readonly CALENDSO_ENCRYPTION_KEY: string | undefined; + } +} diff --git a/lib/auth.ts b/lib/auth.ts index a2b32d9c3a..b3b262f4f6 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -27,3 +27,16 @@ export async function getSession(options: GetSessionOptions): Promise(null); + useEffect(() => { if (!router.query?.callbackUrl) { window.history.replaceState(null, document.title, "?callbackUrl=/"); } }, [router.query]); + async function handleSubmit(e: React.SyntheticEvent) { + e.preventDefault(); + + if (isSubmitting) { + return; + } + + setIsSubmitting(true); + setErrorMessage(null); + + try { + const response = await signIn("credentials", { redirect: false, email, password, totpCode: code }); + if (!response) { + console.error("Received empty response from next auth"); + return; + } + + if (!response.error) { + window.location.reload(); + return; + } + + if (response.error === ErrorCode.SecondFactorRequired) { + setSecondFactorRequired(true); + setErrorMessage(errorMessages[ErrorCode.SecondFactorRequired]); + } else { + setErrorMessage(errorMessages[response.error] || "Something went wrong."); + } + } catch (e) { + setErrorMessage("Something went wrong."); + } finally { + setIsSubmitting(false); + } + } + return (
@@ -22,7 +75,7 @@ export default function Login({ csrfToken }) {
-
+
@@ -60,18 +115,44 @@ export default function Login({ csrfToken }) { type="password" autoComplete="current-password" required + value={password} + onInput={(e) => setPassword(e.currentTarget.value)} className="appearance-none block w-full px-3 py-2 border border-neutral-300 rounded-sm shadow-sm placeholder-gray-400 focus:outline-none focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" />
+ {secondFactorRequired && ( +
+ +
+ setCode(e.currentTarget.value)} + className="appearance-none block w-full px-3 py-2 border border-neutral-300 rounded-sm shadow-sm placeholder-gray-400 focus:outline-none focus:ring-neutral-900 focus:border-neutral-900 sm:text-sm" + /> +
+
+ )} +
+ + {errorMessage &&

{errorMessage}

}
diff --git a/pages/settings/password.tsx b/pages/settings/password.tsx deleted file mode 100644 index b681a473b4..0000000000 --- a/pages/settings/password.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useRef, useState } from "react"; -import prisma from "@lib/prisma"; -import Modal from "@components/Modal"; -import Shell from "@components/Shell"; -import SettingsShell from "@components/Settings"; -import { useSession } from "next-auth/client"; -import Loader from "@components/Loader"; -import { getSession } from "@lib/auth"; - -export default function Settings() { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [session, loading] = useSession(); - - const [successModalOpen, setSuccessModalOpen] = useState(false); - const oldPasswordRef = useRef(); - const newPasswordRef = useRef(); - - if (loading) { - return ; - } - - const closeSuccessModal = () => { - setSuccessModalOpen(false); - }; - - async function changePasswordHandler(event) { - event.preventDefault(); - - const enteredOldPassword = oldPasswordRef.current.value; - const enteredNewPassword = newPasswordRef.current.value; - - // TODO: Add validation - - /*eslint-disable */ - const response = await fetch("/api/auth/changepw", { - method: "PATCH", - body: JSON.stringify({ oldPassword: enteredOldPassword, newPassword: enteredNewPassword }), - headers: { - "Content-Type": "application/json", - }, - }); - /*eslint-enable */ - - setSuccessModalOpen(true); - } - - return ( - - -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -
-
-
- -
-
- ); -} - -export async function getServerSideProps(context) { - const session = await getSession(context); - if (!session) { - return { redirect: { permanent: false, destination: "/auth/login" } }; - } - - const user = await prisma.user.findFirst({ - where: { - email: session.user.email, - }, - select: { - id: true, - username: true, - name: true, - }, - }); - - return { - props: { user }, // will be passed to the page component as props - }; -} diff --git a/pages/settings/security.tsx b/pages/settings/security.tsx new file mode 100644 index 0000000000..0e5a1c9bfb --- /dev/null +++ b/pages/settings/security.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import prisma from "@lib/prisma"; +import Shell from "@components/Shell"; +import SettingsShell from "@components/Settings"; +import { getSession, useSession } from "next-auth/client"; +import Loader from "@components/Loader"; +import TwoFactorAuthSection from "@components/security/TwoFactorAuthSection"; +import ChangePasswordSection from "@components/security/ChangePasswordSection"; + +export default function Security({ user }) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [session, loading] = useSession(); + + if (loading) { + return ; + } + + return ( + + + + + + + ); +} + +export async function getServerSideProps(context) { + const session = await getSession(context); + if (!session) { + return { redirect: { permanent: false, destination: "/auth/login" } }; + } + + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true, + username: true, + name: true, + twoFactorEnabled: true, + }, + }); + + return { + props: { user }, // will be passed to the page component as props + }; +} diff --git a/prisma/migrations/20210918013258_add_two_factor_fields/migration.sql b/prisma/migrations/20210918013258_add_two_factor_fields/migration.sql new file mode 100644 index 0000000000..2fab5f4b40 --- /dev/null +++ b/prisma/migrations/20210918013258_add_two_factor_fields/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "users" ADD COLUMN "twoFactorEnabled" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "twoFactorSecret" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e66f9646a9..6a9bb33afd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -81,6 +81,8 @@ model User { availability Availability[] selectedCalendars SelectedCalendar[] completedOnboarding Boolean? @default(false) + twoFactorSecret String? + twoFactorEnabled Boolean @default(false) plan UserPlan @default(PRO) diff --git a/yarn.lock b/yarn.lock index 00b00f098f..66b7658a5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -798,6 +798,44 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@otplib/core@^12.0.1": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@otplib/core/-/core-12.0.1.tgz#73720a8cedce211fe5b3f683cd5a9c098eaf0f8d" + integrity sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA== + +"@otplib/plugin-crypto@^12.0.1": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz#2b42c624227f4f9303c1c041fca399eddcbae25e" + integrity sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g== + dependencies: + "@otplib/core" "^12.0.1" + +"@otplib/plugin-thirty-two@^12.0.1": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz#5cc9b56e6e89f2a1fe4a2b38900ca4e11c87aa9e" + integrity sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA== + dependencies: + "@otplib/core" "^12.0.1" + thirty-two "^1.0.2" + +"@otplib/preset-default@^12.0.1": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@otplib/preset-default/-/preset-default-12.0.1.tgz#cb596553c08251e71b187ada4a2246ad2a3165ba" + integrity sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ== + dependencies: + "@otplib/core" "^12.0.1" + "@otplib/plugin-crypto" "^12.0.1" + "@otplib/plugin-thirty-two" "^12.0.1" + +"@otplib/preset-v11@^12.0.1": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@otplib/preset-v11/-/preset-v11-12.0.1.tgz#4c7266712e7230500b421ba89252963c838fc96d" + integrity sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg== + dependencies: + "@otplib/core" "^12.0.1" + "@otplib/plugin-crypto" "^12.0.1" + "@otplib/plugin-thirty-two" "^12.0.1" + "@panva/asn1.js@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" @@ -1370,6 +1408,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== +"@types/qrcode@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.4.1.tgz#0689f400c3a95d2db040c99c99834faa09ee9dc1" + integrity sha512-vxMyr7JM7tYPxu8vUE83NiosWX5DZieCyYeJRoOIg0pAkyofCBzknJ2ycUZkPGDFis2RS8GN/BeJLnRnAPxeCA== + dependencies: + "@types/node" "*" + "@types/react-dates@^21.8.3": version "21.8.3" resolved "https://registry.yarnpkg.com/@types/react-dates/-/react-dates-21.8.3.tgz#dc4e71f83d09979b1c4f355c267e52a850d0fe2c" @@ -1658,6 +1703,11 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -1668,7 +1718,7 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -2146,6 +2196,19 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -2156,7 +2219,12 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= -buffer-from@^1.0.0: +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + +buffer-from@^1.0.0, buffer-from@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== @@ -2183,6 +2251,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.4.3: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -2224,7 +2300,7 @@ camelcase-css@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -camelcase@^5.3.1: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -2390,6 +2466,15 @@ cli-truncate@^2.1.0: slice-ansi "^3.0.0" string-width "^4.2.0" +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -2786,6 +2871,11 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -2889,6 +2979,11 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dijkstrajs@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257" + integrity sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3000,6 +3095,11 @@ emittery@^0.8.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3481,6 +3581,13 @@ find-cache-dir@3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -3618,7 +3725,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -4049,7 +4156,7 @@ ics@^2.31.0: nanoid "^3.1.23" yup "^0.32.9" -ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4245,6 +4352,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4380,6 +4492,11 @@ isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5157,6 +5274,14 @@ loader-utils@1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -5826,6 +5951,15 @@ ospath@^1.2.2: resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= +otplib@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/otplib/-/otplib-12.0.1.tgz#c1d3060ab7aadf041ed2960302f27095777d1f73" + integrity sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg== + dependencies: + "@otplib/core" "^12.0.1" + "@otplib/preset-default" "^12.0.1" + "@otplib/preset-v11" "^12.0.1" + p-each-series@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" @@ -5838,13 +5972,20 @@ p-limit@3.1.0: dependencies: yocto-queue "^0.1.0" -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -5937,6 +6078,11 @@ path-browserify@1.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -6046,6 +6192,11 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" +pngjs@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" + integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== + pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -6275,6 +6426,19 @@ purgecss@^4.0.3: postcss "^8.2.1" postcss-selector-parser "^6.0.2" +qrcode@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83" + integrity sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q== + dependencies: + buffer "^5.4.3" + buffer-alloc "^1.2.0" + buffer-from "^1.1.1" + dijkstrajs "^1.0.1" + isarray "^2.0.1" + pngjs "^3.3.0" + yargs "^13.2.4" + qs@^6.7.0: version "6.10.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" @@ -6677,6 +6841,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + require_optional@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" @@ -6832,6 +7001,11 @@ semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: dependencies: lru-cache "^6.0.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -7110,6 +7284,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -7195,6 +7378,13 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -7374,6 +7564,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +thirty-two@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a" + integrity sha1-TKL//AKlEpDSdEueP1V2k8prYno= + throat@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" @@ -7859,6 +8054,11 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + which-typed-array@^1.1.2: version "1.1.7" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" @@ -7895,6 +8095,15 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -7968,6 +8177,11 @@ xtend@^4.0.0, xtend@^4.0.2: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -7992,11 +8206,35 @@ yargonaut@^1.1.4: figlet "^1.1.1" parent-require "^1.0.0" +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs@^13.2.4: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yargs@^16.0.0, yargs@^16.0.3: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"