From 329abe98186c0996d621be6b0bc0138aee8f6c8f Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Fri, 13 Sep 2024 10:52:21 -0400 Subject: [PATCH] feat: add error states for general, passkey and wallet-connect errors (#959) *feat: added .nvmrc to enforce node version for local development * feat: auth card error states * feat: add wallet error states * feat: integrate error states to wallet connect * feat: storybook auto-docs, all auth types in story * fix: registration disclaimer opening new tab --------- Co-authored-by: rob chang --- .nvmrc | 1 + account-kit/react/.storybook/main.ts | 1 - account-kit/react/.storybook/preview.ts | 1 - account-kit/react/.storybook/preview.tsx | 59 ++++ account-kit/react/package.json | 7 + account-kit/react/public/mockServiceWorker.js | 284 ++++++++++++++++++ .../src/components/auth/card/add-passkey.tsx | 27 +- .../src/components/auth/card/content.tsx | 13 - .../react/src/components/auth/card/eoa.tsx | 34 ++- .../card/error/connection-error.stories.tsx | 29 ++ .../auth/card/error/connection-error.tsx | 78 +++++ .../auth/card/error/general-error.stories.tsx | 20 ++ .../auth/card/error/general-error.tsx | 16 + .../auth/card/error/icons/wallet-icon.tsx | 24 ++ .../src/components/auth/card/error/types.ts | 7 + .../auth/card/footer/email-not-reveived.tsx | 51 ++++ .../components/auth/card/footer/help-text.tsx | 13 + .../{ => auth/card/footer}/poweredby.tsx | 4 +- .../card/footer/registration-disclaimer.tsx | 15 + .../components/auth/card/index.stories.tsx | 144 +++++++++ .../react/src/components/auth/card/index.tsx | 55 ++-- .../components/auth/card/loading/email.tsx | 49 +-- .../components/auth/card/loading/index.tsx | 23 -- .../components/auth/card/loading/passkey.tsx | 2 - .../react/src/components/auth/card/main.tsx | 26 +- .../components/auth/card/passkey-added.tsx | 2 - .../components/auth/card/passkey.stories.tsx | 72 +++++ .../react/src/components/auth/card/steps.tsx | 11 +- .../react/src/components/auth/context.ts | 5 +- .../components/auth/sections/EmailAuth.tsx | 27 +- .../src/components/auth/sections/Footer.tsx | 38 +++ .../react/src/components/button.stories.tsx | 20 ++ .../react/src/icons/EOAConnectionFailed.tsx | 140 +++++++++ .../react/src/icons/coinbaseWallet.tsx | 38 +++ account-kit/react/src/icons/metamask.tsx | 138 +++++++++ .../src/icons/passkeyConnectionFailed.tsx | 216 +++++++++++++ account-kit/react/src/icons/timeout.tsx | 188 ++++++++++++ .../react/src/icons/walletConnectIcon.tsx | 22 ++ account-kit/react/src/icons/warning.tsx | 18 ++ account-kit/react/src/strings.ts | 19 ++ .../components/preview/AuthCardWrapper.tsx | 2 +- yarn.lock | 253 +++++++++++++++- 42 files changed, 2023 insertions(+), 169 deletions(-) create mode 100644 .nvmrc delete mode 100644 account-kit/react/.storybook/preview.ts create mode 100644 account-kit/react/.storybook/preview.tsx create mode 100644 account-kit/react/public/mockServiceWorker.js create mode 100644 account-kit/react/src/components/auth/card/error/connection-error.stories.tsx create mode 100644 account-kit/react/src/components/auth/card/error/connection-error.tsx create mode 100644 account-kit/react/src/components/auth/card/error/general-error.stories.tsx create mode 100644 account-kit/react/src/components/auth/card/error/general-error.tsx create mode 100644 account-kit/react/src/components/auth/card/error/icons/wallet-icon.tsx create mode 100644 account-kit/react/src/components/auth/card/error/types.ts create mode 100644 account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx create mode 100644 account-kit/react/src/components/auth/card/footer/help-text.tsx rename account-kit/react/src/components/{ => auth/card/footer}/poweredby.tsx (70%) create mode 100644 account-kit/react/src/components/auth/card/footer/registration-disclaimer.tsx create mode 100644 account-kit/react/src/components/auth/card/index.stories.tsx delete mode 100644 account-kit/react/src/components/auth/card/loading/index.tsx create mode 100644 account-kit/react/src/components/auth/card/passkey.stories.tsx create mode 100644 account-kit/react/src/components/auth/sections/Footer.tsx create mode 100644 account-kit/react/src/icons/EOAConnectionFailed.tsx create mode 100644 account-kit/react/src/icons/coinbaseWallet.tsx create mode 100644 account-kit/react/src/icons/metamask.tsx create mode 100644 account-kit/react/src/icons/passkeyConnectionFailed.tsx create mode 100644 account-kit/react/src/icons/timeout.tsx create mode 100644 account-kit/react/src/icons/walletConnectIcon.tsx create mode 100644 account-kit/react/src/icons/warning.tsx diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..2cc859384 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.8.0 \ No newline at end of file diff --git a/account-kit/react/.storybook/main.ts b/account-kit/react/.storybook/main.ts index 189ee3500..0e98c3403 100644 --- a/account-kit/react/.storybook/main.ts +++ b/account-kit/react/.storybook/main.ts @@ -1,5 +1,4 @@ import type { StorybookConfig } from "@storybook/react-vite"; - import react from "@vitejs/plugin-react"; import { mergeConfig } from "vite"; diff --git a/account-kit/react/.storybook/preview.ts b/account-kit/react/.storybook/preview.ts deleted file mode 100644 index d3349be43..000000000 --- a/account-kit/react/.storybook/preview.ts +++ /dev/null @@ -1 +0,0 @@ -import "./tailwind.css"; diff --git a/account-kit/react/.storybook/preview.tsx b/account-kit/react/.storybook/preview.tsx new file mode 100644 index 000000000..d42be8d29 --- /dev/null +++ b/account-kit/react/.storybook/preview.tsx @@ -0,0 +1,59 @@ +import "./tailwind.css"; + +import type { Preview } from "@storybook/react"; +import { initialize, mswLoader } from "msw-storybook-addon"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { AlchemyAccountProvider, createConfig } from "../src"; +import React, { useEffect } from "react"; +import { sepolia } from "@account-kit/infra"; + +const queryClient = new QueryClient(); + +initialize(); + +const config = createConfig( + { + rpcUrl: "/api/rpc", + chain: sepolia, + ssr: true, + }, + { + illustrationStyle: "outline", + auth: { + sections: [[{ type: "email" as const }], [{ type: "passkey" as const }]], + addPasskeyOnSignup: true, + }, + } +); + +const preview: Preview = { + decorators: [ + (Story, { args }) => { + // Sync CSS variables + useEffect(() => { + const root = document.querySelector(":root") as HTMLElement; + if (args.isLight === false) { + root.classList.remove("light"); + root.classList.add("dark"); + } else { + root.classList.remove("dark"); + root.classList.add("light"); + } + }, [args]); + return ( + + + + + + ); + }, + ], + args: { + isLight: true, + }, + loaders: [mswLoader], + tags: ["autodocs"], +}; + +export default preview; diff --git a/account-kit/react/package.json b/account-kit/react/package.json index b4d725488..909e460a7 100644 --- a/account-kit/react/package.json +++ b/account-kit/react/package.json @@ -56,6 +56,8 @@ "@storybook/testing-library": "^0.2.2", "@tanstack/react-query": "^5.28.9", "autoprefixer": "^10.4.20", + "msw": "^2.4.4", + "msw-storybook-addon": "^2.0.3", "postcss": "^8.4.45", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -101,5 +103,10 @@ "gitHead": "ee46e8bb857de3b631044fa70714ea706d9e317d", "optionalDependencies": { "alchemy-sdk": "^3.0.0" + }, + "msw": { + "workerDirectory": [ + "public" + ] } } diff --git a/account-kit/react/public/mockServiceWorker.js b/account-kit/react/public/mockServiceWorker.js new file mode 100644 index 000000000..4725e55aa --- /dev/null +++ b/account-kit/react/public/mockServiceWorker.js @@ -0,0 +1,284 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const PACKAGE_VERSION = "2.4.4"; +const INTEGRITY_CHECKSUM = "26357c79639bfa20d64c0efca2a87423"; +const IS_MOCKED_RESPONSE = Symbol("isMockedResponse"); +const activeClientIds = new Set(); + +self.addEventListener("install", function () { + self.skipWaiting(); +}); + +self.addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("message", async function (event) { + const clientId = event.source.id; + + if (!clientId || !self.clients) { + return; + } + + const client = await self.clients.get(clientId); + + if (!client) { + return; + } + + const allClients = await self.clients.matchAll({ + type: "window", + }); + + switch (event.data) { + case "KEEPALIVE_REQUEST": { + sendToClient(client, { + type: "KEEPALIVE_RESPONSE", + }); + break; + } + + case "INTEGRITY_CHECK_REQUEST": { + sendToClient(client, { + type: "INTEGRITY_CHECK_RESPONSE", + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }); + break; + } + + case "MOCK_ACTIVATE": { + activeClientIds.add(clientId); + + sendToClient(client, { + type: "MOCKING_ENABLED", + payload: true, + }); + break; + } + + case "MOCK_DEACTIVATE": { + activeClientIds.delete(clientId); + break; + } + + case "CLIENT_CLOSED": { + activeClientIds.delete(clientId); + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId; + }); + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister(); + } + + break; + } + } +}); + +self.addEventListener("fetch", function (event) { + const { request } = event; + + // Bypass navigation requests. + if (request.mode === "navigate") { + return; + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === "only-if-cached" && request.mode !== "same-origin") { + return; + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return; + } + + // Generate unique request ID. + const requestId = crypto.randomUUID(); + event.respondWith(handleRequest(event, requestId)); +}); + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event); + const response = await getResponse(event, client, requestId); + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + (async function () { + const responseClone = response.clone(); + + sendToClient( + client, + { + type: "RESPONSE", + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body] + ); + })(); + } + + return response; +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId); + + if (client?.frameType === "top-level") { + return client; + } + + const allClients = await self.clients.matchAll({ + type: "window", + }); + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === "visible"; + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id); + }); +} + +async function getResponse(event, client, requestId) { + const { request } = event; + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone(); + + function passthrough() { + const headers = Object.fromEntries(requestClone.headers.entries()); + + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers["x-msw-intention"]; + + return fetch(requestClone, { headers }); + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough(); + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough(); + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer(); + const clientMessage = await sendToClient( + client, + { + type: "REQUEST", + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer] + ); + + switch (clientMessage.type) { + case "MOCK_RESPONSE": { + return respondWithMock(clientMessage.data); + } + + case "PASSTHROUGH": { + return passthrough(); + } + } + + return passthrough(); +} + +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error); + } + + resolve(event.data); + }; + + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)) + ); + }); +} + +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error(); + } + + const mockedResponse = new Response(response.body, response); + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }); + + return mockedResponse; +} diff --git a/account-kit/react/src/components/auth/card/add-passkey.tsx b/account-kit/react/src/components/auth/card/add-passkey.tsx index f458a860a..1e0929a1f 100644 --- a/account-kit/react/src/components/auth/card/add-passkey.tsx +++ b/account-kit/react/src/components/auth/card/add-passkey.tsx @@ -6,8 +6,8 @@ import { } from "../../../icons/illustrations/passkeys.js"; import { ls } from "../../../strings.js"; import { Button } from "../../button.js"; -import { PoweredBy } from "../../poweredby.js"; -import { useAuthContext } from "../context.js"; +import { useAuthContext, type AuthStep } from "../context.js"; +import { ConnectionError } from "./error/connection-error.js"; const BENEFITS = [ { @@ -22,15 +22,35 @@ const BENEFITS = [ }, ]; +type AddPasskeyProps = { + authStep: Extract; +}; + // eslint-disable-next-line jsdoc/require-jsdoc -export const AddPasskey = () => { +export const AddPasskey = ({ authStep }: AddPasskeyProps) => { const { setAuthStep } = useAuthContext(); const { addPasskey, isAddingPasskey } = useAddPasskey({ onSuccess: () => { setAuthStep({ type: "passkey_create_success" }); }, + onError: () => { + setAuthStep({ + type: "passkey_create", + error: new Error("Failed to add passkey"), + }); + }, }); + if (authStep.error) { + return ( + setAuthStep({ type: "complete" })} + /> + ); + } + return (
@@ -69,7 +89,6 @@ export const AddPasskey = () => { {ls.addPasskey.skip}
-
); }; diff --git a/account-kit/react/src/components/auth/card/content.tsx b/account-kit/react/src/components/auth/card/content.tsx index b6bc820a1..f5c820300 100644 --- a/account-kit/react/src/components/auth/card/content.tsx +++ b/account-kit/react/src/components/auth/card/content.tsx @@ -1,14 +1,9 @@ import type { ReactNode } from "react"; -import { PoweredBy } from "../../poweredby.js"; interface CardContentProps { header: ReactNode | string; icon?: ReactNode; description: ReactNode | string; - support?: { - text: string; - cta: ReactNode; - }; error?: Error | string; className?: string; } @@ -18,7 +13,6 @@ export const CardContent = ({ header, icon, description, - support, className, }: CardContentProps) => { return ( @@ -36,13 +30,6 @@ export const CardContent = ({ ) : ( description )} - {support && ( -
- {support.text} - {support.cta} -
- )} - ); }; diff --git a/account-kit/react/src/components/auth/card/eoa.tsx b/account-kit/react/src/components/auth/card/eoa.tsx index 3dc847b0d..2d2733291 100644 --- a/account-kit/react/src/components/auth/card/eoa.tsx +++ b/account-kit/react/src/components/auth/card/eoa.tsx @@ -3,11 +3,13 @@ import { walletConnect } from "wagmi/connectors"; import { useChain } from "../../../hooks/useChain.js"; import { useConnect } from "../../../hooks/useConnect.js"; import { useUiConfig } from "../../../hooks/useUiConfig.js"; -import { WalletConnectIcon } from "../../../icons/walletConnect.js"; +import { WalletConnectIcon } from "../../../icons/walletConnectIcon.js"; import { Button } from "../../button.js"; import { useAuthContext, type AuthStep } from "../context.js"; import type { AuthType } from "../types.js"; import { CardContent } from "./content.js"; +import { Spinner } from "../../../icons/spinner.js"; +import { ConnectionError } from "./error/connection-error.js"; interface Props { authStep: Extract; @@ -38,15 +40,33 @@ type WalletConnectCardProps = { }; export const WalletConnectCard = ({ authStep }: WalletConnectCardProps) => { + const { setAuthStep } = useAuthContext(); + + if (authStep.error) { + return ( + setAuthStep({ type: "wallet_connect" })} + handleUseAnotherMethod={() => setAuthStep({ type: "pick_eoa" })} + /> + ); + } + // If error render the error card here? return ( +
+ +
+ +
+
} description="Please follow the instructions in the popup to connect." error={authStep.error} @@ -132,7 +152,7 @@ export const EoaPickCard = () => { + + + ); +}; diff --git a/account-kit/react/src/components/auth/card/error/general-error.stories.tsx b/account-kit/react/src/components/auth/card/error/general-error.stories.tsx new file mode 100644 index 000000000..0c59e7117 --- /dev/null +++ b/account-kit/react/src/components/auth/card/error/general-error.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { GeneralError } from "./general-error.js"; + +const Container = () => { + return ( +
+
+ +
+
+ ); +}; +const meta: Meta = { + title: "Errors/General Error", + component: Container, +}; +export default meta; + +type Story = StoryObj; +export const Default: Story = {}; diff --git a/account-kit/react/src/components/auth/card/error/general-error.tsx b/account-kit/react/src/components/auth/card/error/general-error.tsx new file mode 100644 index 000000000..3d00673b6 --- /dev/null +++ b/account-kit/react/src/components/auth/card/error/general-error.tsx @@ -0,0 +1,16 @@ +import { Warning } from "../../../../icons/warning.js"; +import { ls } from "../../../../strings.js"; + +export const GeneralError = () => { + return ( +
+ +
+

+ {ls.error.general.title} +

+

{ls.error.general.body}

+
+
+ ); +}; diff --git a/account-kit/react/src/components/auth/card/error/icons/wallet-icon.tsx b/account-kit/react/src/components/auth/card/error/icons/wallet-icon.tsx new file mode 100644 index 000000000..944871e3d --- /dev/null +++ b/account-kit/react/src/components/auth/card/error/icons/wallet-icon.tsx @@ -0,0 +1,24 @@ +import { CoinbaseWallet } from "../../../../../icons/coinbaseWallet.js"; +import { EOAConnectionFailed } from "../../../../../icons/EOAConnectionFailed.js"; +import { MetaMask } from "../../../../../icons/metamask.js"; +import { WalletConnectIcon } from "../../../../../icons/walletConnectIcon.js"; +import type { WalletType } from "../types.js"; + +export const WalletIcon = ({ walletType }: { walletType: WalletType }) => { + return ( +
+ +
+ {walletType === "MetaMask" && ( + + )} + {walletType === "WalletConnect" && ( + + )} + {walletType === "CoinbaseWallet" && ( + + )} +
+
+ ); +}; diff --git a/account-kit/react/src/components/auth/card/error/types.ts b/account-kit/react/src/components/auth/card/error/types.ts new file mode 100644 index 000000000..76a46efc3 --- /dev/null +++ b/account-kit/react/src/components/auth/card/error/types.ts @@ -0,0 +1,7 @@ +export type WalletType = "CoinbaseWallet" | "MetaMask" | "WalletConnect"; +export type ConnectionErrorProps = { + connectionType: "passkey" | "wallet" | "timeout"; + walletType?: WalletType; + handleTryAgain?: () => void; + handleUseAnotherMethod?: () => void; +}; diff --git a/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx b/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx new file mode 100644 index 000000000..648d8ea3f --- /dev/null +++ b/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from "react"; +import { useAuthenticate } from "../../../../hooks/useAuthenticate.js"; +import { ls } from "../../../../strings.js"; +import { useAuthContext, type AuthStep } from "../../context.js"; +import { Button } from "../../../button.js"; + +type EmailNotReceivedDisclaimerProps = { + authStep: Extract; +}; +export const EmailNotReceivedDisclaimer = ({ + authStep, +}: EmailNotReceivedDisclaimerProps) => { + const { setAuthStep } = useAuthContext(); + const [emailResent, setEmailResent] = useState(false); + const { authenticate } = useAuthenticate({ + onSuccess: () => { + setAuthStep({ type: "complete" }); + }, + }); + + useEffect(() => { + if (emailResent) { + // set the text back to "Resend" after 2 seconds + setTimeout(() => { + setEmailResent(false); + }, 2000); + } + }, [emailResent]); + + return ( +
+ + {ls.loadingEmail.emailNotReceived} + + +
+ ); +}; diff --git a/account-kit/react/src/components/auth/card/footer/help-text.tsx b/account-kit/react/src/components/auth/card/footer/help-text.tsx new file mode 100644 index 000000000..bd2c68ddc --- /dev/null +++ b/account-kit/react/src/components/auth/card/footer/help-text.tsx @@ -0,0 +1,13 @@ +import { ls } from "../../../../strings.js"; + +export const HelpText = () => ( +
+ + {ls.loadingPasskey.supportText} + + {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} + + {ls.loadingPasskey.supportLink} + +
+); diff --git a/account-kit/react/src/components/poweredby.tsx b/account-kit/react/src/components/auth/card/footer/poweredby.tsx similarity index 70% rename from account-kit/react/src/components/poweredby.tsx rename to account-kit/react/src/components/auth/card/footer/poweredby.tsx index ee71cc0b0..c4eb157a9 100644 --- a/account-kit/react/src/components/poweredby.tsx +++ b/account-kit/react/src/components/auth/card/footer/poweredby.tsx @@ -1,5 +1,5 @@ -import { AlchemyLogo } from "../icons/alchemy.js"; -import { ls } from "../strings.js"; +import { AlchemyLogo } from "../../../../icons/alchemy.js"; +import { ls } from "../../../../strings.js"; // eslint-disable-next-line jsdoc/require-jsdoc export const PoweredBy = () => ( diff --git a/account-kit/react/src/components/auth/card/footer/registration-disclaimer.tsx b/account-kit/react/src/components/auth/card/footer/registration-disclaimer.tsx new file mode 100644 index 000000000..019cf3277 --- /dev/null +++ b/account-kit/react/src/components/auth/card/footer/registration-disclaimer.tsx @@ -0,0 +1,15 @@ +import { ls } from "../../../../strings.js"; + +export const RegistrationDisclaimer = () => ( +
+ {ls.login.tosPrefix} + + {ls.login.tosLink} + +
+); diff --git a/account-kit/react/src/components/auth/card/index.stories.tsx b/account-kit/react/src/components/auth/card/index.stories.tsx new file mode 100644 index 000000000..8870e9b2f --- /dev/null +++ b/account-kit/react/src/components/auth/card/index.stories.tsx @@ -0,0 +1,144 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { AuthCard } from "./index.jsx"; +import { useUiConfig } from "../../../hooks/useUiConfig.js"; +import type { AuthType } from "../types.js"; +import { useEffect } from "react"; +import { http, HttpResponse } from "msw"; +const Test = (props: any) => { + const { updateConfig } = useUiConfig(); + let sections: AuthType[][] = [ + [{ type: "email" as const }], + [{ type: "passkey" as const }], + ]; + if (props.authType === "email") { + sections = [[{ type: "email" as const }]]; + } + if (props.authType === "passkey") { + sections = [[{ type: "passkey" as const }]]; + } + if (props.authType === "email-passkey") { + sections = [[{ type: "email" as const }], [{ type: "passkey" as const }]]; + } + if (props.authType === "external_wallets") { + sections = [ + [ + { + type: "external_wallets", + walletConnect: { + projectId: "30e7ffaff99063e68cc9870c105d905b", + }, + }, + ], + ]; + } + if (props.authType === "all") { + sections = [ + [{ type: "email" as const }], + [{ type: "passkey" as const }], + [ + { + type: "external_wallets", + walletConnect: { + projectId: "30e7ffaff99063e68cc9870c105d905b", + }, + }, + ], + ]; + } + + const ui = { + theme: "dark", + primaryColor: { + light: "red", + dark: "#9AB7FF", + }, + borderRadius: "sm", + illustrationStyle: "outline", + logoLight: undefined, + logoDark: undefined, + }; + useEffect(() => { + const uiConfig = { + auth: { + showEmail: true, + showExternalWallets: false, + showPasskey: true, + addPasskey: true, + sections, + }, + ui, + }; + + updateConfig(uiConfig); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.authType]); + + return ( +
+ +
+ ); +}; + +const meta: Meta = { + title: "AuthCard", + component: Test, + argTypes: { + authType: { + options: ["email", "passkey", "email-passkey", "external_wallets", "all"], + control: { type: "radio" }, + }, + }, + args: { + authType: "email-passkey", + }, + parameters: { + msw: { + handlers: [ + http.post("/api/rpc/signer/v1/lookup", () => { + return HttpResponse.json({ + orgId: "483c1263-a6e3-4db1-a8b0-894e4902e404", + }); + }), + ], + }, + }, +}; +export default meta; + +type Story = StoryObj; +export const Default: Story = { + args: { + authType: "email", + }, +}; + +export const withLookupError: Story = { + parameters: { + msw: { + handlers: [ + http.post("/api/rpc/signer/v1/lookup", () => { + return new HttpResponse(null, { + status: 500, + statusText: "MSW server error from Storybook", + }); + }), + ], + }, + }, +}; + +export const withSignupError: Story = { + parameters: { + msw: { + handlers: [ + http.post("/api/rpc/signer/v1/signup", () => { + return new HttpResponse(null, { + status: 500, + statusText: "MSW server error from Storybook", + }); + }), + ], + }, + }, +}; diff --git a/account-kit/react/src/components/auth/card/index.tsx b/account-kit/react/src/components/auth/card/index.tsx index 62b809b99..35751e7c7 100644 --- a/account-kit/react/src/components/auth/card/index.tsx +++ b/account-kit/react/src/components/auth/card/index.tsx @@ -1,8 +1,6 @@ "use client"; import { useCallback, useLayoutEffect, useMemo, useRef } from "react"; -import { BaseError } from "viem"; -import { useAuthError } from "../../../hooks/useAuthError.js"; import { useAuthModal } from "../../../hooks/useAuthModal.js"; import { useElementHeight } from "../../../hooks/useElementHeight.js"; import { useSigner } from "../../../hooks/useSigner.js"; @@ -10,9 +8,9 @@ import { useSignerStatus } from "../../../hooks/useSignerStatus.js"; import { useUiConfig } from "../../../hooks/useUiConfig.js"; import { IS_SIGNUP_QP } from "../../constants.js"; import { Navigation } from "../../navigation.js"; -import { Notification } from "../../notification.js"; import { useAuthContext } from "../context.js"; import { Step } from "./steps.js"; +import { Footer } from "../sections/Footer.js"; export type AuthCardProps = { className?: string; @@ -58,7 +56,6 @@ export const AuthCardContent = ({ const { authStep, setAuthStep } = useAuthContext(); const signer = useSigner(); - const error = useAuthError(); const contentRef = useRef(null); const { height } = useElementHeight(contentRef); @@ -66,13 +63,14 @@ export const AuthCardContent = ({ const didGoBack = useRef(false); const { - auth: { hideError, onAuthSuccess }, + auth: { onAuthSuccess }, } = useUiConfig(); const canGoBack = useMemo(() => { return [ "email_verify", "passkey_verify", + "passkey_create", "pick_eoa", "wallet_connect", "eoa_connect", @@ -83,6 +81,7 @@ export const AuthCardContent = ({ switch (authStep.type) { case "email_verify": case "passkey_verify": + case "passkey_create": signer?.disconnect(); // Terminate any inflight authentication didGoBack.current = true; setAuthStep({ type: "initial" }); @@ -126,40 +125,28 @@ export const AuthCardContent = ({ return (
-
- {!hideError && error && error.message && ( - - )} -
- {/* Wrapper container that sizes its height dynamically */}
-
- {(canGoBack || showClose) && ( - - )} - +
+
+ {(canGoBack || showClose) && ( + + )} + +
+
diff --git a/account-kit/react/src/components/auth/card/loading/email.tsx b/account-kit/react/src/components/auth/card/loading/email.tsx index c74e1e49b..38f995821 100644 --- a/account-kit/react/src/components/auth/card/loading/email.tsx +++ b/account-kit/react/src/components/auth/card/loading/email.tsx @@ -1,29 +1,19 @@ import { useEffect, useState } from "react"; -import { useAuthenticate } from "../../../../hooks/useAuthenticate.js"; import { useSignerStatus } from "../../../../hooks/useSignerStatus.js"; -import { Button } from "../../../button.js"; -import { PoweredBy } from "../../../poweredby.js"; import { useAuthContext, type AuthStep } from "../../context.js"; import { Spinner } from "../../../../icons/spinner.js"; import { ls } from "../../../../strings.js"; import { EmailIllustration } from "../../../../icons/illustrations/email.js"; interface LoadingEmailProps { - context: Extract; + authStep: Extract; } // eslint-disable-next-line jsdoc/require-jsdoc -export const LoadingEmail = ({ context }: LoadingEmailProps) => { +export const LoadingEmail = ({ authStep }: LoadingEmailProps) => { // yup, re-sent and resent. I'm not fixing it const [emailResent, setEmailResent] = useState(false); - const { setAuthStep } = useAuthContext(); - const { authenticate } = useAuthenticate({ - onSuccess: () => { - setAuthStep({ type: "complete" }); - }, - }); - useEffect(() => { if (emailResent) { // set the text back to "Resend" after 2 seconds @@ -43,51 +33,28 @@ export const LoadingEmail = ({ context }: LoadingEmailProps) => {

{ls.loadingEmail.verificationSent}
- {context.email} + {authStep.email}

- -
-
-

- {ls.loadingEmail.emailNotReceived} -

- -
- -
); }; interface CompletingEmailAuthProps { - context: Extract; + authStep: Extract; } // eslint-disable-next-line jsdoc/require-jsdoc -export const CompletingEmailAuth = ({ context }: CompletingEmailAuthProps) => { +export const CompletingEmailAuth = ({ authStep }: CompletingEmailAuthProps) => { const { isConnected } = useSignerStatus(); const { setAuthStep } = useAuthContext(); useEffect(() => { - if (isConnected && context.createPasskeyAfter) { + if (isConnected && authStep.createPasskeyAfter) { setAuthStep({ type: "passkey_create" }); } else if (isConnected) { setAuthStep({ type: "complete" }); } - }, [context.createPasskeyAfter, isConnected, setAuthStep]); + }, [authStep.createPasskeyAfter, isConnected, setAuthStep]); return (
@@ -98,8 +65,6 @@ export const CompletingEmailAuth = ({ context }: CompletingEmailAuthProps) => {

{ls.completingEmail.body}

- -
); }; diff --git a/account-kit/react/src/components/auth/card/loading/index.tsx b/account-kit/react/src/components/auth/card/loading/index.tsx deleted file mode 100644 index 45ce1f1ad..000000000 --- a/account-kit/react/src/components/auth/card/loading/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import type { AuthStep } from "../../context.js"; -import { CompletingEmailAuth, LoadingEmail } from "./email.js"; -import { LoadingPasskeyAuth } from "./passkey.js"; - -type LoadingAuthProps = { - context?: AuthStep; -}; - -// eslint-disable-next-line jsdoc/require-jsdoc -export const LoadingAuth = ({ context }: LoadingAuthProps) => { - switch (context?.type) { - case "email_verify": - return ; - case "passkey_verify": - return ; - case "email_completing": - return ; - default: { - console.warn("Unhandled loading state! rendering empty state", context); - return null; - } - } -}; diff --git a/account-kit/react/src/components/auth/card/loading/passkey.tsx b/account-kit/react/src/components/auth/card/loading/passkey.tsx index 8564affe2..ba6a0c203 100644 --- a/account-kit/react/src/components/auth/card/loading/passkey.tsx +++ b/account-kit/react/src/components/auth/card/loading/passkey.tsx @@ -1,6 +1,5 @@ import { ls } from "../../../../strings.js"; import { LoadingPasskey } from "../../../../icons/passkey.js"; -import { PoweredBy } from "../../../poweredby.js"; // eslint-disable-next-line jsdoc/require-jsdoc export const LoadingPasskeyAuth = () => { @@ -25,7 +24,6 @@ export const LoadingPasskeyAuth = () => { {ls.loadingPasskey.supportLink} */} - ); diff --git a/account-kit/react/src/components/auth/card/main.tsx b/account-kit/react/src/components/auth/card/main.tsx index 0f18cc9f6..35986f0ec 100644 --- a/account-kit/react/src/components/auth/card/main.tsx +++ b/account-kit/react/src/components/auth/card/main.tsx @@ -1,12 +1,17 @@ import { Fragment } from "react"; import { useUiConfig } from "../../../hooks/useUiConfig.js"; -import { ls } from "../../../strings.js"; import { Divider } from "../../divider.js"; -import { PoweredBy } from "../../poweredby.js"; import { AuthSection } from "../sections/AuthSection.js"; +import { GeneralError } from "./error/general-error.js"; +import { type AuthStep } from "../context.js"; + +type MainAuthContentProps = { + authStep: AuthStep; +}; // eslint-disable-next-line jsdoc/require-jsdoc -export const MainAuthContent = () => { +export const MainAuthContent = ({ authStep }: MainAuthContentProps) => { + const isError = authStep.type === "initial" && authStep.error; const { auth: { header, sections, hideSignInText }, } = useUiConfig(); @@ -15,6 +20,7 @@ export const MainAuthContent = () => { <> {header} {!hideSignInText &&

Sign in

} + {isError && } {sections?.map((section, idx) => { return ( @@ -25,20 +31,6 @@ export const MainAuthContent = () => { ); })} -
-

- {`${ls.login.tosPrefix} `} - - {ls.login.tosLink} - -

- -
); }; diff --git a/account-kit/react/src/components/auth/card/passkey-added.tsx b/account-kit/react/src/components/auth/card/passkey-added.tsx index 70c629b33..1a887d7f6 100644 --- a/account-kit/react/src/components/auth/card/passkey-added.tsx +++ b/account-kit/react/src/components/auth/card/passkey-added.tsx @@ -1,5 +1,4 @@ import { AddedPasskeyIllustration } from "../../../icons/illustrations/added-passkey.js"; -import { PoweredBy } from "../../poweredby.js"; // eslint-disable-next-line jsdoc/require-jsdoc export function PasskeyAdded() { @@ -12,7 +11,6 @@ export function PasskeyAdded() {

You can use this passkey to sign in next time.

- ); } diff --git a/account-kit/react/src/components/auth/card/passkey.stories.tsx b/account-kit/react/src/components/auth/card/passkey.stories.tsx new file mode 100644 index 000000000..476054781 --- /dev/null +++ b/account-kit/react/src/components/auth/card/passkey.stories.tsx @@ -0,0 +1,72 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { AuthCard } from "./index.jsx"; +import { useUiConfig } from "../../../hooks/useUiConfig.js"; +import type { AuthType } from "../types.js"; +import { useEffect } from "react"; +import { http, HttpResponse } from "msw"; +import { useAuthContext } from "../context.js"; + +const PasskeyStory = (props: any) => { + const { updateConfig } = useUiConfig(); + let sections: AuthType[][] = [[{ type: "passkey" as const }]]; + + const ui = { + theme: "dark", + primaryColor: { + light: "red", + dark: "#9AB7FF", + }, + borderRadius: "sm", + illustrationStyle: "outline", + logoLight: undefined, + logoDark: undefined, + }; + + useEffect(() => { + const uiConfig = { + auth: { + showEmail: true, + showExternalWallets: false, + showPasskey: true, + addPasskey: true, + sections, + }, + ui, + }; + + updateConfig(uiConfig); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.authType]); + + const { setAuthStep } = useAuthContext(); + useEffect(() => { + setAuthStep({ type: "passkey_create" }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ; +}; + +const meta: Meta = { + title: "Passkey", + component: PasskeyStory, + args: { + authType: "passkey", + }, + + parameters: { + msw: { + handlers: [ + http.post("/api/rpc/signer/v1/lookup", () => { + return HttpResponse.json({ + orgId: "483c1263-a6e3-4db1-a8b0-894e4902e404", + }); + }), + ], + }, + }, +}; +export default meta; + +type Story = StoryObj; +export const Default: Story = {}; diff --git a/account-kit/react/src/components/auth/card/steps.tsx b/account-kit/react/src/components/auth/card/steps.tsx index 70abc9f61..b923e7d1c 100644 --- a/account-kit/react/src/components/auth/card/steps.tsx +++ b/account-kit/react/src/components/auth/card/steps.tsx @@ -1,7 +1,8 @@ import { useAuthContext } from "../context.js"; import { AddPasskey } from "./add-passkey.js"; import { EoaConnectCard, EoaPickCard, WalletConnectCard } from "./eoa.js"; -import { LoadingAuth } from "./loading/index.js"; +import { LoadingEmail, CompletingEmailAuth } from "./loading/email.js"; +import { LoadingPasskeyAuth } from "./loading/passkey.js"; import { MainAuthContent } from "./main.js"; import { PasskeyAdded } from "./passkey-added.js"; @@ -11,11 +12,13 @@ export const Step = () => { switch (authStep.type) { case "email_verify": + return ; case "passkey_verify": + return ; case "email_completing": - return ; + return ; case "passkey_create": - return ; + return ; case "passkey_create_success": return ; case "eoa_connect": @@ -27,6 +30,6 @@ export const Step = () => { case "complete": case "initial": default: - return ; + return ; } }; diff --git a/account-kit/react/src/components/auth/context.ts b/account-kit/react/src/components/auth/context.ts index 645f72252..098a4c78d 100644 --- a/account-kit/react/src/components/auth/context.ts +++ b/account-kit/react/src/components/auth/context.ts @@ -6,10 +6,10 @@ import { createContext, useContext } from "react"; export type AuthStep = | { type: "email_verify"; email: string } | { type: "passkey_verify"; error?: Error } - | { type: "passkey_create" } + | { type: "passkey_create"; error?: Error } | { type: "passkey_create_success" } | { type: "email_completing"; createPasskeyAfter?: boolean } - | { type: "initial" } + | { type: "initial"; error?: Error } | { type: "complete" } | { type: "eoa_connect"; connector: Connector; error?: Error } | { type: "wallet_connect"; error?: Error } @@ -28,6 +28,7 @@ export const AuthModalContext = createContext( // eslint-disable-next-line jsdoc/require-jsdoc export const useAuthContext = (): AuthContextType => { const context = useContext(AuthModalContext); + if (!context) { throw new Error( "useAuthModalContext must be used within a AuthModalProvider" diff --git a/account-kit/react/src/components/auth/sections/EmailAuth.tsx b/account-kit/react/src/components/auth/sections/EmailAuth.tsx index 1e7c3a233..cafc2a86e 100644 --- a/account-kit/react/src/components/auth/sections/EmailAuth.tsx +++ b/account-kit/react/src/components/auth/sections/EmailAuth.tsx @@ -33,10 +33,8 @@ export const EmailAuth = ({ setAuthStep({ type: "complete" }); }, onError: (error) => { - // TODO: need to handle this and show it to the user console.error(error); - // TODO: need to pass this error along - setAuthStep({ type: "initial" }); + setAuthStep({ type: "initial", error }); }, }); @@ -45,13 +43,24 @@ export const EmailAuth = ({ email: "", }, onSubmit: async ({ value: { email } }) => { - const existingUser = await signer?.getUser(email); - const redirectParams = new URLSearchParams(); - if (existingUser == null) { - redirectParams.set(IS_SIGNUP_QP, "true"); - } + try { + const existingUser = await signer?.getUser(email); + const redirectParams = new URLSearchParams(); + + if (existingUser == null) { + redirectParams.set(IS_SIGNUP_QP, "true"); + } - await authenticateAsync({ type: "email", email, redirectParams }); + await authenticateAsync({ + type: "email", + email, + redirectParams, + }); + } catch (e) { + const error = e instanceof Error ? e : new Error("An Unknown error"); + + setAuthStep({ type: "initial", error }); + } }, validatorAdapter: zodValidator, }); diff --git a/account-kit/react/src/components/auth/sections/Footer.tsx b/account-kit/react/src/components/auth/sections/Footer.tsx new file mode 100644 index 000000000..8cd16ceff --- /dev/null +++ b/account-kit/react/src/components/auth/sections/Footer.tsx @@ -0,0 +1,38 @@ +import { EmailNotReceivedDisclaimer } from "../card/footer/email-not-reveived.js"; +import { HelpText } from "../card/footer/help-text.js"; +import { PoweredBy } from "../card/footer/poweredby.js"; +import { RegistrationDisclaimer } from "../card/footer/registration-disclaimer.js"; +import type { AuthStep } from "../context.js"; + +type FooterProps = { + authStep: AuthStep; +}; + +const RenderFooterText = ({ authStep }: FooterProps) => { + switch (authStep.type) { + case "initial": + return ; + case "email_verify": + return ; + case "passkey_create": + case "wallet_connect": + case "passkey_verify": + return ; + case "email_completing": + case "passkey_create_success": + case "eoa_connect": + case "pick_eoa": + case "complete": + return null; + } +}; +export const Footer = ({ authStep }: FooterProps) => { + return ( +
+ +
+ +
+
+ ); +}; diff --git a/account-kit/react/src/components/button.stories.tsx b/account-kit/react/src/components/button.stories.tsx index 08f83b2db..d412d9803 100644 --- a/account-kit/react/src/components/button.stories.tsx +++ b/account-kit/react/src/components/button.stories.tsx @@ -4,6 +4,26 @@ import { Button } from "./button.js"; const meta: Meta = { component: Button, title: "Button", + args: { + variant: "primary", + disabled: false, + }, + argTypes: { + variant: { + control: { + type: "radio", + options: ["primary", "secondary", "social"], + }, + }, + disabled: { + control: { + type: "boolean", + }, + }, + onClick: { + action: "clicked", + }, + }, }; export default meta; type Story = StoryObj; diff --git a/account-kit/react/src/icons/EOAConnectionFailed.tsx b/account-kit/react/src/icons/EOAConnectionFailed.tsx new file mode 100644 index 000000000..301bfded0 --- /dev/null +++ b/account-kit/react/src/icons/EOAConnectionFailed.tsx @@ -0,0 +1,140 @@ +export const EOAConnectionFailed = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/account-kit/react/src/icons/coinbaseWallet.tsx b/account-kit/react/src/icons/coinbaseWallet.tsx new file mode 100644 index 000000000..f052d6fb9 --- /dev/null +++ b/account-kit/react/src/icons/coinbaseWallet.tsx @@ -0,0 +1,38 @@ +export const CoinbaseWallet = ({ + className, + ...props +}: React.JSX.IntrinsicAttributes & React.SVGProps) => ( +
+ + + + + + + + + + + +
+); diff --git a/account-kit/react/src/icons/metamask.tsx b/account-kit/react/src/icons/metamask.tsx new file mode 100644 index 000000000..6b0c8839c --- /dev/null +++ b/account-kit/react/src/icons/metamask.tsx @@ -0,0 +1,138 @@ +export const MetaMask = ({ + className, + ...props +}: React.JSX.IntrinsicAttributes & React.SVGProps) => { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; diff --git a/account-kit/react/src/icons/passkeyConnectionFailed.tsx b/account-kit/react/src/icons/passkeyConnectionFailed.tsx new file mode 100644 index 000000000..be8b61c4e --- /dev/null +++ b/account-kit/react/src/icons/passkeyConnectionFailed.tsx @@ -0,0 +1,216 @@ +export const PasskeyConnectionFailed = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/account-kit/react/src/icons/timeout.tsx b/account-kit/react/src/icons/timeout.tsx new file mode 100644 index 000000000..3a7d1c0a7 --- /dev/null +++ b/account-kit/react/src/icons/timeout.tsx @@ -0,0 +1,188 @@ +export const Timeout = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/account-kit/react/src/icons/walletConnectIcon.tsx b/account-kit/react/src/icons/walletConnectIcon.tsx new file mode 100644 index 000000000..cb0a134bb --- /dev/null +++ b/account-kit/react/src/icons/walletConnectIcon.tsx @@ -0,0 +1,22 @@ +export const WalletConnectIcon = ({ + className, + ...props +}: React.JSX.IntrinsicAttributes & React.SVGProps) => { + return ( +
+ + + +
+ ); +}; diff --git a/account-kit/react/src/icons/warning.tsx b/account-kit/react/src/icons/warning.tsx new file mode 100644 index 000000000..5b0aca62e --- /dev/null +++ b/account-kit/react/src/icons/warning.tsx @@ -0,0 +1,18 @@ +export const Warning = () => { + return ( + + + + ); +}; diff --git a/account-kit/react/src/strings.ts b/account-kit/react/src/strings.ts index 72dce13ae..d18d066b9 100644 --- a/account-kit/react/src/strings.ts +++ b/account-kit/react/src/strings.ts @@ -42,6 +42,25 @@ const STRINGS = { poweredBy: { title: "powered by", }, + error: { + general: { + title: "Permission denied", + body: "The request is currently not allowed by the agent or the platform. Try again later.", + }, + connection: { + passkeyTitle: "Connection failed", + passkeyBody: + "Passkey request timed out or canceled by the agent. You may have to use another method to register a passkey for your account.", + walletTitle: "Couldn't connect to ", + walletBody: "The wallet’s connection failed or canceled", + timedOutTitle: "Connection timed out", + timedOutBody: "The connection timed out, please try again.", + }, + cta: { + tryAgain: "Try again", + useAnotherMethod: "Use another method", + }, + }, }, }; diff --git a/examples/ui-demo/src/components/preview/AuthCardWrapper.tsx b/examples/ui-demo/src/components/preview/AuthCardWrapper.tsx index 35b908a7c..6eae1ea6b 100644 --- a/examples/ui-demo/src/components/preview/AuthCardWrapper.tsx +++ b/examples/ui-demo/src/components/preview/AuthCardWrapper.tsx @@ -18,7 +18,7 @@ export function AuthCardWrapper({ className }: { className?: string }) { > {!user ? (
-
+
diff --git a/yarn.lock b/yarn.lock index aa0b43640..d43782205 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2261,6 +2261,28 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@bundled-es-modules/cookie@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz#c3b82703969a61cf6a46e959a012b2c257f6b164" + integrity sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw== + dependencies: + cookie "^0.5.0" + +"@bundled-es-modules/statuses@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz#761d10f44e51a94902c4da48675b71a76cc98872" + integrity sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg== + dependencies: + statuses "^2.0.1" + +"@bundled-es-modules/tough-cookie@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz#fa9cd3cedfeecd6783e8b0d378b4a99e52bde5d3" + integrity sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw== + dependencies: + "@types/tough-cookie" "^4.0.5" + tough-cookie "^4.1.4" + "@clack/core@^0.3.3": version "0.3.4" resolved "https://registry.yarnpkg.com/@clack/core/-/core-0.3.4.tgz#375e82fc8fe46650b37cab2f2ea8752c6b7f0450" @@ -3469,6 +3491,45 @@ resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@inquirer/confirm@^3.0.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-3.2.0.tgz#6af1284670ea7c7d95e3f1253684cfbd7228ad6a" + integrity sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw== + dependencies: + "@inquirer/core" "^9.1.0" + "@inquirer/type" "^1.5.3" + +"@inquirer/core@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-9.1.0.tgz#158b82dc44564a1abd0ce14723d50c3efa0634a2" + integrity sha512-RZVfH//2ytTjmaBIzeKT1zefcQZzuruwkpTwwbe/i2jTl4o9M+iML5ChULzz6iw1Ok8iUBBsRCjY2IEbD8Ft4w== + dependencies: + "@inquirer/figures" "^1.0.5" + "@inquirer/type" "^1.5.3" + "@types/mute-stream" "^0.0.4" + "@types/node" "^22.5.2" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + cli-spinners "^2.9.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/figures@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.5.tgz#57f9a996d64d3e3345d2a3ca04d36912e94f8790" + integrity sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA== + +"@inquirer/type@^1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.5.3.tgz#220ae9f3d5ae17dd3b2ce5ffd6b48c4a30c73181" + integrity sha512-xUQ14WQGR/HK5ei+2CvgcwoH9fQ4PgPGmVFSN0pc1+fVyDL3MREhyAY7nxEErSu6CkllBM3D7e3e+kOvtu+eIg== + dependencies: + mute-stream "^1.0.0" + "@ioredis/commands@^1.1.1": version "1.2.0" resolved "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz" @@ -4284,6 +4345,18 @@ "@motionone/dom" "^10.16.4" tslib "^2.3.1" +"@mswjs/interceptors@^0.35.0": + version "0.35.0" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.35.0.tgz#ccdabb668833b97d3c5f26e761f5ea665629dd5f" + integrity sha512-f5cHyIvm4m4g1I5x9EH1etGx0puaU0OaX2szqGRVBVgUC6aMASlOI5hbpe7tJ9l4/VWjCUu5OMraCazLZGI24A== + dependencies: + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/logger" "^0.3.0" + "@open-draft/until" "^2.0.0" + is-node-process "^1.2.0" + outvariant "^1.4.3" + strict-event-emitter "^0.5.1" + "@next/env@14.1.4": version "14.1.4" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.4.tgz#432e80651733fbd67230bf262aee28be65252674" @@ -4892,6 +4965,24 @@ dependencies: "@octokit/openapi-types" "^18.0.0" +"@open-draft/deferred-promise@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz#4a822d10f6f0e316be4d67b4d4f8c9a124b073bd" + integrity sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA== + +"@open-draft/logger@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@open-draft/logger/-/logger-0.3.0.tgz#2b3ab1242b360aa0adb28b85f5d7da1c133a0954" + integrity sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ== + dependencies: + is-node-process "^1.2.0" + outvariant "^1.4.0" + +"@open-draft/until@^2.0.0", "@open-draft/until@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" + integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== + "@parcel/watcher-android-arm64@2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.3.0.tgz#d82e74bb564ebd4d8a88791d273a3d2bd61e27ab" @@ -6877,6 +6968,11 @@ dependencies: "@types/node" "*" +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@types/cross-spawn@^6.0.2": version "6.0.6" resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.6.tgz#0163d0b79a6f85409e0decb8dcca17147f81fd22" @@ -7121,6 +7217,13 @@ resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== +"@types/mute-stream@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" + integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== + dependencies: + "@types/node" "*" + "@types/node-fetch@^2.6.4": version "2.6.11" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" @@ -7167,6 +7270,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@^22.5.2": + version "22.5.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8" + integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg== + dependencies: + undici-types "~6.19.2" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz" @@ -7282,11 +7392,21 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/statuses@^2.0.4": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.5.tgz#f61ab46d5352fd73c863a1ea4e1cef3b0b51ae63" + integrity sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A== + "@types/supports-color@^8.0.0": version "8.1.3" resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.3.tgz#b769cdce1d1bb1a3fa794e35b62c62acdf93c139" integrity sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg== +"@types/tough-cookie@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + "@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" @@ -7314,6 +7434,11 @@ dependencies: "@types/node" "*" +"@types/wrap-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" + integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -8656,7 +8781,7 @@ ansi-colors@^4.1.1: resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^4.2.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -9924,6 +10049,11 @@ cli-width@^3.0.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + client-only@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" @@ -10330,6 +10460,11 @@ cookie@0.6.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + core-js-compat@^3.31.0, core-js-compat@^3.33.1: version "3.34.0" resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz" @@ -13296,6 +13431,11 @@ graphemer@^1.4.0: resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +graphql@^16.8.1: + version "16.9.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== + h3@^1.8.1, h3@^1.8.2: version "1.9.0" resolved "https://registry.npmjs.org/h3/-/h3-1.9.0.tgz" @@ -13543,6 +13683,11 @@ hastscript@^8.0.0: property-information "^6.0.0" space-separated-tokens "^2.0.0" +headers-polyfill@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-4.0.3.tgz#922a0155de30ecc1f785bcf04be77844ca95ad07" + integrity sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ== + hey-listen@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz" @@ -14209,6 +14354,11 @@ is-negative-zero@^2.0.2: resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-node-process@^1.0.1, is-node-process@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134" + integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw== + is-number-object@^1.0.4: version "1.0.7" resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" @@ -17038,6 +17188,36 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msw-storybook-addon@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/msw-storybook-addon/-/msw-storybook-addon-2.0.3.tgz#6a9ccf19f89ec9dde1d17e4a3be71d93b42e857a" + integrity sha512-CzHmGO32JeOPnyUnRWnB0PFTXCY1HKfHiEB/6fYoUYiFm2NYosLjzs9aBd3XJUryYEN0avJqMNh7nCRDxE5JjQ== + dependencies: + is-node-process "^1.0.1" + +msw@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.4.4.tgz#d40c12b2d99750e3b2cbd94ef269a23b06a9aa67" + integrity sha512-iuM0qGs4YmgYCLH+xqb07w2e/e4fYmsx3+WHVlIOUA34TW1sw+wRpNmOlXnLDkw/T7233Jnm6t+aNf4v2E3e2Q== + dependencies: + "@bundled-es-modules/cookie" "^2.0.0" + "@bundled-es-modules/statuses" "^1.0.1" + "@bundled-es-modules/tough-cookie" "^0.1.6" + "@inquirer/confirm" "^3.0.0" + "@mswjs/interceptors" "^0.35.0" + "@open-draft/until" "^2.1.0" + "@types/cookie" "^0.6.0" + "@types/statuses" "^2.0.4" + chalk "^4.1.2" + graphql "^16.8.1" + headers-polyfill "^4.0.2" + is-node-process "^1.2.0" + outvariant "^1.4.2" + path-to-regexp "^6.2.0" + strict-event-emitter "^0.5.1" + type-fest "^4.9.0" + yargs "^17.7.2" + multiformats@^9.4.2: version "9.9.0" resolved "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz" @@ -17059,7 +17239,7 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mute-stream@~1.0.0: +mute-stream@^1.0.0, mute-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== @@ -17902,6 +18082,11 @@ outdent@^0.8.0: resolved "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz" integrity sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A== +outvariant@^1.4.0, outvariant@^1.4.2, outvariant@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873" + integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" @@ -18290,6 +18475,11 @@ path-to-regexp@^1.0.0: dependencies: isarray "0.0.1" +path-to-regexp@^6.2.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36" + integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw== + path-type@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" @@ -18795,6 +18985,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + pump@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" @@ -18803,7 +18998,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -18859,6 +19054,11 @@ query-string@7.1.3: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -20259,7 +20459,7 @@ stat-mode@0.3.0: resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.3.0.tgz#69283b081f851582b328d2a4ace5f591ce52f54b" integrity sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng== -statuses@2.0.1: +statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== @@ -20358,6 +20558,11 @@ streamsearch@^1.1.0: resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +strict-event-emitter@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93" + integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ== + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" @@ -21041,6 +21246,16 @@ toml@^3.0.0: resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== +tough-cookie@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -21271,6 +21486,11 @@ type-fest@^3.8.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== +type-fest@^4.9.0: + version "4.26.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.26.1.tgz#a4a17fa314f976dd3e6d6675ef6c775c16d7955e" + integrity sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -21409,6 +21629,11 @@ undici-types@~5.26.4: resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + undici@5.28.4: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" @@ -21638,6 +21863,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" @@ -21715,6 +21945,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + use-callback-ref@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz" @@ -22522,7 +22760,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@17.7.2, yargs@^17.0.0, yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: +yargs@17.7.2, yargs@^17.0.0, yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -22603,6 +22841,11 @@ yocto-queue@^1.0.0: resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== +yoctocolors-cjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== + yoctocolors@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.1.tgz#e0167474e9fbb9e8b3ecca738deaa61dd12e56fc"