From 89e2cd621cde6c662a28740f3667df51e661a0ab Mon Sep 17 00:00:00 2001 From: Ciprian Draghici Date: Thu, 29 Aug 2024 18:40:38 +0300 Subject: [PATCH] CrossWindow login (#13) * wip crosswindow provider login * implement login with crosswindow provider, fix network config initialization and relogin * apply specific condition for crosswindow at the generic login * add todos * revert minification changes * fix todo * todo fix * changelog update * rebasing on top of development branch * cleanup * upgrade sdk=dapp-utils and cross-window-provider * 0.0.0-alpha.9 --- CHANGELOG.md | 4 +- package.json | 10 +- src/apiCalls/accounts/getAccountFromApi.ts | 7 +- .../methods}/account/getLatestNonce.ts | 0 src/core/methods/init/init.ts | 50 ++++++-- .../methods/login/helpers/getCallbackUrl.ts | 6 + .../helpers/getImpersonatedAccountDetails.ts | 35 ++++++ .../login/helpers/impersonateAccount.ts | 52 ++++++++ .../login/helpers/processModifiedAccount.ts | 52 -------- src/core/methods/login/login.ts | 88 ++++++++++--- src/core/methods/login/webWalletLogin.ts | 118 ------------------ src/core/providers/ProviderFactory.ts | 14 +++ src/core/providers/accountProvider.ts | 3 +- src/core/providers/helpers/emptyProvider.ts | 9 +- .../providers/types/providerFactory.types.ts | 15 ++- src/store/actions/config/configActions.ts | 7 ++ .../actions/loginInfo/loginInfoActions.ts | 23 ++-- .../actions/sharedActions/sharedActions.ts | 7 +- src/store/middleware/applyMiddlewares.ts | 3 +- src/store/middleware/logoutMiddleware.ts | 6 +- src/store/selectors/configSelectors.ts | 6 + src/store/selectors/index.ts | 1 + src/store/selectors/loginInfoSelectors.ts | 3 - src/store/slices/account/account.ts | 110 ---------------- src/store/slices/account/index.ts | 2 +- src/store/slices/config/config.types.ts | 5 + src/store/slices/config/configSlice.ts | 22 ++++ src/store/slices/config/index.ts | 1 + src/store/slices/helpers/getActions.ts | 46 ------- src/store/slices/helpers/getVanillaStore.ts | 59 --------- src/store/slices/helpers/index.ts | 1 - src/store/slices/index.ts | 2 + src/store/slices/loginInfo/index.ts | 2 +- src/store/slices/network/index.ts | 2 +- src/store/slices/shared/listenToLogin.ts | 12 -- src/store/store.ts | 10 +- src/store/store.types.ts | 2 + src/types/login.types.ts | 4 - src/utils/account/getAddress.ts | 6 - src/utils/account/getIsLoggedIn.ts | 5 - src/utils/account/index.ts | 1 - yarn.lock | 36 ++++-- 42 files changed, 354 insertions(+), 493 deletions(-) rename src/{utils => core/methods}/account/getLatestNonce.ts (100%) create mode 100644 src/core/methods/login/helpers/getCallbackUrl.ts create mode 100644 src/core/methods/login/helpers/getImpersonatedAccountDetails.ts create mode 100644 src/core/methods/login/helpers/impersonateAccount.ts delete mode 100644 src/core/methods/login/helpers/processModifiedAccount.ts delete mode 100644 src/core/methods/login/webWalletLogin.ts create mode 100644 src/store/actions/config/configActions.ts create mode 100644 src/store/selectors/configSelectors.ts delete mode 100644 src/store/slices/account/account.ts create mode 100644 src/store/slices/config/config.types.ts create mode 100644 src/store/slices/config/configSlice.ts create mode 100644 src/store/slices/config/index.ts delete mode 100644 src/store/slices/helpers/getActions.ts delete mode 100644 src/store/slices/helpers/getVanillaStore.ts delete mode 100644 src/store/slices/helpers/index.ts delete mode 100644 src/store/slices/shared/listenToLogin.ts delete mode 100644 src/utils/account/getAddress.ts delete mode 100644 src/utils/account/getIsLoggedIn.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a8281c9..c727b63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - -- [Added sdk-web-wallet-cross-window-provider as peer dependency](https://github.com/multiversx/mx-sdk-dapp-core/pull/12) +- [CrossWindow login](https://github.com/multiversx/mx-sdk-dapp-core/pull/13) +- [Added sdk-web-wallet-cross-window-provider as peer dependency](https://github.com/multiversx/mx-sdk-dapp-core/pull/14) - [Generic login + ExtensionProvider login](https://github.com/multiversx/mx-sdk-dapp-core/pull/12) - [Make middlewares registration more scalable](https://github.com/multiversx/mx-sdk-dapp-core/pull/11) - [Fix Node Polyfills](https://github.com/multiversx/mx-sdk-dapp-core/pull/10) diff --git a/package.json b/package.json index 8fcca57..35afdd1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-dapp-core", - "version": "0.0.0-alpha.7", + "version": "0.0.0-alpha.9", "main": "out/index.js", "module": "out/index.js", "types": "out/index.d.ts", @@ -45,8 +45,8 @@ }, "peerDependencies": { "@multiversx/sdk-core": ">= 13.0.0", - "@multiversx/sdk-dapp-utils": "^0.0.1", - "@multiversx/sdk-web-wallet-cross-window-provider": ">= 0.4.2", + "@multiversx/sdk-dapp-utils": ">= 0.1.0", + "@multiversx/sdk-web-wallet-cross-window-provider": ">= 1.0.0", "axios": ">=1.6.5", "bignumber.js": "9.x" }, @@ -55,8 +55,8 @@ }, "devDependencies": { "@multiversx/sdk-core": ">= 13.0.0", - "@multiversx/sdk-dapp-utils": "^0.0.1", - "@multiversx/sdk-web-wallet-cross-window-provider": ">= 0.4.2", + "@multiversx/sdk-dapp-utils": ">= 0.1.0", + "@multiversx/sdk-web-wallet-cross-window-provider": ">= 1.0.0", "@swc/core": "^1.4.17", "@swc/jest": "^0.2.36", "@types/node": "20.12.8", diff --git a/src/apiCalls/accounts/getAccountFromApi.ts b/src/apiCalls/accounts/getAccountFromApi.ts index 1cb8f10..001403f 100644 --- a/src/apiCalls/accounts/getAccountFromApi.ts +++ b/src/apiCalls/accounts/getAccountFromApi.ts @@ -1,12 +1,15 @@ import { ACCOUNTS_ENDPOINT } from 'apiCalls/endpoints'; import { axiosInstance } from 'apiCalls/utils/axiosInstance'; import { getCleanApiAddress } from 'apiCalls/utils/getCleanApiAddress'; +import { AccountType } from 'types/account.types'; export const accountFetcher = (address: string | null) => { const apiAddress = getCleanApiAddress(); const url = `${apiAddress}/${ACCOUNTS_ENDPOINT}/${address}?withGuardianInfo=true`; // we need to get it with an axios instance because of cross-window user interaction issues - return axiosInstance.get(url); + return axiosInstance.get(url, { + baseURL: apiAddress + }); }; export const getAccountFromApi = async (address?: string) => { @@ -16,7 +19,7 @@ export const getAccountFromApi = async (address?: string) => { try { const { data } = await accountFetcher(address); - return data; + return data as AccountType; } catch (err) { console.error('error fetching configuration for ', address); } diff --git a/src/utils/account/getLatestNonce.ts b/src/core/methods/account/getLatestNonce.ts similarity index 100% rename from src/utils/account/getLatestNonce.ts rename to src/core/methods/account/getLatestNonce.ts diff --git a/src/core/methods/init/init.ts b/src/core/methods/init/init.ts index 1e7c369..07d78d0 100644 --- a/src/core/methods/init/init.ts +++ b/src/core/methods/init/init.ts @@ -1,6 +1,9 @@ import { initStore } from 'store/store'; import { defaultStorageCallback, StorageCallback } from 'store/storage'; -import { setTokenLoginNativeAuthTokenConfig } from 'store/actions/loginInfo/loginInfoActions'; +import { setNativeAuthConfig } from 'store/actions/config/configActions'; +import { initializeNetwork } from 'store/actions'; +import { CustomNetworkType } from 'types/network.types'; +import { EnvironmentsEnum } from 'types/enums.types'; import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types'; import { getDefaultNativeAuthConfig } from 'services/nativeAuth/methods/getDefaultNativeAuthConfig'; @@ -8,23 +11,54 @@ type InitAppType = { storage?: { getStorageCallback: StorageCallback; }; - nativeAuth?: boolean | NativeAuthConfigType; + dAppConfig?: { + nativeAuth?: boolean | NativeAuthConfigType; + network?: CustomNetworkType; + environment?: EnvironmentsEnum; + }; }; const defaultInitAppProps = { storage: { getStorageCallback: defaultStorageCallback } }; -export const initializeDApp = (props?: InitAppType) => { - const { storage, nativeAuth } = { ...defaultInitAppProps, ...props }; + +/** + * Initializes the dApp with the given configuration. + * @param props - The configuration for the dApp initialization. + * @param props.storage - The storage configuration for the dApp. + * @param props.storage.getStorageCallback - The callback to get the storage (custom storage). + * @param props.nativeAuth - The native auth configuration for the dApp. + * @param props.nativeAuth - If set to `true`, will fallback on default configuration. + * @param props.nativeAuth - If set to `false`, will disable native auth. + * @param props.nativeAuth - If set to `NativeAuthConfigType`, will set the native auth configuration. + * + * !!! Avoid changing the configuration during the dApp lifecycle. + * + * @example + * ```ts + * initializeDApp({ + * nativeAuth: true + * }); + * ``` + * */ +export const initializeDApp = async (props?: InitAppType) => { + const { storage, dAppConfig } = { ...defaultInitAppProps, ...props }; initStore(storage.getStorageCallback); - if (nativeAuth) { + if (dAppConfig?.nativeAuth) { const nativeAuthConfig: NativeAuthConfigType = - typeof nativeAuth === 'boolean' + typeof dAppConfig.nativeAuth === 'boolean' ? getDefaultNativeAuthConfig() - : nativeAuth; + : dAppConfig.nativeAuth; + + setNativeAuthConfig(nativeAuthConfig); + } - setTokenLoginNativeAuthTokenConfig(nativeAuthConfig); + if (dAppConfig?.network) { + await initializeNetwork({ + customNetworkConfig: dAppConfig.network, + environment: dAppConfig.environment ?? EnvironmentsEnum.devnet + }); } }; diff --git a/src/core/methods/login/helpers/getCallbackUrl.ts b/src/core/methods/login/helpers/getCallbackUrl.ts new file mode 100644 index 0000000..fb84905 --- /dev/null +++ b/src/core/methods/login/helpers/getCallbackUrl.ts @@ -0,0 +1,6 @@ +import { getWindowLocation } from 'utils/window/getWindowLocation'; + +export function getCallbackUrl() { + const { origin, pathname } = getWindowLocation(); + return encodeURIComponent(`${origin}${pathname}`); +} diff --git a/src/core/methods/login/helpers/getImpersonatedAccountDetails.ts b/src/core/methods/login/helpers/getImpersonatedAccountDetails.ts new file mode 100644 index 0000000..3381c26 --- /dev/null +++ b/src/core/methods/login/helpers/getImpersonatedAccountDetails.ts @@ -0,0 +1,35 @@ +import { getAccount } from 'utils/account/getAccount'; +import { getModifiedLoginToken } from './getModifiedLoginToken'; + +interface GetImpersonatedAccountDetailsType { + address: string; + originalLoginToken?: string; + extraInfoData: { + multisig?: string; + impersonate?: string; + }; +} + +export const getImpersonatedAccountDetails = async ({ + originalLoginToken, + extraInfoData, + address +}: GetImpersonatedAccountDetailsType) => { + const modifiedLoginToken = await getModifiedLoginToken({ + loginToken: originalLoginToken, + extraInfoData + }); + + const tokenAddress = + extraInfoData.multisig || extraInfoData.impersonate || address; + + const accountAddress = modifiedLoginToken != null ? tokenAddress : address; + + const account = await getAccount(accountAddress); + + return { + account, + address: accountAddress, + modifiedLoginToken + }; +}; diff --git a/src/core/methods/login/helpers/impersonateAccount.ts b/src/core/methods/login/helpers/impersonateAccount.ts new file mode 100644 index 0000000..0a3be16 --- /dev/null +++ b/src/core/methods/login/helpers/impersonateAccount.ts @@ -0,0 +1,52 @@ +import { setAccount } from 'store/actions/account'; +import { setLoginToken } from 'store/actions/loginInfo/loginInfoActions'; +import { IProvider } from 'core/providers/types/providerFactory.types'; +import { loginAction } from 'store/actions'; +import { AccountType } from 'types/account.types'; +import { getImpersonatedAccountDetails } from './getImpersonatedAccountDetails'; +import { getLatestNonce } from 'core/methods/account/getLatestNonce'; + +export async function impersonateAccount({ + loginToken, + extraInfoData, + address, + provider +}: { + loginToken: string; + extraInfoData: { + multisig?: string; + impersonate?: string; + }; + address: string; + provider: IProvider; +}) { + const impersonationDetails = await getImpersonatedAccountDetails({ + originalLoginToken: loginToken, + extraInfoData, + address + }); + + if (impersonationDetails.modifiedLoginToken) { + setLoginToken(impersonationDetails.modifiedLoginToken); + } + + if (impersonationDetails.account) { + loginAction({ + address: impersonationDetails.address, + providerType: provider.getType() + }); + + const newAccount: AccountType = { + ...impersonationDetails.account, + nonce: getLatestNonce(impersonationDetails.account) + }; + + setAccount(newAccount); + return { + ...impersonationDetails, + account: newAccount + }; + } + + return impersonationDetails; +} diff --git a/src/core/methods/login/helpers/processModifiedAccount.ts b/src/core/methods/login/helpers/processModifiedAccount.ts deleted file mode 100644 index a433019..0000000 --- a/src/core/methods/login/helpers/processModifiedAccount.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { getAccount } from 'utils/account/getAccount'; -import { - getModifiedLoginToken, - GetMultiSigLoginTokenType -} from './getModifiedLoginToken'; - -interface SetMultisigLoginToken extends GetMultiSigLoginTokenType { - signature?: string; - address: string; - loginService: T; -} - -export const processModifiedAccount = async < - T extends { - setLoginToken: (loginToken: string) => void; - setTokenLoginInfo: ({ - address, - signature - }: { - address: string; - signature: string; - }) => string | undefined; - } ->({ - loginToken: token, - extraInfoData, - address, - signature, - loginService -}: SetMultisigLoginToken) => { - const loginToken = await getModifiedLoginToken({ - loginToken: token, - extraInfoData - }); - - const tokenAddress = - extraInfoData.multisig || extraInfoData.impersonate || address; - - const accountAddress = loginToken != null ? tokenAddress : address; - - if (loginToken != null) { - loginService.setLoginToken(loginToken); - } - - if (signature) { - loginService.setTokenLoginInfo({ signature, address }); - } - - const account = await getAccount(accountAddress); - - return account; -}; diff --git a/src/core/methods/login/login.ts b/src/core/methods/login/login.ts index b3e0ffa..aa17476 100644 --- a/src/core/methods/login/login.ts +++ b/src/core/methods/login/login.ts @@ -1,5 +1,5 @@ import { nativeAuth } from 'services/nativeAuth'; -import { setAddress } from 'store/actions/account'; +import { setAccount, setAddress } from 'store/actions/account'; import { setProviderType, setTokenLogin @@ -13,11 +13,17 @@ import { ProviderFactory } from 'core/providers/ProviderFactory'; import { nativeAuthConfigSelector } from 'store/selectors'; import { getState } from 'store/store'; import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types'; -import { getIsLoggedIn } from 'utils/account/getIsLoggedIn'; -import { getAddress } from 'utils/account/getAddress'; +import { getIsLoggedIn } from 'core/methods/account/getIsLoggedIn'; +import { getAddress } from 'core/methods/account/getAddress'; +import { loginAction, logoutAction } from 'store/actions'; +import { impersonateAccount } from './helpers/impersonateAccount'; +import { SECOND_LOGIN_ATTEMPT_ERROR } from 'constants/errorMessages.constants'; +import { getCallbackUrl } from './helpers/getCallbackUrl'; async function loginWithoutNativeToken(provider: IProvider) { - await provider.login(); + await provider.login?.({ + callbackUrl: getCallbackUrl() + }); const address = provider.getAddress?.(); @@ -32,6 +38,28 @@ async function loginWithoutNativeToken(provider: IProvider) { }; } +async function tryImpersonateAccount({ + loginToken, + extraInfoData, + address, + provider +}: { + loginToken: string; + extraInfoData: { + multisig?: string; + impersonate?: string; + }; + address: string; + provider: IProvider; +}) { + return await impersonateAccount({ + loginToken, + extraInfoData, + address, + provider + }); +} + async function loginWithNativeToken( provider: IProvider, nativeAuthConfig: NativeAuthConfigType @@ -42,17 +70,27 @@ async function loginWithNativeToken( noCache: true }); - await provider.login({ token: loginToken }); + const loginResult = await provider.login?.({ + callbackUrl: getCallbackUrl(), + token: loginToken + }); - const address = provider.getAddress?.(); - const signature = provider.getTokenLoginSignature?.(); + const address = provider.getAddress + ? // TODO check why on the second login the address is fetched asynchronously (looks like the crosswindow provider has getAddress as an async function) + await provider.getAddress() + : loginResult?.address; + const signature = provider.getTokenLoginSignature + ? provider.getTokenLoginSignature() + : loginResult?.signature; if (!address) { - throw new Error('Address not found'); + console.warn('Login cancelled.'); + return null; } if (!signature) { - throw new Error('Signature not found'); + console.error('Failed to sign login token'); + return null; } const nativeAuthToken = nativeAuthClient.getToken({ @@ -61,19 +99,39 @@ async function loginWithNativeToken( signature }); - setAddress(address); setTokenLogin({ loginToken, signature, - nativeAuthToken, - nativeAuthConfig + nativeAuthToken + }); + loginAction({ + address, + providerType: provider.getType() }); - return { + const impersonationDetails = await tryImpersonateAccount({ + loginToken, + extraInfoData: { + multisig: loginResult?.multisig, + impersonate: loginResult?.impersonate + }, address, + provider + }); + + if (impersonationDetails.account) { + setAccount(impersonationDetails.account); + } else { + logoutAction(); + console.error('Failed to fetch account'); + throw new Error('Failed to fetch account'); + } + + return { + address: impersonationDetails?.address || address, signature, nativeAuthToken, - loginToken, + loginToken: impersonationDetails?.modifiedLoginToken || loginToken, nativeAuthConfig }; } @@ -87,7 +145,7 @@ export const login = async ({ if (loggedIn) { console.warn('Already logged in with:', getAddress()); - return; + throw new Error(SECOND_LOGIN_ATTEMPT_ERROR); } const factory = new ProviderFactory(); diff --git a/src/core/methods/login/webWalletLogin.ts b/src/core/methods/login/webWalletLogin.ts deleted file mode 100644 index 4112087..0000000 --- a/src/core/methods/login/webWalletLogin.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { OnProviderLoginType } from 'types/login.types'; -import { getWindowLocation } from 'utils/window/getWindowLocation'; -import { getLoginService } from './helpers/getLoginService'; -import { networkSelector } from 'store/selectors'; -import { getState } from 'store/store'; -import { getIsLoggedIn } from '../account/getIsLoggedIn'; -import { setAccountProvider } from 'core/providers/accountProvider'; -import { SECOND_LOGIN_ATTEMPT_ERROR } from 'constants/errorMessages.constants'; -import { isBrowserWithPopupConfirmation } from 'constants/browser.constants'; -import { processModifiedAccount } from './helpers/processModifiedAccount'; -import { loginAction } from 'store/actions/sharedActions'; -import { setAccount } from 'store/actions/account/accountActions'; -import { getLatestNonce } from 'utils/account/getLatestNonce'; -import { AccountType } from 'types/account.types'; -import { CrossWindowProvider } from 'lib/sdkWebWalletCrossWindowProvider'; -import { ProviderTypeEnum } from '../../providers/types/providerFactory.types'; - -export const webWalletLogin = async ({ - token: tokenToSign, - nativeAuth, - hasConsentPopup, - walletAddress -}: OnProviderLoginType & { - hasConsentPopup?: boolean; - walletAddress?: string; -}): Promise => { - const hasNativeAuth = nativeAuth != null; - const loginService = getLoginService(nativeAuth); - let token = tokenToSign; - const network = networkSelector(getState()); - - const isLoggedIn = getIsLoggedIn(); - - if (isLoggedIn) { - throw new Error(SECOND_LOGIN_ATTEMPT_ERROR); - } - - const isSuccessfullyInitialized: boolean = - await CrossWindowProvider.getInstance().init(); - const provider: CrossWindowProvider = - CrossWindowProvider.getInstance().setWalletUrl( - walletAddress ?? network.walletAddress - ); - - try { - if (!isSuccessfullyInitialized) { - console.warn('Something went wrong trying to redirect to wallet login..'); - return null; - } - - const { origin, pathname } = getWindowLocation(); - const callbackUrl: string = encodeURIComponent(`${origin}${pathname}`); - - if (hasNativeAuth && !token) { - token = await loginService.getNativeAuthLoginToken(); - - // Fetching block failed - if (!token) { - console.warn('Fetching block failed. Login cancelled.'); - return null; - } - } - - if (token) { - loginService.setLoginToken(token); - } - - const providerLoginData = { - callbackUrl, - ...(token && { token }) - }; - - const needsConsent = isBrowserWithPopupConfirmation && hasNativeAuth; - - if (needsConsent || hasConsentPopup) { - provider.setShouldShowConsentPopup(true); - } - - const { signature, address, multisig, impersonate } = - await provider.login(providerLoginData); - - setAccountProvider(provider); - - if (!address) { - console.warn('Login cancelled.'); - return null; - } - - const account = await processModifiedAccount({ - loginToken: token, - extraInfoData: { multisig, impersonate }, - address, - signature, - loginService - }); - - if (!account) { - return null; - } - - loginAction({ - address: account.address, - providerType: ProviderTypeEnum.crossWindow - }); - - const newAccount: AccountType = { - ...account, - nonce: getLatestNonce(account) - }; - - setAccount(newAccount); - - return newAccount; - } catch (error) { - console.error('error logging in', error); - throw error; - } -}; diff --git a/src/core/providers/ProviderFactory.ts b/src/core/providers/ProviderFactory.ts index d11d1c0..a317179 100644 --- a/src/core/providers/ProviderFactory.ts +++ b/src/core/providers/ProviderFactory.ts @@ -6,6 +6,7 @@ import { IProviderFactory, ProviderTypeEnum } from './types/providerFactory.types'; +import { isBrowserWithPopupConfirmation } from '../../constants'; export class ProviderFactory { public async create({ @@ -36,6 +37,10 @@ export class ProviderFactory { return provider.account.signature; }; + createdProvider.getType = () => { + return ProviderTypeEnum.extension; + }; + break; } @@ -47,6 +52,10 @@ export class ProviderFactory { }); createdProvider = provider as unknown as IProvider; + createdProvider.getType = () => { + return ProviderTypeEnum.crossWindow; + }; + break; } @@ -69,6 +78,11 @@ export class ProviderFactory { const provider = CrossWindowProvider.getInstance(); await provider.init(); provider.setWalletUrl(String(walletAddress)); + + if (isBrowserWithPopupConfirmation) { + provider.setShouldShowConsentPopup(true); + } + return provider; } diff --git a/src/core/providers/accountProvider.ts b/src/core/providers/accountProvider.ts index bdce2ab..f1282f3 100644 --- a/src/core/providers/accountProvider.ts +++ b/src/core/providers/accountProvider.ts @@ -1,8 +1,7 @@ import { emptyProvider } from './helpers/emptyProvider'; -import { CrossWindowProvider } from 'lib/sdkWebWalletCrossWindowProvider'; import { IProvider } from 'core/providers/types/providerFactory.types'; -export type ProvidersType = IProvider | CrossWindowProvider; +export type ProvidersType = IProvider; let accountProvider: ProvidersType = emptyProvider; diff --git a/src/core/providers/helpers/emptyProvider.ts b/src/core/providers/helpers/emptyProvider.ts index 95d81f6..7503998 100644 --- a/src/core/providers/helpers/emptyProvider.ts +++ b/src/core/providers/helpers/emptyProvider.ts @@ -1,6 +1,9 @@ import { SignableMessage, Transaction } from '@multiversx/sdk-core'; import { EngineTypes } from 'utils/walletconnect/__sdkWalletconnectProvider'; -import { IProvider } from 'core/providers/types/providerFactory.types'; +import { + IProvider, + ProviderTypeEnum +} from 'core/providers/types/providerFactory.types'; export const DAPP_INIT_ROUTE = '/dapp/init'; @@ -101,6 +104,10 @@ export class EmptyProvider implements IProvider { getTokenLoginSignature(): string | undefined { throw new Error(notInitializedError(`getSignature`)); } + + getType(): ProviderTypeEnum { + return ProviderTypeEnum.none; + } } export const emptyProvider = new EmptyProvider(); diff --git a/src/core/providers/types/providerFactory.types.ts b/src/core/providers/types/providerFactory.types.ts index 4bc2ed7..ee6a022 100644 --- a/src/core/providers/types/providerFactory.types.ts +++ b/src/core/providers/types/providerFactory.types.ts @@ -1,17 +1,26 @@ import type { IDAppProviderBase } from '@multiversx/sdk-dapp-utils'; +// @ts-ignore export interface IProvider extends IDAppProviderBase { init: () => Promise; - // TODO change return type to { address: string, signature: string } and also change the return type in IDAppProviderBase. - login: (options?: { token?: string }) => Promise; + login: (options?: { callbackUrl?: string; token?: string }) => Promise<{ + address: string; + signature: string; + multisig?: string; + impersonate?: string; + [key: string]: unknown; + }>; logout: () => Promise; setShouldShowConsentPopup?: (shouldShow: boolean) => void; - getAddress(): string | undefined; + getType: () => ProviderTypeEnum; + getAddress(): Promise; // TODO will be removed as soon as the new login method is implemented in the same way for all providers getTokenLoginSignature(): string | undefined; + // getExtraInfoData(): { multisig?: string; impersonate?: string } | undefined; } export interface IProviderConfig { + // TODO check if we have to pass the network object as argument here or it should be read from the state network: { walletAddress: string; }; diff --git a/src/store/actions/config/configActions.ts b/src/store/actions/config/configActions.ts new file mode 100644 index 0000000..ef3c122 --- /dev/null +++ b/src/store/actions/config/configActions.ts @@ -0,0 +1,7 @@ +import { getStore } from 'store/store'; +import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types'; + +export const setNativeAuthConfig = (config: NativeAuthConfigType) => + getStore().setState(({ config: state }) => { + state.nativeAuthConfig = config; + }); diff --git a/src/store/actions/loginInfo/loginInfoActions.ts b/src/store/actions/loginInfo/loginInfoActions.ts index 887a670..68d6d6b 100644 --- a/src/store/actions/loginInfo/loginInfoActions.ts +++ b/src/store/actions/loginInfo/loginInfoActions.ts @@ -6,7 +6,6 @@ import { } from 'store/slices/loginInfo/loginInfo.types'; import { getStore } from 'store/store'; import { ProviderTypeEnum } from 'core/providers/types/providerFactory.types'; -import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types'; export const setProviderType = (providerType: ProviderTypeEnum) => getStore().setState(({ loginInfo: state }) => { @@ -18,26 +17,22 @@ export const setTokenLogin = (tokenLogin: TokenLoginType) => state.tokenLogin = tokenLogin; }); -export const setTokenLoginSignature = (signature: string) => +export const setLoginToken = (loginToken: string) => getStore().setState(({ loginInfo: state }) => { - if (state?.tokenLogin != null) { - state.tokenLogin.signature = signature; + if (state.tokenLogin != null) { + state.tokenLogin.loginToken = loginToken; + return; } + state.tokenLogin = { + loginToken + }; }); -export const setTokenLoginNativeAuthTokenConfig = ( - nativeAuthConfig: NativeAuthConfigType -) => +export const setTokenLoginSignature = (signature: string) => getStore().setState(({ loginInfo: state }) => { if (state?.tokenLogin != null) { - state.tokenLogin.nativeAuthConfig = nativeAuthConfig; - return; + state.tokenLogin.signature = signature; } - - state.tokenLogin = { - nativeAuthConfig, - loginToken: '' - }; }); export const setWalletLogin = (walletLogin: LoginInfoType | null) => diff --git a/src/store/actions/sharedActions/sharedActions.ts b/src/store/actions/sharedActions/sharedActions.ts index e03b1af..91b89b1 100644 --- a/src/store/actions/sharedActions/sharedActions.ts +++ b/src/store/actions/sharedActions/sharedActions.ts @@ -14,7 +14,12 @@ export const loginAction = ({ providerType }: LoginActionPayloadType) => getStore().setState(({ account, loginInfo }) => { + console.log('settings address with:', address); + account.address = address; account.publicKey = new Address(address).hex(); - loginInfo.providerType = providerType; + + if (loginInfo) { + loginInfo.providerType = providerType; + } }); diff --git a/src/store/middleware/applyMiddlewares.ts b/src/store/middleware/applyMiddlewares.ts index e141f6e..5d4dd7f 100644 --- a/src/store/middleware/applyMiddlewares.ts +++ b/src/store/middleware/applyMiddlewares.ts @@ -1,8 +1,7 @@ import { StoreType } from '../store.types'; -import { StoreApi } from 'zustand/vanilla'; import { logoutMiddleware } from './logoutMiddleware'; export const applyMiddlewares = (state: StoreType, _prevState: StoreType) => { logoutMiddleware(state); // TODO add more middlewares here and eventually use _prevState if applicable -}; \ No newline at end of file +}; diff --git a/src/store/middleware/logoutMiddleware.ts b/src/store/middleware/logoutMiddleware.ts index 303703c..b4fe50a 100644 --- a/src/store/middleware/logoutMiddleware.ts +++ b/src/store/middleware/logoutMiddleware.ts @@ -23,8 +23,8 @@ export function setLoginExpiresAt(expiresAt: number) { }); } -export const logoutMiddleware = (newStore: StoreType) => { - const isLoggedIn = isLoggedInSelector(newStore); +export const logoutMiddleware = (state: StoreType) => { + const isLoggedIn = isLoggedInSelector(state); const loginTimestamp = storage.local.getItem(localStorageKeys.loginExpiresAt); if (!isLoggedIn) { @@ -41,6 +41,6 @@ export const logoutMiddleware = (newStore: StoreType) => { if (isExpired) { // logout - resetStore(newStore); + resetStore(state); } }; diff --git a/src/store/selectors/configSelectors.ts b/src/store/selectors/configSelectors.ts new file mode 100644 index 0000000..d4e507d --- /dev/null +++ b/src/store/selectors/configSelectors.ts @@ -0,0 +1,6 @@ +import { StoreType } from 'store/store.types'; + +export const configSelector = ({ config }: StoreType) => config; + +export const nativeAuthConfigSelector = ({ config }: StoreType) => + config.nativeAuthConfig; diff --git a/src/store/selectors/index.ts b/src/store/selectors/index.ts index 5043925..0d00fab 100644 --- a/src/store/selectors/index.ts +++ b/src/store/selectors/index.ts @@ -2,3 +2,4 @@ export * from './accountSelectors'; export * from './networkSelectors'; export * from './storeSelector'; export * from './loginInfoSelectors'; +export * from './configSelectors'; diff --git a/src/store/selectors/loginInfoSelectors.ts b/src/store/selectors/loginInfoSelectors.ts index 4453da2..f8251c9 100644 --- a/src/store/selectors/loginInfoSelectors.ts +++ b/src/store/selectors/loginInfoSelectors.ts @@ -4,6 +4,3 @@ export const loginInfoSelector = ({ loginInfo }: StoreType) => loginInfo; export const tokenLoginSelector = ({ loginInfo }: StoreType) => loginInfo.tokenLogin; - -export const nativeAuthConfigSelector = ({ loginInfo }: StoreType) => - loginInfo.tokenLogin?.nativeAuthConfig; diff --git a/src/store/slices/account/account.ts b/src/store/slices/account/account.ts deleted file mode 100644 index 4e96d71..0000000 --- a/src/store/slices/account/account.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { AccountType } from 'types/account.types'; -import { getActions } from '../helpers'; -import { getVanillaStore } from '../helpers/getVanillaStore'; -import { getReactStore } from './../helpers/getReactStore'; -import { GetSetType } from './../helpers/types'; -import { listenToLogout } from './../shared/listenToLogout'; -import { - AccountSliceType, - BatchTransactionsWSResponseType, - LedgerAccountType -} from './account.types'; -import { emptyAccount } from './emptyAccount'; -import { listenToLogin } from '../shared/listenToLogin'; -import { Address } from '@multiversx/sdk-core/out'; - -const initialData: AccountSliceType = { - address: '', - websocketEvent: null, - websocketBatchEvent: null, - accounts: { '': emptyAccount }, - ledgerAccount: null, - publicKey: '', - walletConnectAccount: null -}; - -const actions = { - setAddress: (_address: string) => {}, - setAccount: (_account: AccountType) => {}, - setLedgerAccount: (_ledgerAccount: LedgerAccountType | null) => {}, - updateLedgerAccount: (_ledgerAccount: { - index: LedgerAccountType['index']; - address: LedgerAccountType['address']; - }) => {}, - setWalletConnectAccount: (_walletConnectAccount: string | null) => {}, - setWebsocketEvent: (_message: string) => {}, - setWebsocketBatchEvent: (_data: BatchTransactionsWSResponseType) => {} -}; - -const initialState = { - ...initialData, - ...actions -}; - -type StateType = typeof initialState; - -const definition = (set: GetSetType): StateType => { - const createActions = getActions({ set, actions }); - - return { - ...initialData, - ...createActions({ - setAddress: (state, address) => { - state.address = address; - }, - setAccount: (state, account) => { - const isSameAddress = state.address === account.address; - state.accounts = { - [state.address]: isSameAddress ? account : emptyAccount - }; - }, - setLedgerAccount: (state, ledgerAccount) => { - state.ledgerAccount = ledgerAccount; - }, - updateLedgerAccount: (state, { index, address }) => { - if (state.ledgerAccount) { - state.ledgerAccount.address = address; - state.ledgerAccount.index = index; - } - }, - setWalletConnectAccount: (state, walletConnectAccount) => { - state.walletConnectAccount = walletConnectAccount; - }, - setWebsocketEvent: (state, message) => { - state.websocketEvent = { - timestamp: Date.now(), - message - }; - }, - setWebsocketBatchEvent: (state, data) => { - state.websocketBatchEvent = { - timestamp: Date.now(), - data - }; - } - }) - }; -}; - -const handleLogout = listenToLogout((state: StateType) => { - state.setAddress(''); -}); - -const handleLogin = listenToLogin( - (state: StateType, { detail: { address } }) => { - state.address = address; - state.publicKey = new Address(address).hex(); - } -); - -export const accountStore = getVanillaStore({ - name: 'accountStore', - definition, - middleware: [handleLogout, handleLogin] -}); - -// react store -export const useAccountStore = getReactStore({ - initialState, - store: accountStore -}); diff --git a/src/store/slices/account/index.ts b/src/store/slices/account/index.ts index 96e1a81..754ef64 100644 --- a/src/store/slices/account/index.ts +++ b/src/store/slices/account/index.ts @@ -1 +1 @@ -export * from './accountSlice'; +export { accountSlice } from './accountSlice'; diff --git a/src/store/slices/config/config.types.ts b/src/store/slices/config/config.types.ts new file mode 100644 index 0000000..52b3270 --- /dev/null +++ b/src/store/slices/config/config.types.ts @@ -0,0 +1,5 @@ +import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types'; + +export interface ConfigSliceType { + nativeAuthConfig: NativeAuthConfigType | null; +} diff --git a/src/store/slices/config/configSlice.ts b/src/store/slices/config/configSlice.ts new file mode 100644 index 0000000..3c4500d --- /dev/null +++ b/src/store/slices/config/configSlice.ts @@ -0,0 +1,22 @@ +import { StateCreator } from 'zustand/vanilla'; +import { StoreType, MutatorsIn } from 'store/store.types'; +import { ConfigSliceType } from './config.types'; + +// Do not export initial state for the config slice. +// This will be permanently defined by the dApp at dApp initialization. +// The config should be changed by using the `setNativeAuthConfig` action in some specific cases. +// Preferably, the config should be set at the dApp initialization and not changed during the dApp lifecycle. (e.g. when the user logs in/log out) +const initialState: ConfigSliceType = { + nativeAuthConfig: null +}; + +function getConfigSlice(): StateCreator< + StoreType, + MutatorsIn, + [], + ConfigSliceType +> { + return () => initialState; +} + +export const configSlice = getConfigSlice(); diff --git a/src/store/slices/config/index.ts b/src/store/slices/config/index.ts new file mode 100644 index 0000000..94cbeb5 --- /dev/null +++ b/src/store/slices/config/index.ts @@ -0,0 +1 @@ +export { configSlice } from './configSlice'; diff --git a/src/store/slices/helpers/getActions.ts b/src/store/slices/helpers/getActions.ts deleted file mode 100644 index 17e45be..0000000 --- a/src/store/slices/helpers/getActions.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { getKeys } from './getKeys'; -import { GetSetType } from './types'; - -// Define a function getActions that takes a set function and a set of actions, and returns a function to create wrapped actions -export const getActions = any>>({ - set, - actions, - shouldReplace = false -}: { - set: GetSetType; - actions: A; - shouldReplace?: boolean; -}) => { - type ActionType = typeof actions; - - // Define the return type for the wrapped actions - type CreateSetterReturnType = { - [P in K]: (...args: Parameters) => void; - }; - - const keys = getKeys(actions); // Generate a mapping of action names to keys using the getKeys helper function - - // Define a wrapper function that takes an object with action implementations and returns wrapped actions - const actionsCreator = (actionObj: { - [P in K]: (state: T, ...args: Parameters) => void; - }): CreateSetterReturnType => { - // Initialize the result object to store the wrapped actions - const result = {} as CreateSetterReturnType; - - // Iterate over each action in the provided action object - for (const name in actionObj) { - const func = actionObj[name]; // Get the action implementation function - - // Create a wrapped function for each action that sets the state and dispatches an action type - result[name] = (...args: Parameters) => - set((state) => func(state, ...args), shouldReplace, { - type: keys[name] - }); - } - - return result; - }; - - // Return the wrapper function from getActions - return actionsCreator; -}; diff --git a/src/store/slices/helpers/getVanillaStore.ts b/src/store/slices/helpers/getVanillaStore.ts deleted file mode 100644 index 83f95cf..0000000 --- a/src/store/slices/helpers/getVanillaStore.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { storage } from 'constants/storage'; -import { StoreApi, createStore } from 'zustand'; -import { devtools, persist } from 'zustand/middleware'; -import { immer } from 'zustand/middleware/immer'; -import { GetSetType } from './../helpers/types'; - -const getStore = ({ - definitionFunc, - name -}: { - definitionFunc: (...a: Parameters>) => T; - name: string; -}) => - createStore()( - devtools( - persist( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore:next-line - immer((...a) => definitionFunc(...a)), - { - name, - storage - } - ) - ) - ); - -type GetDefinitionType = { - definition: (set: GetSetType) => T; - middleware?: Array<(...args: any) => any>; -}; - -// Definition function combining definition and handleLogout -const getDefinition = - ({ definition, middleware = [] }: GetDefinitionType) => - (...a: Parameters>) => { - const appliedMiddleware = middleware.reduce((acc, current) => { - acc = { - ...acc, - ...current(...a) - }; - return acc; - }, {}); - - return { - ...definition(...(a as Parameters)), - ...appliedMiddleware - }; - }; - -export const getVanillaStore = ({ - name, - definition, - middleware = [] -}: GetDefinitionType & { name: string }) => - getStore({ - definitionFunc: getDefinition({ definition, middleware }), - name - }); diff --git a/src/store/slices/helpers/index.ts b/src/store/slices/helpers/index.ts deleted file mode 100644 index 26337ae..0000000 --- a/src/store/slices/helpers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './getActions'; diff --git a/src/store/slices/index.ts b/src/store/slices/index.ts index 5451f0f..80d5a64 100644 --- a/src/store/slices/index.ts +++ b/src/store/slices/index.ts @@ -1,2 +1,4 @@ export * from './account'; export * from './network'; +export * from './loginInfo'; +export * from './config'; diff --git a/src/store/slices/loginInfo/index.ts b/src/store/slices/loginInfo/index.ts index 33acda8..45c466b 100644 --- a/src/store/slices/loginInfo/index.ts +++ b/src/store/slices/loginInfo/index.ts @@ -1 +1 @@ -export * from './loginInfoSlice'; +export { loginInfoSlice } from './loginInfoSlice'; diff --git a/src/store/slices/network/index.ts b/src/store/slices/network/index.ts index 2dbcb1d..47a3c69 100644 --- a/src/store/slices/network/index.ts +++ b/src/store/slices/network/index.ts @@ -1 +1 @@ -export * from './networkSlice'; +export { networkSlice } from './networkSlice'; diff --git a/src/store/slices/shared/listenToLogin.ts b/src/store/slices/shared/listenToLogin.ts deleted file mode 100644 index 067c8a0..0000000 --- a/src/store/slices/shared/listenToLogin.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { SharedActionsEnum } from 'store/actions/constants'; -import { getListenToEvent } from 'store/helpers/eventHandlers'; -import { LoginMethodsEnum } from 'types/enums.types'; - -export interface LoginActionPayloadType { - address: string; - loginMethod: LoginMethodsEnum; -} - -export const listenToLogin = getListenToEvent( - SharedActionsEnum.LOGIN -); diff --git a/src/store/store.ts b/src/store/store.ts index 917579e..32d2e3a 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -6,12 +6,11 @@ import { defaultStorageCallback, StorageCallback } from './storage'; -import { networkSlice } from './slices/network/networkSlice'; -import { accountSlice } from './slices/account/accountSlice'; +import { networkSlice, accountSlice, loginInfoSlice } from './slices'; import { createBoundedUseStore } from './createBoundedStore'; -import { loginInfoSlice } from './slices/loginInfo'; import { StoreType } from './store.types'; -import { applyMiddlewares } from './middleware/applyMiddlewares'; +import { applyMiddlewares } from './middleware'; +import { configSlice } from './slices'; export type MutatorsIn = [ ['zustand/devtools', never], @@ -32,7 +31,8 @@ export const createDAppStore = (getStorageCallback: StorageCallback) => { immer((...args) => ({ network: networkSlice(...args), account: accountSlice(...args), - loginInfo: loginInfoSlice(...args) + loginInfo: loginInfoSlice(...args), + config: configSlice(...args) })), { name: 'sdk-dapp-store', diff --git a/src/store/store.types.ts b/src/store/store.types.ts index 8fa32da..8145523 100644 --- a/src/store/store.types.ts +++ b/src/store/store.types.ts @@ -1,11 +1,13 @@ import { AccountSliceType } from './slices/account/account.types'; import { LoginInfoSliceType } from './slices/loginInfo/loginInfo.types'; import { NetworkSliceType } from './slices/network/networkSlice.types'; +import { ConfigSliceType } from './slices/config/config.types'; export type StoreType = { network: NetworkSliceType; account: AccountSliceType; loginInfo: LoginInfoSliceType; + config: ConfigSliceType; }; export type MutatorsIn = [ diff --git a/src/types/login.types.ts b/src/types/login.types.ts index 4a5f053..a7b9a5c 100644 --- a/src/types/login.types.ts +++ b/src/types/login.types.ts @@ -12,8 +12,4 @@ export interface TokenLoginType { loginToken: string; signature?: string; nativeAuthToken?: string; - /** - * config to be restored when web wallet provider returns url signature - */ - nativeAuthConfig?: NativeAuthConfigType; } diff --git a/src/utils/account/getAddress.ts b/src/utils/account/getAddress.ts deleted file mode 100644 index c65e0a5..0000000 --- a/src/utils/account/getAddress.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { addressSelector } from 'store/selectors'; -import { getState } from 'store/store'; - -export const getAddress = () => { - return addressSelector(getState()); -}; diff --git a/src/utils/account/getIsLoggedIn.ts b/src/utils/account/getIsLoggedIn.ts deleted file mode 100644 index 722dd1c..0000000 --- a/src/utils/account/getIsLoggedIn.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { getAddress } from './getAddress'; - -export const getIsLoggedIn = () => { - return Boolean(getAddress()); -}; diff --git a/src/utils/account/index.ts b/src/utils/account/index.ts index e8c98d1..7603022 100644 --- a/src/utils/account/index.ts +++ b/src/utils/account/index.ts @@ -1,2 +1 @@ export * from './getAccount'; -export * from './getLatestNonce'; diff --git a/yarn.lock b/yarn.lock index 9eef0d1..2b70c98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -996,10 +996,10 @@ json-bigint "1.0.0" keccak "3.0.2" -"@multiversx/sdk-dapp-utils@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@multiversx/sdk-dapp-utils/-/sdk-dapp-utils-0.0.1.tgz#ba8bd9319649d45dea9a76beca1e03d957dea2cb" - integrity sha512-fl3TdES93Jc4T559BI+QxNRGRUTabb7TiAXHKL9g6mbLD+silK+5euAoDpPBkbZpVFnfsXQssUVuyKBV4Ine6w== +"@multiversx/sdk-dapp-utils@>= 0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@multiversx/sdk-dapp-utils/-/sdk-dapp-utils-0.1.0.tgz#3103c2ffc648703e75f96acd229af299cb0840ab" + integrity sha512-EFvktZ/S1WQ1ie02nnKZHARC4r23JZWwoTFd5py1qi/Z/UoLHIzJ394HLjXFb6gBTsp4wnvNwIXBA/DNrd2Yeg== "@multiversx/sdk-extension-provider@3.0.0": version "3.0.0" @@ -1056,10 +1056,10 @@ "@walletconnect/utils" "2.12.2" bech32 "1.1.4" -"@multiversx/sdk-web-wallet-cross-window-provider@>= 0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@multiversx/sdk-web-wallet-cross-window-provider/-/sdk-web-wallet-cross-window-provider-0.4.2.tgz#49f6d7f526bd3ed16bb7c9eb85b084d710f2168f" - integrity sha512-0J99FYrMyQ9wzjGJKGc6nim2s4tZGMsmEClU/5lZDkTwgbfJd2LdMX2bm4yMYmBE8uUp/qOIHQOXFMZCW7VDFQ== +"@multiversx/sdk-web-wallet-cross-window-provider@>= 1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@multiversx/sdk-web-wallet-cross-window-provider/-/sdk-web-wallet-cross-window-provider-1.0.0.tgz#14ea0eb110de78a7e5dfbb1cb237cc8f86006b40" + integrity sha512-xqdKCFpBCxNcp4aSwC2FLbks2Ii2uy5YpHnqR8qnqCnjH6TqdGZ1xKzQauZsiYqseVueVTmynK28w9pTOZ0Oqg== dependencies: "@types/jest" "^29.5.11" "@types/qs" "6.9.10" @@ -6426,7 +6426,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -6440,6 +6440,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -6998,7 +7005,7 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -7016,6 +7023,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"