From 342cc914139dca7cc82bff780f6bd5815113855d Mon Sep 17 00:00:00 2001 From: Ignacio Date: Sun, 21 Jan 2024 22:26:21 +0800 Subject: [PATCH] refactor: reorganise staking section --- .github/workflows/test.yml | 1 + e2e/poms/staking_page.ts | 2 +- jest.config.js | 17 + package.json | 5 +- public/locales/en/staking.json | 6 +- public/locales/zh-CN/staking.json | 6 +- public/locales/zh-HK/staking.json | 6 +- scripts/check_ci.sh | 1 + scripts/lint.sh | 1 + src/hooks/use_middle_ellipsis.ts | 23 + .../guide/components/guide_details/index.tsx | 28 - .../components/network_info/index.tsx | 9 - src/screens/staking/components/hooks.ts | 6 +- .../components/network_card/index.tsx | 5 +- .../network_card/popover.module.scss | 22 +- .../components/network_card/popover.tsx | 168 +- .../components/network_grid/index.tsx | 3 - .../staking/components/networks/index.tsx | 2 +- .../staking_section/claim_rewards_modal.tsx | 4 +- .../staking_section/connect_wallet_modal.tsx | 6 +- .../networks_select.module.scss | 33 + .../staking_section/networks_select.tsx | 81 +- .../staking_section/staking_modal.module.scss | 6 + .../staking_section/staking_modal.tsx | 43 +- .../staking_section/unstaking_modal.tsx | 25 +- .../components/staking_widget/index.tsx | 14 +- .../staking_sdk/__tests__/formatters.test.ts | 10 + .../staking/lib/staking_sdk/context.tsx | 197 +- src/screens/staking/lib/staking_sdk/core.ts | 17 +- .../staking/lib/staking_sdk/formatters.ts | 76 +- .../staking/lib/staking_sdk/staking_client.ts | 1 + .../staking/lib/staking_sdk/utils/accounts.ts | 9 +- .../staking/lib/staking_sdk/utils/coins.ts | 31 +- .../staking/lib/staking_sdk/utils/networks.ts | 49 + .../lib/staking_sdk/wallet_operations.ts | 238 +-- src/tests/styleMock.ts | 1 + src/tests/tsconfig.test.json | 6 + yarn.lock | 1623 ++++++++++++++++- 38 files changed, 2298 insertions(+), 483 deletions(-) create mode 100644 jest.config.js create mode 100644 src/hooks/use_middle_ellipsis.ts create mode 100644 src/screens/staking/lib/staking_sdk/__tests__/formatters.test.ts create mode 100644 src/screens/staking/lib/staking_sdk/utils/networks.ts create mode 100644 src/tests/styleMock.ts create mode 100644 src/tests/tsconfig.test.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 441bb081..1d4294a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,5 +12,6 @@ jobs: - run: yarn --immutable - run: yarn lint + - run: yarn test - run: yarn type-check - run: yarn build diff --git a/e2e/poms/staking_page.ts b/e2e/poms/staking_page.ts index 4e965114..2be3e756 100644 --- a/e2e/poms/staking_page.ts +++ b/e2e/poms/staking_page.ts @@ -1,10 +1,10 @@ import type { Coin } from "@cosmjs/stargate"; import type { Page } from "@playwright/test"; +import type { TStakingContext } from "@src/screens/staking/lib/staking_sdk/context"; import type { Account, StakingNetworkId, - TStakingContext, Wallet, } from "@src/screens/staking/lib/staking_sdk/core"; import { WalletId } from "@src/screens/staking/lib/staking_sdk/core"; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..d6e30c73 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + moduleNameMapper: { + "\\.(css)$": "/src/tests/styleMock.ts", + "\\.(scss)$": "/src/tests/styleMock.ts", + "^@src/(.*)$": "/src/$1", + }, + preset: "ts-jest", + testEnvironment: "node", + transform: { + "\\.(ts|tsx)$": [ + "ts-jest", + { + tsconfig: "/src/tests/tsconfig.test.json", + }, + ], + }, +}; diff --git a/package.json b/package.json index 769c8360..6940fa74 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "postinstall": "husky install", "scss-types": "typed-scss-modules src", "start": "next start", - "test": "echo no tests", + "test": "jest src", "type-check": "tsc" }, "dependencies": { @@ -67,6 +67,7 @@ "@stylistic/eslint-plugin": "^1.5.4", "@svgr/webpack": "^8.1.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/jest": "^29.5.11", "@types/node": "^20.11.5", "@types/nodemailer": "^6.4.14", "@types/ramda": "^0.29.9", @@ -85,6 +86,7 @@ "eslint-plugin-playwright": "^0.22.1", "husky": "^8.0.3", "i18next-parser": "^8.12.0", + "jest": "^29.7.0", "postcss": "^8.4.33", "prettier": "^3.2.4", "rss": "^1.2.2", @@ -93,6 +95,7 @@ "stylelint-config-standard-scss": "^13.0.0", "stylelint-order": "^6.0.4", "stylelint-scss": "^6.0.0", + "ts-jest": "^29.1.1", "ts-unused-exports": "^10.0.1", "tsx": "^4.7.0", "typed-scss-modules": "^8.0.0", diff --git a/public/locales/en/staking.json b/public/locales/en/staking.json index 3d882884..ebd6e226 100644 --- a/public/locales/en/staking.json +++ b/public/locales/en/staking.json @@ -1,4 +1,5 @@ { + "addressCopied": "Address copied", "apy": "Annual Percentage Yield", "benefits bullet 1": "Earn your staking rewards, it allows you to earn rewards for securing the network with your contribution", "benefits bullet 2": "Boost network security, the more token holders that stake, the more secure the network becomes", @@ -115,12 +116,14 @@ "memoError": { "tooLongMemo": "Please enter a smaller memo" }, + "networkSelect": "Select network:", "success": { "sub": "Thank you for supporting Forbole !", "title": "Your token has been staked!" }, "title": "Stake with Forbole", - "tokenAmount": "Token amount" + "tokenAmount": "Token amount", + "walletSellect": "Stake from:" }, "stakingWidget": { "connected_one": "Connected wallet", @@ -164,6 +167,7 @@ "title": "Unstake", "unlockedDateInfo": "Your token will be locked in until <1>{{date}} ({{days}} days later)." }, + "votingPower": "Voting Power", "wallets": { "keplr": "Keplr" }, diff --git a/public/locales/zh-CN/staking.json b/public/locales/zh-CN/staking.json index 0741a90f..4e82c710 100644 --- a/public/locales/zh-CN/staking.json +++ b/public/locales/zh-CN/staking.json @@ -1,4 +1,5 @@ { + "addressCopied": "Address copied", "apy": "年收益率", "benefits bullet 1": "赚取您的质押奖励,它可以让您通过贡献来获得保护网络的奖励", "benefits bullet 2": "提高网络安全性,质押的代币持有者越多,网络就越安全", @@ -115,12 +116,14 @@ "memoError": { "tooLongMemo": "Please enter a smaller memo" }, + "networkSelect": "Select network:", "success": { "sub": "Thank you for supporting Forbole !", "title": "Your token has been staked!" }, "title": "Stake with forbole", - "tokenAmount": "Token amount" + "tokenAmount": "Token amount", + "walletSellect": "Stake from:" }, "stakingWidget": { "connected_other": "Connected wallet", @@ -163,6 +166,7 @@ "title": "Unstake", "unlockedDateInfo": "Your token will be locked in until <2>{{date}} ({{days}} days later)." }, + "votingPower": "Voting Power", "wallets": { "keplr": "Keplr" }, diff --git a/public/locales/zh-HK/staking.json b/public/locales/zh-HK/staking.json index 26ab8e3e..909c1791 100644 --- a/public/locales/zh-HK/staking.json +++ b/public/locales/zh-HK/staking.json @@ -1,4 +1,5 @@ { + "addressCopied": "Address copied", "apy": "年報酬率", "benefits bullet 1": "賺取您的質押獎勵,它可以讓您透過貢獻來獲得保護網路的獎勵", "benefits bullet 2": "提高網路安全性,質押的代幣持有者越多,網路就越安全", @@ -115,12 +116,14 @@ "memoError": { "tooLongMemo": "Please enter a smaller memo" }, + "networkSelect": "Select network:", "success": { "sub": "Thank you for supporting Forbole !", "title": "Your token has been staked!" }, "title": "Stake with forbole", - "tokenAmount": "Token amount" + "tokenAmount": "Token amount", + "walletSellect": "Stake from:" }, "stakingWidget": { "connected_other": "Connected wallet", @@ -163,6 +166,7 @@ "title": "Unstake", "unlockedDateInfo": "Your token will be locked in until <2>{{date}} ({{days}} days later)." }, + "votingPower": "Voting Power", "wallets": { "keplr": "Keplr" }, diff --git a/scripts/check_ci.sh b/scripts/check_ci.sh index 5d8ec4f8..2c81f86a 100755 --- a/scripts/check_ci.sh +++ b/scripts/check_ci.sh @@ -4,5 +4,6 @@ set -e yarn lint yarn type-check +yarn test QUICK_BUILD=true yarn build diff --git a/scripts/lint.sh b/scripts/lint.sh index fe185ef5..bdea32f4 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -15,6 +15,7 @@ yarn ts-unused-exports tsconfig.json \ --excludePathsFromReport='.*rss.xml' \ --excludePathsFromReport='i18next-parser.config' \ --excludePathsFromReport='index-poc' \ + --excludePathsFromReport='styleMock' \ --excludePathsFromReport='playwright.config' yarn i18next diff --git a/src/hooks/use_middle_ellipsis.ts b/src/hooks/use_middle_ellipsis.ts new file mode 100644 index 00000000..49d2b9ff --- /dev/null +++ b/src/hooks/use_middle_ellipsis.ts @@ -0,0 +1,23 @@ +import { useEffect, useState } from "react"; + +export const useMiddleEllipsis = (text: string, chars = 10) => { + const [parsedText, setParsedText] = useState(""); + + useEffect(() => { + const listener = () => { + const parsed = `${text.substring(0, chars)}...${text.substring( + text.length - chars, + text.length, + )}`; + + setParsedText(parsed); + }; + + // In the future this can be updated to listen to a resize event + listener(); + }, [text, chars]); + + if (!parsedText) return null; + + return parsedText; +}; diff --git a/src/screens/network_guides/components/guide/components/guide_details/index.tsx b/src/screens/network_guides/components/guide/components/guide_details/index.tsx index 534325a7..05dead5f 100644 --- a/src/screens/network_guides/components/guide/components/guide_details/index.tsx +++ b/src/screens/network_guides/components/guide/components/guide_details/index.tsx @@ -1,37 +1,10 @@ import DOMPurify from "isomorphic-dompurify"; -import useTranslation from "next-translate/useTranslation"; -import Link from "next/link"; - -import HighlightButton from "@src/components/highlight-button"; -import { getNetworkInfo } from "@src/utils/network_info"; import * as styles from "./index.module.scss"; const GuideDetails = ({ post }: any) => { - const { tags } = post; - const { t } = useTranslation("staking"); - - const networkData = - // eslint-disable-next-line no-nested-ternary - tags.length <= 1 - ? null - : tags[1].slug === "crypto-org" - ? getNetworkInfo("crypto.org") - : getNetworkInfo(tags[1].slug); - const { sanitize } = DOMPurify; - const ctaLink = !!networkData?.delegate ? ( -
- - {t("stake_now")} - -
- ) : null; - return (
@@ -41,7 +14,6 @@ const GuideDetails = ({ post }: any) => { dangerouslySetInnerHTML={{ __html: sanitize(post.html) }} />
- {ctaLink}
); diff --git a/src/screens/network_guides/components/network_info/index.tsx b/src/screens/network_guides/components/network_info/index.tsx index 7ad8d14c..6339d495 100644 --- a/src/screens/network_guides/components/network_info/index.tsx +++ b/src/screens/network_guides/components/network_info/index.tsx @@ -11,11 +11,9 @@ import { import DOMPurify from "isomorphic-dompurify"; import useTranslation from "next-translate/useTranslation"; import Image from "next/legacy/image"; -import Link from "next/link"; import type { MouseEvent } from "react"; import { useCallback, useState } from "react"; -import HighlightButton from "@src/components/highlight-button"; import CopyIcon from "@src/components/icons/icon_copy.svg"; import * as guideStyles from "@src/screens/network_guides/components/guide/components/guide_details/index.module.scss"; import { getMiddleEllipsis } from "@src/utils/get_middle_ellipsis"; @@ -140,13 +138,6 @@ const NetworkInfo = ({ post }: any) => { )} - {!!networkData?.delegate && ( -
- - {t("stake_now")} - -
- )}
diff --git a/src/screens/staking/components/hooks.ts b/src/screens/staking/components/hooks.ts index f3bd0d00..d30069fa 100644 --- a/src/screens/staking/components/hooks.ts +++ b/src/screens/staking/components/hooks.ts @@ -114,11 +114,7 @@ export const useInitStaking = () => { fetchNetworksInfo(stakingRef.current.setState); - tryToConnectWallets( - stakingRef.current.state, - stakingRef.current.setState, - connectedWallets, - ).then(() => { + tryToConnectWallets(stakingRef.current, connectedWallets).then(() => { stakingRef.current.setState({ hasInit: true, }); diff --git a/src/screens/staking/components/networks/components/network_grid/components/network_card/index.tsx b/src/screens/staking/components/networks/components/network_grid/components/network_card/index.tsx index 6b046a45..aa6a1f64 100644 --- a/src/screens/staking/components/networks/components/network_grid/components/network_card/index.tsx +++ b/src/screens/staking/components/networks/components/network_grid/components/network_card/index.tsx @@ -19,7 +19,6 @@ import PopOver from "./popover"; import StakingLabel from "./staking-label"; interface CardProp { - canStakeToAnyWallet: boolean; network: Network; networkSummary: ParamsProps; setShowPopover: Dispatch>; @@ -27,7 +26,6 @@ interface CardProp { } const NetworkCard = ({ - canStakeToAnyWallet, network, networkSummary, setShowPopover, @@ -49,8 +47,7 @@ const NetworkCard = ({ [network], ); - const chainSupportsStaking = - canStakeToAnyWallet && networkKeyToNetworkId[network.key as NetworkKey]; + const chainSupportsStaking = networkKeyToNetworkId[network.key as NetworkKey]; const isEmptyPopover = networksWithHiddenInfo.has(network.graphql) || diff --git a/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.module.scss b/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.module.scss index 28da52b7..11ae2e9a 100644 --- a/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.module.scss +++ b/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.module.scss @@ -71,14 +71,19 @@ $popover-background: linear-gradient( .label { align-items: center; + color: #25282d; display: flex; flex: 1; flex-flow: row nowrap; - font-size: 20px; - font-weight: 400; + font-size: 16px; + font-style: normal; + font-weight: 600; gap: 2px; justify-content: center; + letter-spacing: 0.032px; + line-height: 20px; text-align: left; + text-shadow: $box-shadow-variant-3; white-space: nowrap; width: 100%; } @@ -171,3 +176,16 @@ $popover-background: linear-gradient( body .stake { font-size: 16px; } + +.totalValue { + > div:nth-child(2) { + color: #616161; + font-size: 14px; + font-style: normal; + font-weight: 400; + letter-spacing: 0.308px; + line-height: 20px; + text-align: right; + text-shadow: $box-shadow-variant-3; + } +} diff --git a/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.tsx b/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.tsx index 1624b15a..6d07fc89 100644 --- a/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.tsx +++ b/src/screens/staking/components/networks/components/network_grid/components/network_card/popover.tsx @@ -1,6 +1,5 @@ import type { Coin } from "@cosmjs/stargate"; import { LinearProgress } from "@mui/material"; -import BigNumber from "bignumber.js"; import useTranslation from "next-translate/useTranslation"; import type { Dispatch, @@ -8,7 +7,7 @@ import type { ReactNode, SetStateAction, } from "react"; -import { useContext, useEffect, useMemo, useRef, useState } from "react"; +import { useContext, useEffect, useMemo, useState } from "react"; import CtaButton from "@src/components/cta-button"; import EmptyButton from "@src/components/empty-button"; @@ -21,8 +20,10 @@ import { StakingContext, getAccountsForNetwork, getClaimableRewardsForNetwork, - getCoinPrice, + getCoinPriceForNetwork, + getHasNetworkSupportedWallet, getNetworkStakingInfo, + getNetworkVotingPower, getStakedDataForNetwork, setSelectedAccount, useStakingRef, @@ -38,9 +39,11 @@ import type { NetworkInfo, StakingNetworkId, } from "@src/screens/staking/lib/staking_sdk/core"; -import { formatCoin } from "@src/screens/staking/lib/staking_sdk/formatters"; +import { + formatCoin, + formatStakedDataUSD, +} from "@src/screens/staking/lib/staking_sdk/formatters"; import { accountHasDelegations } from "@src/screens/staking/lib/staking_sdk/utils/accounts"; -import { normaliseCoin } from "@src/screens/staking/lib/staking_sdk/utils/coins"; import { convertToMoney } from "@src/utils/convert_to_money"; import type { Network, NetworkKey } from "@src/utils/network_info"; @@ -66,7 +69,6 @@ const PopOver = ({ }: PopOverProps) => { const networkNetworkId = networkKeyToNetworkId[network.key as NetworkKey]; const stakingNetworkId = networkKeyToNetworkId[network.key as NetworkKey]; - const requestingCoinPrice = useRef(""); const stakingRef = useStakingRef(); @@ -74,6 +76,11 @@ const PopOver = ({ ? networksWithStaking.has(networkNetworkId) : false; + const hasNetworkSupportedWallet = + isStakingSupported && stakingNetworkId + ? getHasNetworkSupportedWallet(stakingRef.current.state, stakingNetworkId) + : false; + const { t } = useTranslation("staking"); const [stakingNetworkInfo, setStakingNetworkInfo] = @@ -86,13 +93,11 @@ const PopOver = ({ useEffect(() => { if (stakingNetworkId) { - getNetworkStakingInfo( - stakingRef.current.setState, - stakingRef.current.state, - stakingNetworkId, - ).then((newInfo) => { - setStakingNetworkInfo(newInfo); - }); + getNetworkStakingInfo(stakingRef.current, stakingNetworkId).then( + (newInfo) => { + setStakingNetworkInfo(newInfo); + }, + ); } }, [stakingNetworkId, stakingRef]); @@ -127,44 +132,15 @@ const PopOver = ({ return result; }, [stakingState, stakingNetworkId, stakingRef]); - // @TODO: move some logic to the sdk useEffect(() => { - if (!stakingNetworkId) return; - - const parsedDenom = mainNetworkDenom[stakingNetworkId]; - const { coinsPrices } = stakingRef.current.state; - - if ( - parsedDenom && - !coinsPrices[parsedDenom] && - requestingCoinPrice.current !== parsedDenom - ) { - requestingCoinPrice.current = parsedDenom; - - (async () => { - await getCoinPrice( - stakingRef.current.state, - stakingRef.current.setState, - parsedDenom, - ); - - requestingCoinPrice.current = ""; - })(); - } + getCoinPriceForNetwork(stakingRef.current, stakingNetworkId); }, [stakingRef, stakingNetworkId]); const accountsWithDelegations = accounts?.filter(accountHasDelegations); - // @TODO: Move to sdk - const displayedRewards = (() => { - if (!claimableRewards?.usd) return null; - - if (claimableRewards.usd.lt(new BigNumber(10).pow(-5))) { - return `< ${claimableRewards.usd.toFormat(5)} USD`; - } - - return `${claimableRewards.usd.toFormat(5)} USD`; - })(); + const displayedRewards = claimableRewards + ? `+${formatCoin(claimableRewards)}` + : null; const displayedStaked = (() => { if (!stakedData || !stakingNetworkId) return null; @@ -177,15 +153,9 @@ const PopOver = ({ if (!coinPrice) return [formatCoin(stakedData)]; - const stakedNormalised = normaliseCoin(stakedData); - const coinNum = new BigNumber(stakedNormalised.amount); - const coinValue = coinNum.multipliedBy(new BigNumber(coinPrice)); + const stakedDataUSD = formatStakedDataUSD(stakedData, coinPrice); - if (coinValue.isNaN() || coinValue.lt(new BigNumber(10).pow(-5))) { - return [formatCoin(stakedData), `< ${coinValue.toFormat(5)} USD`]; - } - - return [formatCoin(stakedData), `≈ ${coinValue.toFormat(5)} USD`]; + return [formatCoin(stakedData), stakedDataUSD].filter(Boolean); })(); return ( @@ -207,7 +177,7 @@ const PopOver = ({ {displayedStaked && (
{t("totalStaked")}
-
+
{displayedStaked.map((item, itemIdx) => (
{item}
))} @@ -224,20 +194,44 @@ const PopOver = ({ )} {!!networkSummary && (
- {!!networkSummary.bonded && networkSummary.bonded > 0 && ( -
-
- - {network.denom?.toUpperCase()} -
- - {convertToMoney(networkSummary.bonded)} - -
- )} + {(() => { + const votingPower = (() => { + if (stakingNetworkId) { + const parsedVotingPower = getNetworkVotingPower( + stakingRef.current.state, + stakingNetworkId, + ); + + if (!parsedVotingPower) return null; + + return parsedVotingPower + ? [t("votingPower"), formatCoin(parsedVotingPower, 0)] + : null; + } + + return networkSummary?.bonded > 0 + ? [ + network.denom?.toUpperCase(), + convertToMoney(networkSummary.bonded), + ] + : null; + })(); + + return ( + !!votingPower && ( +
+
+ + {votingPower[0]} +
+ {votingPower[1]} +
+ ) + ); + })()} {(() => { const apy = (() => { if (stakingNetworkId) return stakingNetworkInfo?.apy; @@ -294,18 +288,32 @@ const PopOver = ({ )}
- {isStakingSupported && !!accounts?.length && ( + {isStakingSupported && ( <> - { - setSelectedAccount(setStakingState, "stake", accounts[0]); - }} - > - {t("popover.stake")} - - {!!claimableRewards && ( + {hasInit && (!hasNetworkSupportedWallet || !!accounts?.length) && ( + { + if (!hasNetworkSupportedWallet) { + setSelectedAccount( + stakingRef.current.setState, + "connect_wallet", + null, + ); + + return; + } + + if (!accounts?.length) return; + + setSelectedAccount(setStakingState, "stake", accounts[0]); + }} + > + {t("popover.stake")} + + )} + {!!claimableRewards && !!accounts?.length && ( { setSelectedAccount( diff --git a/src/screens/staking/components/networks/components/network_grid/index.tsx b/src/screens/staking/components/networks/components/network_grid/index.tsx index d53d62af..99e1302a 100644 --- a/src/screens/staking/components/networks/components/network_grid/index.tsx +++ b/src/screens/staking/components/networks/components/network_grid/index.tsx @@ -2,7 +2,6 @@ import { LayoutGroup } from "framer-motion"; import { useState } from "react"; import { NoSSR } from "@src/components/no-ssr"; -import { getCanStakeToAnyWallet } from "@src/screens/staking/lib/staking_sdk/wallet_operations"; import type { Network } from "@src/utils/network_info"; import NetworkCard from "./components/network_card"; @@ -16,7 +15,6 @@ type Props = { const NetworkGrid = ({ allNetworkInfo, sortedNetworks }: Props) => { const [showPopover, setShowPopover] = useState(""); - const canStakeToAnyWallet = getCanStakeToAnyWallet(); return (
@@ -24,7 +22,6 @@ const NetworkGrid = ({ allNetworkInfo, sortedNetworks }: Props) => { {sortedNetworks.map((network, index) => ( { if (isOpen && networkId && address) { setGasFee(null); - getClaimRewardsFee({ address, networkId }).then((fee) => { - setGasFee(fee); - }); + getClaimRewardsFee({ address, networkId }).then(setGasFee); } }, [isOpen, networkId, address]); diff --git a/src/screens/staking/components/staking_section/connect_wallet_modal.tsx b/src/screens/staking/components/staking_section/connect_wallet_modal.tsx index 42d37935..aef35867 100644 --- a/src/screens/staking/components/staking_section/connect_wallet_modal.tsx +++ b/src/screens/staking/components/staking_section/connect_wallet_modal.tsx @@ -51,11 +51,7 @@ const ConnectWalletModal = () => { onClick={() => { setIsLoading(true); - tryToConnectWallets( - stakingRef.current.state, - stakingRef.current.setState, - [walletId], - ) + tryToConnectWallets(stakingRef.current, [walletId], true) .then((connected) => { if (connected) { toastSuccess({ diff --git a/src/screens/staking/components/staking_section/networks_select.module.scss b/src/screens/staking/components/staking_section/networks_select.module.scss index a9c21015..8258cdb8 100644 --- a/src/screens/staking/components/staking_section/networks_select.module.scss +++ b/src/screens/staking/components/staking_section/networks_select.module.scss @@ -1,5 +1,6 @@ @import "src/styles/sass.scss"; +.singleItem, .control { border-radius: 8px; box-shadow: @@ -22,10 +23,12 @@ .row { align-items: center; display: flex; + flex: 1; flex-direction: row; gap: 20px; justify-content: flex-start; min-height: 70px; + width: 100%; } .select { @@ -70,3 +73,33 @@ line-height: 20px; } } + +.rewards { + color: #1ec490; + flex: 1; + font-size: 18px; + font-style: normal; + font-weight: 600; + letter-spacing: 0.108px; + line-height: 21px; + text-align: right; +} + +.singleItem { + align-items: center; + display: flex; + flex-direction: row; + padding: 16px; +} + +.address { + background: none; + border: none; + color: #9d9d9d; + cursor: pointer; + font-size: 14px; + font-style: normal; + font-weight: 400; + letter-spacing: 0.308px; + line-height: 20px; +} diff --git a/src/screens/staking/components/staking_section/networks_select.tsx b/src/screens/staking/components/staking_section/networks_select.tsx index e836bc86..5867fd79 100644 --- a/src/screens/staking/components/staking_section/networks_select.tsx +++ b/src/screens/staking/components/staking_section/networks_select.tsx @@ -1,18 +1,27 @@ import MenuItem from "@mui/material/MenuItem"; import Select from "@mui/material/Select"; +import useTranslation from "next-translate/useTranslation"; +import { toastSuccess } from "@src/components/notification"; +import { useMiddleEllipsis } from "@src/hooks/use_middle_ellipsis"; import { + getClaimableRewardsForNetwork, getSelectedAccount, getWalletAccounts, setSelectedAccount, useStakingRef, } from "@src/screens/staking/lib/staking_sdk/context"; -import type { StakingNetworkId } from "@src/screens/staking/lib/staking_sdk/core"; +import type { + Account, + StakingNetworkId, +} from "@src/screens/staking/lib/staking_sdk/core"; import { networkIdToNetworkKey } from "@src/screens/staking/lib/staking_sdk/core"; +import { formatCoin } from "@src/screens/staking/lib/staking_sdk/formatters"; import { accountHasRewards, getAccountNormalisedBalance, } from "@src/screens/staking/lib/staking_sdk/utils/accounts"; +import { walletsIcons } from "@src/screens/staking/lib/wallet_info"; import type { NetworkKey } from "@src/utils/network_info"; import { getNetworkInfo } from "@src/utils/network_info"; @@ -34,12 +43,13 @@ const MenuProps = { type NetworkItemProps = { denom: string; + rightSide?: React.ReactNode; value: StakingNetworkId; }; const SEPARATOR = "____"; -const NetworkItem = ({ denom, value }: NetworkItemProps) => { +const NetworkItem = ({ denom, rightSide, value }: NetworkItemProps) => { const networkName = networkIdToNetworkKey[value]; const networkInfo = networkName @@ -56,13 +66,48 @@ const NetworkItem = ({ denom, value }: NetworkItemProps) => {
{denom}
{name}
+ {rightSide} +
+ ); +}; + +type WalletItemProps = { + account: Account; + walletName?: string; +}; + +const WalletItem = ({ account, walletName }: WalletItemProps) => { + const { wallet } = account; + const { t } = useTranslation("staking"); + const WalletIcon = walletsIcons[wallet]; + const parsedAddress = useMiddleEllipsis(account.address); + + return ( +
+ {!!WalletIcon && } +
+
{walletName}
+ +
); }; type Props = { disabled?: boolean; - variant: "accounts_with_rewards" | "accounts"; + variant: "accounts_wallet" | "accounts_with_rewards" | "accounts"; }; const NetworksSelect = ({ disabled, variant }: Props) => { @@ -74,10 +119,24 @@ const NetworksSelect = ({ disabled, variant }: Props) => { if (!variant || !selectedAccount) return null; + const isRewards = variant === "accounts_with_rewards"; + const isWallet = variant === "accounts_wallet"; + + if (isWallet) { + const walletName = + stakingRef.current.state.wallets[selectedAccount.wallet]?.name; + + return ( +
+ +
+ ); + } + const allAccounts = getWalletAccounts(stakingState, selectedAccount.wallet); const availableAccounts = allAccounts.filter( - variant === "accounts_with_rewards" ? accountHasRewards : () => true, + isRewards ? accountHasRewards : () => true, ); const handleChange = (event: any) => { @@ -115,10 +174,24 @@ const NetworksSelect = ({ disabled, variant }: Props) => { if (!balance) return null; + const rightSide = (() => { + if (!isRewards) return null; + + const rewards = getClaimableRewardsForNetwork( + stakingRef.current.state, + account.networkId, + ); + + if (!rewards) return null; + + return
+{formatCoin(rewards)}
; + })(); + return ( diff --git a/src/screens/staking/components/staking_section/staking_modal.module.scss b/src/screens/staking/components/staking_section/staking_modal.module.scss index 4ec5437d..0d9b8839 100644 --- a/src/screens/staking/components/staking_section/staking_modal.module.scss +++ b/src/screens/staking/components/staking_section/staking_modal.module.scss @@ -47,3 +47,9 @@ line-height: normal; text-shadow: $box-shadow-variant-3; } + +.selectGroup { + display: flex; + flex-direction: column; + gap: 12px; +} diff --git a/src/screens/staking/components/staking_section/staking_modal.tsx b/src/screens/staking/components/staking_section/staking_modal.tsx index 7d53c662..46fb8293 100644 --- a/src/screens/staking/components/staking_section/staking_modal.tsx +++ b/src/screens/staking/components/staking_section/staking_modal.tsx @@ -1,3 +1,4 @@ +import BigNumber from "bignumber.js"; import useTranslation from "next-translate/useTranslation"; import { useEffect, useState } from "react"; @@ -16,11 +17,9 @@ import { useStakingRef, } from "@src/screens/staking/lib/staking_sdk/context"; import type { NetworkInfo } from "@src/screens/staking/lib/staking_sdk/core"; -import { - formatCoin, - normaliseDenom, -} from "@src/screens/staking/lib/staking_sdk/formatters"; +import { formatCoin } from "@src/screens/staking/lib/staking_sdk/formatters"; import { getAccountNormalisedBalance } from "@src/screens/staking/lib/staking_sdk/utils/accounts"; +import { normaliseDenom } from "@src/screens/staking/lib/staking_sdk/utils/coins"; import { MAX_MEMO, stakeAmount, @@ -55,11 +54,7 @@ const StakingModal = () => { const { networkId } = selectedAccount; - getNetworkStakingInfo( - stakingRef.current.setState, - stakingRef.current.state, - networkId, - ).then((info) => { + getNetworkStakingInfo(stakingRef.current, networkId).then((info) => { setNetworkInfo(info); }); @@ -79,13 +74,10 @@ const StakingModal = () => { if (!balance || !account) return null; - const amountNum = Number(amount); + const amountNum = new BigNumber(amount); const isValidAmount = - !Number.isNaN(amountNum) && - amountNum > 0 && - typeof balance.num === "number" && - amountNum <= balance.num; + !amountNum.isNaN() && amountNum.gt(0) && amountNum.lt(balance.num); const onClose = () => { if (isLoading) return; @@ -139,7 +131,7 @@ const StakingModal = () => { }) .then(async (result) => { if (result.success) { - await syncAccountData(setStakingState, stakingState, selectedAccount); + await syncAccountData(stakingRef.current, selectedAccount); setSelectedAccount(setStakingState, null, null); @@ -162,10 +154,13 @@ const StakingModal = () => { return (
-
- {selectedAccount && ( - - )} +
+ +
+ {selectedAccount && ( + + )} +
{networkInfo?.apy && (
@@ -179,11 +174,19 @@ const StakingModal = () => {
{(networkInfo.apy * 100).toFixed(0)}%
)} +
+ +
+ {selectedAccount && ( + + )} +
+
- {typeof balance?.num === "number" && ( + {!!balance?.num && ( <> :{" "} diff --git a/src/screens/staking/components/staking_section/unstaking_modal.tsx b/src/screens/staking/components/staking_section/unstaking_modal.tsx index 75abd9a9..beb937d3 100644 --- a/src/screens/staking/components/staking_section/unstaking_modal.tsx +++ b/src/screens/staking/components/staking_section/unstaking_modal.tsx @@ -1,3 +1,4 @@ +import BigNumber from "bignumber.js"; import Trans from "next-translate/Trans"; import useTranslation from "next-translate/useTranslation"; import { useRouter } from "next/router"; @@ -17,12 +18,10 @@ import { useStakingRef, } from "@src/screens/staking/lib/staking_sdk/context"; import type { NetworkInfo } from "@src/screens/staking/lib/staking_sdk/core"; -import { - formatCoin, - getUnbondingTimeForNetwork, - normaliseDenom, -} from "@src/screens/staking/lib/staking_sdk/formatters"; +import { formatCoin } from "@src/screens/staking/lib/staking_sdk/formatters"; import { getAccountNormalisedDelegation } from "@src/screens/staking/lib/staking_sdk/utils/accounts"; +import { normaliseDenom } from "@src/screens/staking/lib/staking_sdk/utils/coins"; +import { getUnbondingTimeForNetwork } from "@src/screens/staking/lib/staking_sdk/utils/networks"; import { MAX_MEMO, unstake, @@ -47,11 +46,7 @@ const UnstakingModal = () => { const { networkId } = selectedAccount; - getNetworkStakingInfo( - stakingRef.current.setState, - stakingRef.current.state, - networkId, - ).then((info) => { + getNetworkStakingInfo(stakingRef.current, networkId).then((info) => { setNetworkInfo(info); }); } @@ -82,13 +77,13 @@ const UnstakingModal = () => { const availableAmount = getAccountNormalisedDelegation(account); - const amountNum = Number(amount); + const amountNum = new BigNumber(amount); const isValidAmount = - !Number.isNaN(amountNum) && - amountNum > 0 && + !amountNum.isNaN() && + amountNum.gt(0) && !!availableAmount?.num && - amountNum <= availableAmount.num; + amountNum.lt(availableAmount.num); const unlockedDate = getUnbondingTimeForNetwork(networkInfo, locale); @@ -135,7 +130,7 @@ const UnstakingModal = () => { }) .then(async (unstaked) => { if (unstaked.success) { - await syncAccountData(setStakingState, stakingState, selectedAccount); + await syncAccountData(stakingRef.current, selectedAccount); setSelectedAccount(setStakingState, null, null); diff --git a/src/screens/staking/components/staking_widget/index.tsx b/src/screens/staking/components/staking_widget/index.tsx index 63289eef..0fbef1f8 100644 --- a/src/screens/staking/components/staking_widget/index.tsx +++ b/src/screens/staking/components/staking_widget/index.tsx @@ -7,6 +7,7 @@ import IconMobile from "@src/components/icons/icon_logout.svg"; import IconPlus from "@src/components/icons/icon_plus.svg"; import LoadingSpinner from "@src/components/loading_spinner"; import { tooltipId } from "@src/components/tooltip"; +import type { TStakingContext } from "@src/screens/staking/lib/staking_sdk/context"; import { disconnectWallet, getCanAddWallet, @@ -14,7 +15,6 @@ import { useStakingRef, } from "@src/screens/staking/lib/staking_sdk/context"; import type { - TStakingContext, Wallet, WalletId, } from "@src/screens/staking/lib/staking_sdk/core"; @@ -23,7 +23,6 @@ import { walletsIcons, } from "@src/screens/staking/lib/wallet_info"; -import { getCanStakeToAnyWallet } from "../../lib/staking_sdk/wallet_operations"; import { useInitStaking } from "../hooks"; import ClaimRewardsModal from "../staking_section/claim_rewards_modal"; import ConnectWalletModal from "../staking_section/connect_wallet_modal"; @@ -85,15 +84,10 @@ const StakingWidgetBase = ({ }: Props) => { const { t } = useTranslation("staking"); const walletsIds = Object.keys(wallets).sort() as WalletId[]; - const canStake = getCanStakeToAnyWallet(); const [anchor, setAnchor] = useState(); const onClose = useCallback(() => setAnchor(undefined), [setAnchor]); - if (!canStake) { - return null; - } - if (!hasInit || !walletsIds.length) { return ( { const onDisconnectWallet = useCallback( async (walletId: WalletId) => { - await disconnectWallet( - stakingRef.current.setState, - stakingRef.current.state, - walletId, - ); + await disconnectWallet(stakingRef.current, walletId); }, [stakingRef], ); diff --git a/src/screens/staking/lib/staking_sdk/__tests__/formatters.test.ts b/src/screens/staking/lib/staking_sdk/__tests__/formatters.test.ts new file mode 100644 index 00000000..2f8dd12f --- /dev/null +++ b/src/screens/staking/lib/staking_sdk/__tests__/formatters.test.ts @@ -0,0 +1,10 @@ +import { formatCoin } from "../formatters"; + +describe("formatCoin", () => { + it("returns the expected values", () => { + expect(formatCoin({ amount: "1", denom: "uatom" })).toBe("0.000001 ATOM"); + expect(formatCoin({ amount: "1000000", denom: "uatom" })).toBe("1.00 ATOM"); + expect(formatCoin({ amount: "0", denom: "uatom" })).toBe("0.00 ATOM"); + expect(formatCoin({ amount: "10", denom: "atom" })).toBe("10.00 ATOM"); + }); +}); diff --git a/src/screens/staking/lib/staking_sdk/context.tsx b/src/screens/staking/lib/staking_sdk/context.tsx index 46d894f7..565c2f14 100644 --- a/src/screens/staking/lib/staking_sdk/context.tsx +++ b/src/screens/staking/lib/staking_sdk/context.tsx @@ -6,7 +6,6 @@ import { createContext, useContext, useMemo, useRef, useState } from "react"; import { ENABLE_TESTNETS, WalletId, - defaultState, mainNetworkDenom, networksWithStaking, testnetNetworks, @@ -16,10 +15,8 @@ import type { Account, CoinDenom, NetworkInfo, - SetState, StakingNetworkId, State, - TStakingContext, Wallet, } from "./core"; import { geckoClient } from "./gecko_client"; @@ -27,7 +24,27 @@ import { stakingClient } from "./staking_client"; import { filterUniqueAddresses, sortAccounts } from "./utils/accounts"; import { getEmptyCoin, sumCoins } from "./utils/coins"; import { setConnectedWallet } from "./utils/storage"; -import { disconnecKeplr, useWalletsListeners } from "./wallet_operations"; +import { + disconnecKeplr, + doesWalletSupportNetwork, + useWalletsListeners, +} from "./wallet_operations"; + +type SetState = (state: ((s: State) => State) | Partial) => void; + +export type TStakingContext = { + setState: SetState; + state: State; +}; + +const defaultState: State = { + coinsPrices: {}, + hasInit: false, + networksInfo: {}, + selectedAccount: null, + selectedAction: null, + wallets: {}, +}; const baseContext: TStakingContext = { setState: () => {}, @@ -105,56 +122,101 @@ export const fetchNetworksInfo = async (setState: SetState) => { })); }; +const networkInfoRequests: { + [key in StakingNetworkId]?: Promise; +} = {}; + export const getNetworkStakingInfo = async ( - setState: SetState, - state: State, + context: TStakingContext, networkId: StakingNetworkId, ) => { - if (state.networksInfo[networkId]) { + const { setState, state } = context; + + if (state.networksInfo[networkId]) return state.networksInfo[networkId] as NetworkInfo; - } - const newInfo = await stakingClient.getStakingInfo(networkId); + const request = networkInfoRequests[networkId]; - setState((prevState) => ({ - ...prevState, - networksInfo: { - ...prevState.networksInfo, - [networkId]: newInfo, - }, - })); + if (request) return request; - return newInfo as NetworkInfo; + const newRequest = stakingClient.getStakingInfo(networkId).then((newInfo) => { + setState((prevState) => ({ + ...prevState, + networksInfo: { + ...prevState.networksInfo, + [networkId]: newInfo, + }, + })); + + networkInfoRequests[networkId] = undefined; + + return newInfo as NetworkInfo; + }); + + networkInfoRequests[networkId] = newRequest; + + return newRequest; }; -export const getCoinPrice = async ( - state: State, - setState: SetState, +const coinPriceRequests: { [key in CoinDenom]?: Promise } = {}; + +const getCoinPrice = async ( + context: TStakingContext, denom: CoinDenom, +): Promise => { + const { setState, state } = context; + + const coinPrice = state.coinsPrices[denom]; + + if (coinPrice) return coinPrice; + + const coinRequest = coinPriceRequests[denom]; + + if (coinRequest) return coinRequest; + + const newRequest = geckoClient.getCoinPrice(denom).then((price): string => { + setState((prevState) => ({ + ...prevState, + coinsPrices: { + ...prevState.coinsPrices, + [denom]: price, + }, + })); + + coinPriceRequests[denom] = undefined; + + return price; + }); + + coinPriceRequests[denom] = newRequest; + + return newRequest; +}; + +export const getCoinPriceForNetwork = async ( + context: TStakingContext, + networkId: StakingNetworkId | undefined, ) => { - if (state.coinsPrices[denom]) { - return state.coinsPrices[denom]; - } + if (!networkId) return; - const price = await geckoClient.getCoinPrice(denom); + const parsedDenom = mainNetworkDenom[networkId]; - setState((prevState) => ({ - ...prevState, - coinsPrices: { - ...prevState.coinsPrices, - [denom]: price, - }, - })); + const { coinsPrices } = context.state; - return price; + if (parsedDenom && !coinsPrices[parsedDenom]) { + (async () => { + await getCoinPrice(context, parsedDenom); + })(); + } }; export const setUserWallet = ( - state: State, - setState: SetState, + context: TStakingContext, walletName: WalletId, wallet: Wallet, ) => { + const { setState, state } = context; + setState({ wallets: { ...state.wallets, @@ -181,10 +243,10 @@ export const setSelectedAccount = ( }; export const syncAccountData = async ( - setState: SetState, - state: State, + context: TStakingContext, account: Account, ) => { + const { setState, state } = context; const { address, networkId, wallet: walletId } = account; const [info, rewards] = await Promise.all([ @@ -223,10 +285,11 @@ export const syncAccountData = async ( }; export const disconnectWallet = async ( - setState: SetState, - state: State, + context: TStakingContext, walletId: WalletId, ) => { + const { setState, state } = context; + if (state.wallets[walletId]) { const networks = Object.keys( state.wallets[WalletId.Keplr]?.networks || {}, @@ -255,7 +318,7 @@ export const disconnectWallet = async ( } }; -// Selectors +// Selectors (don't set new state) export const getSelectedAccount = (state: State) => { const { selectedAccount } = state; @@ -320,15 +383,17 @@ export const getStakedDataForNetwork = ( return null; } + const mainDenom = mainNetworkDenom[network]; + return accountsForNetwork .filter(filterUniqueAddresses()) .reduce( (acc, account) => sumCoins(acc, account.info?.delegation), - getEmptyCoin(), + getEmptyCoin(mainDenom || undefined), ); }; -export type NetworkClaimableRewards = { coin: Coin; usd: BigNumber } | null; +export type NetworkClaimableRewards = Coin | null; // This assumes that the rewards coins have been normalized (which happens in // the staking client) @@ -342,7 +407,6 @@ export const getClaimableRewardsForNetwork = ( return null; } - const { coinsPrices } = state; const denom = mainNetworkDenom[network]; if (!denom) { @@ -353,24 +417,13 @@ export const getClaimableRewardsForNetwork = ( (acc, account) => (Array.isArray(account.rewards) ? account.rewards : []).reduce( (acc2, reward) => { - const rewardDenomPrice = - coinsPrices[reward.denom.toLowerCase() as CoinDenom]; - - if (rewardDenomPrice) { - const price = new BigNumber(rewardDenomPrice); - const amount = new BigNumber(reward.amount); - const extraUSD = price.times(amount); - - acc2.usd = acc2.usd.plus(extraUSD); - } - if (denom?.toLowerCase() === reward.denom?.toLowerCase()) { - const existingAmount = new BigNumber(acc2.coin.amount); + const existingAmount = new BigNumber(acc2.amount); const amount = new BigNumber(reward.amount); - acc2.coin = { + return { amount: existingAmount.plus(amount).toString(), - denom: acc2.coin.denom, + denom: acc2.denom, }; } @@ -378,9 +431,39 @@ export const getClaimableRewardsForNetwork = ( }, acc, ), - { coin: getEmptyCoin(denom.toUpperCase()), usd: new BigNumber(0) }, + getEmptyCoin(denom.toUpperCase()), ); }; export const getHasConnectedWallets = (state: State) => Object.keys(state.wallets).length > 0; + +export const getNetworkVotingPower = ( + state: State, + network: StakingNetworkId, +): Coin | null => { + const networkInfo = state.networksInfo[network]; + + if (!networkInfo) return null; + + const mainDenom = mainNetworkDenom[network]; + + if (!mainDenom) return null; + + const { voting_power: votingPower } = networkInfo; + + if (!votingPower) return null; + + return { + amount: votingPower.toString(), + denom: mainDenom, + }; +}; + +export const getHasNetworkSupportedWallet = ( + state: State, + network: StakingNetworkId, +) => + Object.keys(state.wallets).some((walletId) => + doesWalletSupportNetwork(walletId as WalletId, network), + ); diff --git a/src/screens/staking/lib/staking_sdk/core.ts b/src/screens/staking/lib/staking_sdk/core.ts index e5a9c743..bbbe829a 100644 --- a/src/screens/staking/lib/staking_sdk/core.ts +++ b/src/screens/staking/lib/staking_sdk/core.ts @@ -107,6 +107,7 @@ export type NetworkInfo = { apy: number; rpc: string; unbonding_period: number; + voting_power: number; }; export type State = { @@ -117,19 +118,3 @@ export type State = { selectedAction: null | StakeAction; wallets: { [key in WalletId]?: Wallet }; }; - -export type SetState = (state: ((s: State) => State) | Partial) => void; - -export type TStakingContext = { - setState: SetState; - state: State; -}; - -export const defaultState: State = { - coinsPrices: {}, - hasInit: false, - networksInfo: {}, - selectedAccount: null, - selectedAction: null, - wallets: {}, -}; diff --git a/src/screens/staking/lib/staking_sdk/formatters.ts b/src/screens/staking/lib/staking_sdk/formatters.ts index bc602507..ca80c64e 100644 --- a/src/screens/staking/lib/staking_sdk/formatters.ts +++ b/src/screens/staking/lib/staking_sdk/formatters.ts @@ -1,83 +1,39 @@ import type { Coin } from "@cosmjs/stargate"; +import BigNumber from "bignumber.js"; -import type { Network, NetworkKey } from "@src/utils/network_info"; - -import type { NetworkInfo } from "./core"; -import { networkKeyToNetworkId } from "./core"; import { normaliseCoin } from "./utils/coins"; -import { getCanStakeToAnyWallet } from "./wallet_operations"; - -export const normaliseDenom = (denom: string): string => - normaliseCoin({ amount: "0", denom }).denom; -export const formatCoin = (coin: Coin): string => { +export const formatCoin = (coin: Coin, decimals?: number): string => { if (!coin?.denom) { return ""; } const normalisedCoin = normaliseCoin(coin); - const num = Number(normalisedCoin?.amount); + const num = new BigNumber(normalisedCoin?.amount); - if (Number.isNaN(num)) { + if (num.isNaN()) { return `- ${coin.denom.toUpperCase()}`; } - const formatNum = (n: number): string => - n.toLocaleString("en-US", { - maximumFractionDigits: 6, - minimumFractionDigits: 2, + const formatNum = (n: BigNumber): string => + n.toNumber().toLocaleString("en-US", { + maximumFractionDigits: decimals ?? 6, + minimumFractionDigits: decimals ?? 2, }); return `${formatNum(num)} ${normalisedCoin.denom}`; }; -export const sortNetworks = () => { - const canStake = getCanStakeToAnyWallet(); - - return (a: Network, b: Network) => { - if (canStake) { - const networkIdA = networkKeyToNetworkId[a.key as NetworkKey]; - const networkIdB = networkKeyToNetworkId[b.key as NetworkKey]; +export const formatStakedDataUSD = (stakedData: Coin, coinPrice: string) => { + const stakedNormalised = normaliseCoin(stakedData); + const coinNum = new BigNumber(stakedNormalised.amount); + const coinValue = coinNum.multipliedBy(new BigNumber(coinPrice)); - if (networkIdA && !networkIdB) { - return -1; - } - - if (!networkIdA && networkIdB) { - return 1; - } - } - - return a.name.localeCompare(b.name); - }; -}; + const minNum = new BigNumber(10).pow(-5); -export const getUnbondingTimeForNetwork = ( - networkInfo: NetworkInfo | null, - locale?: string, -) => { - if (!networkInfo) { - return null; + if (coinValue.isNaN() || coinValue.lt(minNum)) { + return `< ${minNum.toFormat(5)} USD`; } - const { unbonding_period: unbondingPeriod } = networkInfo; - - if (!unbondingPeriod) { - return null; - } - - const now = new Date(); - const days = Math.ceil(unbondingPeriod / 86400); - const nextDate = new Date(now.getTime() + unbondingPeriod * 1000); - - const nextDateStr = nextDate.toLocaleDateString(locale, { - day: "numeric", - month: "long", - year: "numeric", - }); - - return { - date: nextDateStr, - days, - }; + return `≈ ${coinValue.toFormat(5)} USD`; }; diff --git a/src/screens/staking/lib/staking_sdk/staking_client.ts b/src/screens/staking/lib/staking_sdk/staking_client.ts index 4c933b1e..b2835740 100644 --- a/src/screens/staking/lib/staking_sdk/staking_client.ts +++ b/src/screens/staking/lib/staking_sdk/staking_client.ts @@ -75,6 +75,7 @@ type GetStakingInfoResponse = { apy: number; rpc: string; unbonding_period: number; + voting_power: number; }; export const stakingClient = { diff --git a/src/screens/staking/lib/staking_sdk/utils/accounts.ts b/src/screens/staking/lib/staking_sdk/utils/accounts.ts index 80e75242..5c3415be 100644 --- a/src/screens/staking/lib/staking_sdk/utils/accounts.ts +++ b/src/screens/staking/lib/staking_sdk/utils/accounts.ts @@ -1,4 +1,5 @@ import type { Coin } from "@cosmjs/stargate"; +import BigNumber from "bignumber.js"; import type { Account } from "../core"; import { normaliseCoin } from "./coins"; @@ -26,7 +27,7 @@ export const filterUniqueAddresses = () => { }; }; -type NormalisedInfo = { coin: Coin; num: null | number } | null; +type NormalisedInfo = { coin: Coin; num: BigNumber } | null; export const getAccountNormalisedDelegation = ( account?: Account, @@ -34,11 +35,10 @@ export const getAccountNormalisedDelegation = ( if (!account?.info?.delegation) return null; const available = normaliseCoin(account.info.delegation); - const availableNum = Number(available.amount); return { coin: available, - num: Number.isNaN(availableNum) ? null : availableNum, + num: new BigNumber(available.amount), }; }; @@ -48,11 +48,10 @@ export const getAccountNormalisedBalance = ( if (!account?.info?.balances) return null; const balance = normaliseCoin(account.info.balances); - const balanceNum = Number(balance.amount); return { coin: balance, - num: Number.isNaN(balanceNum) ? null : balanceNum, + num: new BigNumber(balance.amount), }; }; diff --git a/src/screens/staking/lib/staking_sdk/utils/coins.ts b/src/screens/staking/lib/staking_sdk/utils/coins.ts index e84ce072..f62e0cae 100644 --- a/src/screens/staking/lib/staking_sdk/utils/coins.ts +++ b/src/screens/staking/lib/staking_sdk/utils/coins.ts @@ -46,10 +46,31 @@ export const normaliseCoin = (coin: Coin): Coin => { }; }; -// @TODO: Improve multiple denoms handling, also maybe use bigint here -export const sumCoins = (coinA?: Coin, coinB?: Coin): Coin => ({ - amount: (Number(coinA?.amount || 0) + Number(coinB?.amount || 0)).toString(), - denom: coinA?.denom || coinB?.denom || "", -}); +export const normaliseDenom = (denom: string): string => + normaliseCoin({ amount: "0", denom }).denom; export const getEmptyCoin = (denom = ""): Coin => ({ amount: "0", denom }); + +export const sumCoins = (coinA?: Coin, coinB?: Coin): Coin => + [coinA, coinB] + .filter(Boolean) + .map((c) => normaliseCoin(c as Coin)) + .reduce((acc, coin) => { + const { amount } = acc; + const { amount: amountB } = coin; + + if (acc.denom && coin.denom && acc.denom !== coin.denom) { + console.error("Unexpected sum of different denoms", acc, coin); + + // Don't throw here for now + return acc; + } + + const newAmount = new BigNumber(amount).plus(amountB).toString(); + const denom = [acc.denom, coin.denom].find(Boolean) || ""; + + return { + amount: newAmount, + denom, + }; + }, getEmptyCoin()); diff --git a/src/screens/staking/lib/staking_sdk/utils/networks.ts b/src/screens/staking/lib/staking_sdk/utils/networks.ts new file mode 100644 index 00000000..667567b7 --- /dev/null +++ b/src/screens/staking/lib/staking_sdk/utils/networks.ts @@ -0,0 +1,49 @@ +import type { Network, NetworkKey } from "@src/utils/network_info"; + +import type { NetworkInfo } from "../core"; +import { networkKeyToNetworkId } from "../core"; + +export const sortNetworks = () => (a: Network, b: Network) => { + const networkIdA = networkKeyToNetworkId[a.key as NetworkKey]; + const networkIdB = networkKeyToNetworkId[b.key as NetworkKey]; + + if (networkIdA && !networkIdB) { + return -1; + } + + if (!networkIdA && networkIdB) { + return 1; + } + + return a.name.localeCompare(b.name); +}; + +export const getUnbondingTimeForNetwork = ( + networkInfo: NetworkInfo | null, + locale?: string, +) => { + if (!networkInfo) { + return null; + } + + const { unbonding_period: unbondingPeriod } = networkInfo; + + if (!unbondingPeriod) { + return null; + } + + const now = new Date(); + const days = Math.ceil(unbondingPeriod / 86400); + const nextDate = new Date(now.getTime() + unbondingPeriod * 1000); + + const nextDateStr = nextDate.toLocaleDateString(locale, { + day: "numeric", + month: "long", + year: "numeric", + }); + + return { + date: nextDateStr, + days, + }; +}; diff --git a/src/screens/staking/lib/staking_sdk/wallet_operations.ts b/src/screens/staking/lib/staking_sdk/wallet_operations.ts index 2ab7d3ad..6ebeb5b1 100644 --- a/src/screens/staking/lib/staking_sdk/wallet_operations.ts +++ b/src/screens/staking/lib/staking_sdk/wallet_operations.ts @@ -18,15 +18,9 @@ import { useEffect } from "react"; import { toastSuccess } from "@src/components/notification"; import { IS_E2E } from "@src/utils/e2e"; +import type { TStakingContext } from "./context"; import { setUserWallet } from "./context"; -import type { - Account, - SetState, - StakingNetworkId, - State, - TStakingContext, - Wallet, -} from "./core"; +import type { Account, StakingNetworkId, Wallet } from "./core"; import { WalletId, keplrNetworks, networksWithStaking } from "./core"; import { stakingClient } from "./staking_client"; import { addToConnectedWallets, getConnectedWallets } from "./utils/storage"; @@ -223,101 +217,130 @@ export const unstake = async ( .catch(handleKeplrSignError); }); -export const tryToConnectWallets = async ( - stakingState: State, - setStakingState: SetState, - walletsIds: WalletId[], +const tryToConnectKeplr = async ( + context: TStakingContext, + openLinkIfMissing: boolean, ) => { - if (walletsIds.includes(WalletId.Keplr)) { - if (window.keplr) { - const chainsToConnect = Array.from(keplrNetworks); - - await window.keplr.enable(chainsToConnect); - - try { - const handleError = (err: unknown) => { - // eslint-disable-next-line no-console - console.log("debug: index.tsx: err", err); - - return [] as Account[]; - }; - - let walletName = ""; - - const parseAccounts = - (networkId: StakingNetworkId) => - (accounts: readonly AccountData[]): Promise => - Promise.all( - accounts.map((account) => - Promise.all([ - stakingClient.getAddressInfo(networkId, account.address), - stakingClient.getRewardsInfo(networkId, account.address), - window.keplr!.getKey(networkId), - ]).then(([info, rewards, key]) => { - if (key?.name) { - walletName = key.name; - } - - return { - address: account.address, - info, - networkId, - rewards, - wallet: WalletId.Keplr, - }; - }), - ), - ); - - const keplrAccounts = await Promise.all( - Array.from(keplrNetworks).map(async (network) => { - if (networksWithStaking.has(network)) { - const accounts = await window - .keplr!.getOfflineSigner(network) - .getAccounts() - .then(parseAccounts(network)) - .catch(handleError); - - return { - accounts, - networkId: network, + if (window.keplr) { + const chainsToConnect = Array.from(keplrNetworks); + + await window.keplr.enable(chainsToConnect); + + try { + const handleError = (err: unknown) => { + // eslint-disable-next-line no-console + console.log("debug: index.tsx: err", err); + + return [] as Account[]; + }; + + let walletName = ""; + + const parseAccounts = + (networkId: StakingNetworkId) => + (accounts: readonly AccountData[]): Promise => + Promise.all( + accounts.map((account) => + Promise.all([ + stakingClient.getAddressInfo(networkId, account.address), + stakingClient.getRewardsInfo(networkId, account.address), + window.keplr!.getKey(networkId), + ]).then(([info, rewards, key]) => { + if (key?.name) { + walletName = key.name; + } + + return { + address: account.address, + info, + networkId, + rewards, + wallet: WalletId.Keplr, + }; + }), + ), + ); + + const keplrAccounts = await Promise.all( + Array.from(keplrNetworks).map(async (network) => { + if (networksWithStaking.has(network)) { + const accounts = await window + .keplr!.getOfflineSigner(network) + .getAccounts() + .then(parseAccounts(network)) + .catch(handleError); + + return { + accounts, + networkId: network, + }; + } + }), + ); + + addToConnectedWallets(WalletId.Keplr); + + setUserWallet( + context, + WalletId.Keplr, + keplrAccounts.reduce( + (acc, networkObj) => { + if (networkObj) { + acc.networks[networkObj.networkId] = { + accounts: networkObj.accounts, + networkId: networkObj.networkId, }; } - }), - ); - - addToConnectedWallets(WalletId.Keplr); - - setUserWallet( - stakingState, - setStakingState, - WalletId.Keplr, - keplrAccounts.reduce( - (acc, networkObj) => { - if (networkObj) { - acc.networks[networkObj.networkId] = { - accounts: networkObj.accounts, - networkId: networkObj.networkId, - }; - } - - return acc; - }, - { - name: walletName, - networks: {}, - wallet: WalletId.Keplr, - } as Wallet, - ), - ); - - return true; - } catch (error) { - // eslint-disable-next-line no-console - console.log("tryToConnectWallets", error); + + return acc; + }, + { + name: walletName, + networks: {}, + wallet: WalletId.Keplr, + } as Wallet, + ), + ); + + return true; + } catch (error) { + // eslint-disable-next-line no-console + console.log("tryToConnectWallets", error); + } + } else if (openLinkIfMissing) { + if (/Chrome/.test(navigator.userAgent)) { + window.open( + "https://chromewebstore.google.com/detail/keplr/dmkamcknogkgcdfhhbddcghachkejeap", + "_blank", + ); + + return; + } + + window.open("https://wallet.keplr.app/#/dashboard", "_blank"); + } +}; + +export const tryToConnectWallets = async ( + context: TStakingContext, + walletsIds: WalletId[], + openLinkIfMissing = false, +) => { + let connected = true as boolean | undefined; + + for (const walletId of walletsIds) { + switch (walletId) { + case WalletId.Keplr: + connected = await tryToConnectKeplr(context, openLinkIfMissing); + break; + + default: { + walletId satisfies never; } } } + + return connected; }; export const disconnecKeplr = async (networks: StakingNetworkId[]) => { @@ -339,9 +362,7 @@ export const useWalletsListeners = (contextValue: TStakingContext) => { title: t("keplrWalletUpdate"), }); - tryToConnectWallets(contextValue.state, contextValue.setState, [ - WalletId.Keplr, - ]); + tryToConnectWallets(contextValue, [WalletId.Keplr]); } }; @@ -357,5 +378,18 @@ export const useWalletsListeners = (contextValue: TStakingContext) => { }, [contextValue, t]); }; -export const getCanStakeToAnyWallet = () => - typeof window !== "undefined" && (!!window.keplr || IS_E2E); +export const doesWalletSupportNetwork = ( + wallet: WalletId, + networkId: string, +) => { + switch (wallet) { + case WalletId.Keplr: + return keplrNetworks.has(networkId as StakingNetworkId); + + default: { + wallet satisfies never; + + return false; + } + } +}; diff --git a/src/tests/styleMock.ts b/src/tests/styleMock.ts new file mode 100644 index 00000000..b30bd489 --- /dev/null +++ b/src/tests/styleMock.ts @@ -0,0 +1 @@ +export const foo = ""; diff --git a/src/tests/tsconfig.test.json b/src/tests/tsconfig.test.json new file mode 100644 index 00000000..7319adb8 --- /dev/null +++ b/src/tests/tsconfig.test.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "jsx": "react" + } +} diff --git a/yarn.lock b/yarn.lock index 96002a45..defd3825 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,7 +45,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" dependencies: @@ -62,6 +62,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": + version: 7.23.7 + resolution: "@babel/core@npm:7.23.7" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helpers": "npm:^7.23.7" + "@babel/parser": "npm:^7.23.6" + "@babel/template": "npm:^7.22.15" + "@babel/traverse": "npm:^7.23.7" + "@babel/types": "npm:^7.23.6" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 38c9934973d384ed83369712978453eac91dc3f22167404dbdb272b64f602e74728a6f37012c53ee57e521b8ae2da60097f050497d9b6a212d28b59cdfb2cd1d + languageName: node + linkType: hard + "@babel/core@npm:^7.21.0, @babel/core@npm:^7.21.3": version: 7.23.6 resolution: "@babel/core@npm:7.23.6" @@ -96,7 +119,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.22.7, @babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6": +"@babel/generator@npm:^7.22.7, @babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": version: 7.23.6 resolution: "@babel/generator@npm:7.23.6" dependencies: @@ -357,6 +380,17 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.23.7": + version: 7.23.8 + resolution: "@babel/helpers@npm:7.23.8" + dependencies: + "@babel/template": "npm:^7.22.15" + "@babel/traverse": "npm:^7.23.7" + "@babel/types": "npm:^7.23.6" + checksum: d9fce49278a31aaa017a40c1fcdaa450999c49e33582cce8138058c58b1acbe3a2d2488f010f28e91dedf0d35795ea32f0ee18745bbb6c7f54052ae0fd7e6a3f + languageName: node + linkType: hard + "@babel/highlight@npm:^7.23.4": version: 7.23.4 resolution: "@babel/highlight@npm:7.23.4" @@ -368,7 +402,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.20.5, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.6": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.6": version: 7.23.6 resolution: "@babel/parser@npm:7.23.6" bin: @@ -433,7 +467,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-class-properties@npm:^7.12.13": +"@babel/plugin-syntax-bigint@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-bigint@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 686891b81af2bc74c39013655da368a480f17dd237bf9fbc32048e5865cb706d5a8f65438030da535b332b1d6b22feba336da8fa931f663b6b34e13147d12dde + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-properties@npm:^7.12.13, @babel/plugin-syntax-class-properties@npm:^7.8.3": version: 7.12.13 resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" dependencies: @@ -499,7 +544,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-meta@npm:^7.10.4": +"@babel/plugin-syntax-import-meta@npm:^7.10.4, @babel/plugin-syntax-import-meta@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" dependencies: @@ -521,7 +566,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.21.4, @babel/plugin-syntax-jsx@npm:^7.23.3": +"@babel/plugin-syntax-jsx@npm:^7.21.4, @babel/plugin-syntax-jsx@npm:^7.23.3, @babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.23.3 resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" dependencies: @@ -532,7 +577,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4, @babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" dependencies: @@ -554,7 +599,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": +"@babel/plugin-syntax-numeric-separator@npm:^7.10.4, @babel/plugin-syntax-numeric-separator@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" dependencies: @@ -609,7 +654,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-top-level-await@npm:^7.14.5": +"@babel/plugin-syntax-top-level-await@npm:^7.14.5, @babel/plugin-syntax-top-level-await@npm:^7.8.3": version: 7.14.5 resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" dependencies: @@ -620,7 +665,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.21.4, @babel/plugin-syntax-typescript@npm:^7.23.3": +"@babel/plugin-syntax-typescript@npm:^7.21.4, @babel/plugin-syntax-typescript@npm:^7.23.3, @babel/plugin-syntax-typescript@npm:^7.7.2": version: 7.23.3 resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" dependencies: @@ -1457,7 +1502,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.22.15": +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": version: 7.22.15 resolution: "@babel/template@npm:7.22.15" dependencies: @@ -1504,6 +1549,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.23.7": + version: 7.23.7 + resolution: "@babel/traverse@npm:7.23.7" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/parser": "npm:^7.23.6" + "@babel/types": "npm:^7.23.6" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: e32fceb4249beec2bde83968ddffe17444221c1ee5cd18c543a2feaf94e3ca83f2a4dfbc2dcca87cf226e0105973e0fe3717063a21e982a9de9945615ab3f3f5 + languageName: node + linkType: hard + "@babel/types@npm:7.17.0": version: 7.17.0 resolution: "@babel/types@npm:7.17.0" @@ -1514,7 +1577,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.17.0, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.23.6 resolution: "@babel/types@npm:7.23.6" dependencies: @@ -1525,6 +1588,13 @@ __metadata: languageName: node linkType: hard +"@bcoe/v8-coverage@npm:^0.2.3": + version: 0.2.3 + resolution: "@bcoe/v8-coverage@npm:0.2.3" + checksum: 6b80ae4cb3db53f486da2dc63b6e190a74c8c3cca16bb2733f234a0b6a9382b09b146488ae08e2b22cf00f6c83e20f3e040a2f7894f05c045c946d6a090b1d52 + languageName: node + linkType: hard + "@commitlint/cli@npm:^18.4.4": version: 18.4.4 resolution: "@commitlint/cli@npm:18.4.4" @@ -2738,6 +2808,256 @@ __metadata: languageName: node linkType: hard +"@istanbuljs/load-nyc-config@npm:^1.0.0": + version: 1.1.0 + resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" + dependencies: + camelcase: "npm:^5.3.1" + find-up: "npm:^4.1.0" + get-package-type: "npm:^0.1.0" + js-yaml: "npm:^3.13.1" + resolve-from: "npm:^5.0.0" + checksum: dd2a8b094887da5a1a2339543a4933d06db2e63cbbc2e288eb6431bd832065df0c099d091b6a67436e71b7d6bf85f01ce7c15f9253b4cbebcc3b9a496165ba42 + languageName: node + linkType: hard + +"@istanbuljs/schema@npm:^0.1.2": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: 61c5286771676c9ca3eb2bd8a7310a9c063fb6e0e9712225c8471c582d157392c88f5353581c8c9adbe0dff98892317d2fdfc56c3499aa42e0194405206a963a + languageName: node + linkType: hard + +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + slash: "npm:^3.0.0" + checksum: 7be408781d0a6f657e969cbec13b540c329671819c2f57acfad0dae9dbfe2c9be859f38fe99b35dba9ff1536937dc6ddc69fdcd2794812fa3c647a1619797f6c + languageName: node + linkType: hard + +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/reporters": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-changed-files: "npm:^29.7.0" + jest-config: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-resolve-dependencies: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-ansi: "npm:^6.0.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 934f7bf73190f029ac0f96662c85cd276ec460d407baf6b0dbaec2872e157db4d55a7ee0b1c43b18874602f662b37cb973dda469a4e6d88b4e4845b521adeeb2 + languageName: node + linkType: hard + +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" + dependencies: + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + checksum: c7b1b40c618f8baf4d00609022d2afa086d9c6acc706f303a70bb4b67275868f620ad2e1a9efc5edd418906157337cce50589a627a6400bbdf117d351b91ef86 + languageName: node + linkType: hard + +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" + dependencies: + jest-get-type: "npm:^29.6.3" + checksum: 60b79d23a5358dc50d9510d726443316253ecda3a7fb8072e1526b3e0d3b14f066ee112db95699b7a43ad3f0b61b750c72e28a5a1cac361d7a2bb34747fa938a + languageName: node + linkType: hard + +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" + dependencies: + expect: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + checksum: b41f193fb697d3ced134349250aed6ccea075e48c4f803159db102b826a4e473397c68c31118259868fd69a5cba70e97e1c26d2c2ff716ca39dc73a2ccec037e + languageName: node + linkType: hard + +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@sinonjs/fake-timers": "npm:^10.0.2" + "@types/node": "npm:*" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: cf0a8bcda801b28dc2e2b2ba36302200ee8104a45ad7a21e6c234148932f826cb3bc57c8df3b7b815aeea0861d7b6ca6f0d4778f93b9219398ef28749e03595c + languageName: node + linkType: hard + +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + jest-mock: "npm:^29.7.0" + checksum: a385c99396878fe6e4460c43bd7bb0a5cc52befb462cc6e7f2a3810f9e7bcce7cdeb51908fd530391ee452dc856c98baa2c5f5fa8a5b30b071d31ef7f6955cea + languageName: node + linkType: hard + +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" + dependencies: + "@bcoe/v8-coverage": "npm:^0.2.3" + "@jest/console": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + collect-v8-coverage: "npm:^1.0.0" + exit: "npm:^0.1.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + istanbul-lib-coverage: "npm:^3.0.0" + istanbul-lib-instrument: "npm:^6.0.0" + istanbul-lib-report: "npm:^3.0.0" + istanbul-lib-source-maps: "npm:^4.0.0" + istanbul-reports: "npm:^3.1.3" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + slash: "npm:^3.0.0" + string-length: "npm:^4.0.1" + strip-ansi: "npm:^6.0.0" + v8-to-istanbul: "npm:^9.0.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: a754402a799541c6e5aff2c8160562525e2a47e7d568f01ebfc4da66522de39cbb809bbb0a841c7052e4270d79214e70aec3c169e4eae42a03bc1a8a20cb9fa2 + languageName: node + linkType: hard + +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": "npm:^0.27.8" + checksum: b329e89cd5f20b9278ae1233df74016ebf7b385e0d14b9f4c1ad18d096c4c19d1e687aa113a9c976b16ec07f021ae53dea811fb8c1248a50ac34fbe009fdf6be + languageName: node + linkType: hard + +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.18" + callsites: "npm:^3.0.0" + graceful-fs: "npm:^4.2.9" + checksum: a2f177081830a2e8ad3f2e29e20b63bd40bade294880b595acf2fc09ec74b6a9dd98f126a2baa2bf4941acd89b13a4ade5351b3885c224107083a0059b60a219 + languageName: node + linkType: hard + +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + collect-v8-coverage: "npm:^1.0.0" + checksum: 7de54090e54a674ca173470b55dc1afdee994f2d70d185c80236003efd3fa2b753fff51ffcdda8e2890244c411fd2267529d42c4a50a8303755041ee493e6a04 + languageName: node + linkType: hard + +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" + dependencies: + "@jest/test-result": "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + slash: "npm:^3.0.0" + checksum: 593a8c4272797bb5628984486080cbf57aed09c7cfdc0a634e8c06c38c6bef329c46c0016e84555ee55d1cd1f381518cf1890990ff845524c1123720c8c1481b + languageName: node + linkType: hard + +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + babel-plugin-istanbul: "npm:^6.1.1" + chalk: "npm:^4.0.0" + convert-source-map: "npm:^2.0.0" + fast-json-stable-stringify: "npm:^2.1.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pirates: "npm:^4.0.4" + slash: "npm:^3.0.0" + write-file-atomic: "npm:^4.0.2" + checksum: 7f4a7f73dcf45dfdf280c7aa283cbac7b6e5a904813c3a93ead7e55873761fc20d5c4f0191d2019004fac6f55f061c82eb3249c2901164ad80e362e7a7ede5a6 + languageName: node + linkType: hard + +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" + dependencies: + "@jest/schemas": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + "@types/istanbul-reports": "npm:^3.0.0" + "@types/node": "npm:*" + "@types/yargs": "npm:^17.0.8" + chalk: "npm:^4.0.0" + checksum: ea4e493dd3fb47933b8ccab201ae573dcc451f951dc44ed2a86123cd8541b82aa9d2b1031caf9b1080d6673c517e2dcc25a44b2dc4f3fbc37bfc965d444888c0 + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": version: 0.3.3 resolution: "@jridgewell/gen-mapping@npm:0.3.3" @@ -2770,6 +3090,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 18cf19f88e2792c1c91515f2b629aae05f3cdbb2e60c3886e16e80725234ce26dd10144c4981c05d9366e7094498c0b4fe5c1a89f4a730d7376a4ba4af448149 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.20 resolution: "@jridgewell/trace-mapping@npm:0.3.20" @@ -3265,6 +3595,31 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e + languageName: node + linkType: hard + +"@sinonjs/commons@npm:^3.0.0": + version: 3.0.0 + resolution: "@sinonjs/commons@npm:3.0.0" + dependencies: + type-detect: "npm:4.0.8" + checksum: 1df9cd257942f4e4960dfb9fd339d9e97b6a3da135f3d5b8646562918e863809cb8e00268535f4f4723535d2097881c8fc03d545c414d8555183376cfc54ee84 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.3.0 + resolution: "@sinonjs/fake-timers@npm:10.3.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.0" + checksum: 2e2fb6cc57f227912814085b7b01fede050cd4746ea8d49a1e44d5a0e56a804663b0340ae2f11af7559ea9bf4d087a11f2f646197a660ea3cb04e19efc04aa63 + languageName: node + linkType: hard + "@socialgouv/matomo-next@npm:^1.8.0": version: 1.8.0 resolution: "@socialgouv/matomo-next@npm:1.8.0" @@ -3549,6 +3904,47 @@ __metadata: languageName: node linkType: hard +"@types/babel__core@npm:^7.1.14": + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" + dependencies: + "@babel/parser": "npm:^7.20.7" + "@babel/types": "npm:^7.20.7" + "@types/babel__generator": "npm:*" + "@types/babel__template": "npm:*" + "@types/babel__traverse": "npm:*" + checksum: bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff + languageName: node + linkType: hard + +"@types/babel__generator@npm:*": + version: 7.6.8 + resolution: "@types/babel__generator@npm:7.6.8" + dependencies: + "@babel/types": "npm:^7.0.0" + checksum: f0ba105e7d2296bf367d6e055bb22996886c114261e2cb70bf9359556d0076c7a57239d019dee42bb063f565bade5ccb46009bce2044b2952d964bf9a454d6d2 + languageName: node + linkType: hard + +"@types/babel__template@npm:*": + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" + dependencies: + "@babel/parser": "npm:^7.1.0" + "@babel/types": "npm:^7.0.0" + checksum: cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b + languageName: node + linkType: hard + +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": + version: 7.20.5 + resolution: "@types/babel__traverse@npm:7.20.5" + dependencies: + "@babel/types": "npm:^7.20.7" + checksum: 033abcb2f4c084ad33e30c3efaad82161240f351e3c71b6154ed289946b33b363696c0fbd42502b68e4582a87413c418321f40eb1ea863e34fe525641345e05b + languageName: node + linkType: hard + "@types/dompurify@npm:^3.0.5": version: 3.0.5 resolution: "@types/dompurify@npm:3.0.5" @@ -3558,6 +3954,50 @@ __metadata: languageName: node linkType: hard +"@types/graceful-fs@npm:^4.1.3": + version: 4.1.9 + resolution: "@types/graceful-fs@npm:4.1.9" + dependencies: + "@types/node": "npm:*" + checksum: 235d2fc69741448e853333b7c3d1180a966dd2b8972c8cbcd6b2a0c6cd7f8d582ab2b8e58219dbc62cce8f1b40aa317ff78ea2201cdd8249da5025adebed6f0b + languageName: node + linkType: hard + +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7 + languageName: node + linkType: hard + +"@types/istanbul-lib-report@npm:*": + version: 3.0.3 + resolution: "@types/istanbul-lib-report@npm:3.0.3" + dependencies: + "@types/istanbul-lib-coverage": "npm:*" + checksum: 247e477bbc1a77248f3c6de5dadaae85ff86ac2d76c5fc6ab1776f54512a745ff2a5f791d22b942e3990ddbd40f3ef5289317c4fca5741bedfaa4f01df89051c + languageName: node + linkType: hard + +"@types/istanbul-reports@npm:^3.0.0": + version: 3.0.4 + resolution: "@types/istanbul-reports@npm:3.0.4" + dependencies: + "@types/istanbul-lib-report": "npm:*" + checksum: 1647fd402aced5b6edac87274af14ebd6b3a85447ef9ad11853a70fd92a98d35f81a5d3ea9fcb5dbb5834e800c6e35b64475e33fcae6bfa9acc70d61497c54ee + languageName: node + linkType: hard + +"@types/jest@npm:^29.5.11": + version: 29.5.11 + resolution: "@types/jest@npm:29.5.11" + dependencies: + expect: "npm:^29.0.0" + pretty-format: "npm:^29.0.0" + checksum: 524a3394845214581278bf4d75055927261fbeac7e1a89cd621bd0636da37d265fe0a85eac58b5778758faad1cbd7c7c361dfc190c78ebde03a91cce33463261 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.12": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -3711,6 +4151,13 @@ __metadata: languageName: node linkType: hard +"@types/stack-utils@npm:^2.0.0": + version: 2.0.3 + resolution: "@types/stack-utils@npm:2.0.3" + checksum: 1f4658385ae936330581bcb8aa3a066df03867d90281cdf89cc356d404bd6579be0f11902304e1f775d92df22c6dd761d4451c804b0a4fba973e06211e9bd77c + languageName: node + linkType: hard + "@types/symlink-or-copy@npm:^1.2.0": version: 1.2.2 resolution: "@types/symlink-or-copy@npm:1.2.2" @@ -3732,6 +4179,22 @@ __metadata: languageName: node linkType: hard +"@types/yargs-parser@npm:*": + version: 21.0.3 + resolution: "@types/yargs-parser@npm:21.0.3" + checksum: e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0 + languageName: node + linkType: hard + +"@types/yargs@npm:^17.0.8": + version: 17.0.32 + resolution: "@types/yargs@npm:17.0.32" + dependencies: + "@types/yargs-parser": "npm:*" + checksum: 2095e8aad8a4e66b86147415364266b8d607a3b95b4239623423efd7e29df93ba81bb862784a6e08664f645cc1981b25fd598f532019174cd3e5e1e689e1cccf + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^6.19.0": version: 6.19.0 resolution: "@typescript-eslint/eslint-plugin@npm:6.19.0" @@ -4094,6 +4557,15 @@ __metadata: languageName: node linkType: hard +"ansi-escapes@npm:^4.2.1": + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" + dependencies: + type-fest: "npm:^0.21.3" + checksum: da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 + languageName: node + linkType: hard + "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -4126,6 +4598,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: 9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df + languageName: node + linkType: hard + "ansi-styles@npm:^6.1.0": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" @@ -4133,7 +4612,7 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": +"anymatch@npm:^3.0.3, anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" dependencies: @@ -4143,6 +4622,15 @@ __metadata: languageName: node linkType: hard +"argparse@npm:^1.0.7": + version: 1.0.10 + resolution: "argparse@npm:1.0.10" + dependencies: + sprintf-js: "npm:~1.0.2" + checksum: b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -4361,6 +4849,48 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" + dependencies: + "@jest/transform": "npm:^29.7.0" + "@types/babel__core": "npm:^7.1.14" + babel-plugin-istanbul: "npm:^6.1.1" + babel-preset-jest: "npm:^29.6.3" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + slash: "npm:^3.0.0" + peerDependencies: + "@babel/core": ^7.8.0 + checksum: 2eda9c1391e51936ca573dd1aedfee07b14c59b33dbe16ef347873ddd777bcf6e2fc739681e9e9661ab54ef84a3109a03725be2ac32cd2124c07ea4401cbe8c1 + languageName: node + linkType: hard + +"babel-plugin-istanbul@npm:^6.1.1": + version: 6.1.1 + resolution: "babel-plugin-istanbul@npm:6.1.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.0.0" + "@istanbuljs/load-nyc-config": "npm:^1.0.0" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-instrument: "npm:^5.0.4" + test-exclude: "npm:^6.0.0" + checksum: 1075657feb705e00fd9463b329921856d3775d9867c5054b449317d39153f8fbcebd3e02ebf00432824e647faff3683a9ca0a941325ef1afe9b3c4dd51b24beb + languageName: node + linkType: hard + +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" + dependencies: + "@babel/template": "npm:^7.3.3" + "@babel/types": "npm:^7.3.3" + "@types/babel__core": "npm:^7.1.14" + "@types/babel__traverse": "npm:^7.0.6" + checksum: 7e6451caaf7dce33d010b8aafb970e62f1b0c0b57f4978c37b0d457bbcf0874d75a395a102daf0bae0bd14eafb9f6e9a165ee5e899c0a4f1f3bb2e07b304ed2e + languageName: node + linkType: hard + "babel-plugin-macros@npm:^3.1.0": version: 3.1.0 resolution: "babel-plugin-macros@npm:3.1.0" @@ -4408,18 +4938,52 @@ __metadata: languageName: node linkType: hard -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee - languageName: node - linkType: hard - -"balanced-match@npm:^2.0.0": - version: 2.0.0 - resolution: "balanced-match@npm:2.0.0" - checksum: 60a54e0b75a61674e16a7a336b805f06c72d6f8fc457639c24efc512ba2bf9cb5744b9f6f5225afcefb99da39714440c83c737208cc65c5d9ecd1f3093331ca3 - languageName: node +"babel-preset-current-node-syntax@npm:^1.0.0": + version: 1.0.1 + resolution: "babel-preset-current-node-syntax@npm:1.0.1" + dependencies: + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + "@babel/plugin-syntax-bigint": "npm:^7.8.3" + "@babel/plugin-syntax-class-properties": "npm:^7.8.3" + "@babel/plugin-syntax-import-meta": "npm:^7.8.3" + "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/plugin-syntax-numeric-separator": "npm:^7.8.3" + "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" + "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/plugin-syntax-top-level-await": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 5ba39a3a0e6c37d25e56a4fb843be632dac98d54706d8a0933f9bcb1a07987a96d55c2b5a6c11788a74063fb2534fe68c1f1dbb6c93626850c785e0938495627 + languageName: node + linkType: hard + +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" + dependencies: + babel-plugin-jest-hoist: "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: ec5fd0276b5630b05f0c14bb97cc3815c6b31600c683ebb51372e54dcb776cff790bdeeabd5b8d01ede375a040337ccbf6a3ccd68d3a34219125945e167ad943 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"balanced-match@npm:^2.0.0": + version: 2.0.0 + resolution: "balanced-match@npm:2.0.0" + checksum: 60a54e0b75a61674e16a7a336b805f06c72d6f8fc457639c24efc512ba2bf9cb5744b9f6f5225afcefb99da39714440c83c737208cc65c5d9ecd1f3093331ca3 + languageName: node linkType: hard "base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": @@ -4580,6 +5144,24 @@ __metadata: languageName: node linkType: hard +"bs-logger@npm:0.x": + version: 0.2.6 + resolution: "bs-logger@npm:0.2.6" + dependencies: + fast-json-stable-stringify: "npm:2.x" + checksum: 80e89aaaed4b68e3374ce936f2eb097456a0dddbf11f75238dbd53140b1e39259f0d248a5089ed456f1158984f22191c3658d54a713982f676709fbe1a6fa5a0 + languageName: node + linkType: hard + +"bser@npm:2.1.1": + version: 2.1.1 + resolution: "bser@npm:2.1.1" + dependencies: + node-int64: "npm:^0.4.0" + checksum: 24d8dfb7b6d457d73f32744e678a60cc553e4ec0e9e1a01cf614b44d85c3c87e188d3cc78ef0442ce5032ee6818de20a0162ba1074725c0d08908f62ea979227 + languageName: node + linkType: hard + "buffer-equal-constant-time@npm:1.0.1": version: 1.0.1 resolution: "buffer-equal-constant-time@npm:1.0.1" @@ -4587,6 +5169,13 @@ __metadata: languageName: node linkType: hard +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + "buffer-from@npm:~0.1.1": version: 0.1.2 resolution: "buffer-from@npm:0.1.2" @@ -4763,6 +5352,13 @@ __metadata: languageName: node linkType: hard +"char-regex@npm:^1.0.2": + version: 1.0.2 + resolution: "char-regex@npm:1.0.2" + checksum: 57a09a86371331e0be35d9083ba429e86c4f4648ecbe27455dbfb343037c16ee6fdc7f6b61f433a57cc5ded5561d71c56a150e018f40c2ffb7bc93a26dae341e + languageName: node + linkType: hard + "cheerio-select@npm:^2.1.0": version: 2.1.0 resolution: "cheerio-select@npm:2.1.0" @@ -4818,6 +5414,20 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^3.2.0": + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 6f0109e36e111684291d46123d491bc4e7b7a1934c3a20dea28cba89f1d4a03acd892f5f6a81ed3855c38647e285a150e3c9ba062e38943bef57fee6c1554c3a + languageName: node + linkType: hard + +"cjs-module-lexer@npm:^1.0.0": + version: 1.2.3 + resolution: "cjs-module-lexer@npm:1.2.3" + checksum: 0de9a9c3fad03a46804c0d38e7b712fb282584a9c7ef1ed44cae22fb71d9bb600309d66a9711ac36a596fd03422f5bb03e021e8f369c12a39fa1786ae531baab + languageName: node + linkType: hard + "classnames@npm:^2.3.0": version: 2.5.1 resolution: "classnames@npm:2.5.1" @@ -4871,6 +5481,20 @@ __metadata: languageName: node linkType: hard +"co@npm:^4.6.0": + version: 4.6.0 + resolution: "co@npm:4.6.0" + checksum: c0e85ea0ca8bf0a50cbdca82efc5af0301240ca88ebe3644a6ffb8ffe911f34d40f8fbcf8f1d52c5ddd66706abd4d3bfcd64259f1e8e2371d4f47573b0dc8c28 + languageName: node + linkType: hard + +"collect-v8-coverage@npm:^1.0.0": + version: 1.0.2 + resolution: "collect-v8-coverage@npm:1.0.2" + checksum: ed7008e2e8b6852c5483b444a3ae6e976e088d4335a85aa0a9db2861c5f1d31bd2d7ff97a60469b3388deeba661a619753afbe201279fb159b4b9548ab8269a1 + languageName: node + linkType: hard + "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -5117,6 +5741,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + prompts: "npm:^2.0.1" + bin: + create-jest: bin/create-jest.js + checksum: e7e54c280692470d3398f62a6238fd396327e01c6a0757002833f06d00afc62dd7bfe04ff2b9cd145264460e6b4d1eb8386f2925b7e567f97939843b7b0e812f + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -5301,6 +5942,18 @@ __metadata: languageName: node linkType: hard +"dedent@npm:^1.0.0": + version: 1.5.1 + resolution: "dedent@npm:1.5.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: f8612cd5b00aab58b18bb95572dca08dc2d49720bfa7201a444c3dae430291e8a06d4928614a6ec8764d713927f44bce9c990d3b8238fca2f430990ddc17c070 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -5308,7 +5961,7 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.3.1": +"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" checksum: e53481aaf1aa2c4082b5342be6b6d8ad9dfe387bc92ce197a66dea08bd4265904a087e75e464f14d1347cf2ac8afe1e4c16b266e0561cc5df29382d3c5f80044 @@ -5358,6 +6011,20 @@ __metadata: languageName: node linkType: hard +"detect-newline@npm:^3.0.0": + version: 3.1.0 + resolution: "detect-newline@npm:3.1.0" + checksum: c38cfc8eeb9fda09febb44bcd85e467c970d4e3bf526095394e5a4f18bc26dd0cf6b22c69c1fa9969261521c593836db335c2795218f6d781a512aea2fb8209d + languageName: node + linkType: hard + +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -5545,6 +6212,13 @@ __metadata: languageName: node linkType: hard +"emittery@npm:^0.13.1": + version: 0.13.1 + resolution: "emittery@npm:0.13.1" + checksum: 1573d0ae29ab34661b6c63251ff8f5facd24ccf6a823f19417ae8ba8c88ea450325788c67f16c99edec8de4b52ce93a10fe441ece389fd156e88ee7dab9bfa35 + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -6107,6 +6781,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 + languageName: node + linkType: hard + "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -6383,6 +7064,16 @@ __metadata: languageName: node linkType: hard +"esprima@npm:^4.0.0": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 + languageName: node + linkType: hard + "esquery@npm:^1.4.2": version: 1.5.0 resolution: "esquery@npm:1.5.0" @@ -6432,6 +7123,26 @@ __metadata: languageName: node linkType: hard +"exit@npm:^0.1.2": + version: 0.1.2 + resolution: "exit@npm:0.1.2" + checksum: 71d2ad9b36bc25bb8b104b17e830b40a08989be7f7d100b13269aaae7c3784c3e6e1e88a797e9e87523993a25ba27c8958959a554535370672cfb4d824af8989 + languageName: node + linkType: hard + +"expect@npm:^29.0.0, expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" + dependencies: + "@jest/expect-utils": "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 2eddeace66e68b8d8ee5f7be57f3014b19770caaf6815c7a08d131821da527fb8c8cb7b3dcd7c883d2d3d8d184206a4268984618032d1e4b16dc8d6596475d41 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -6466,7 +7177,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:^2.0.0": +"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: 7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b @@ -6496,6 +7207,15 @@ __metadata: languageName: node linkType: hard +"fb-watchman@npm:^2.0.0": + version: 2.0.2 + resolution: "fb-watchman@npm:2.0.2" + dependencies: + bser: "npm:2.1.1" + checksum: feae89ac148adb8f6ae8ccd87632e62b13563e6fb114cacb5265c51f585b17e2e268084519fb2edd133872f1d47a18e6bfd7e5e08625c0d41b93149694187581 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -6530,7 +7250,7 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^4.1.0": +"find-up@npm:^4.0.0, find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" dependencies: @@ -6633,6 +7353,7 @@ __metadata: "@trivago/prettier-plugin-sort-imports": "npm:^4.3.0" "@tryghost/admin-api": "npm:^1.13.11" "@tryghost/content-api": "npm:^1.11.20" + "@types/jest": "npm:^29.5.11" "@types/node": "npm:^20.11.5" "@types/nodemailer": "npm:^6.4.14" "@types/ramda": "npm:^0.29.9" @@ -6657,6 +7378,7 @@ __metadata: husky: "npm:^8.0.3" i18next-parser: "npm:^8.12.0" isomorphic-dompurify: "npm:^2.2.0" + jest: "npm:^29.7.0" jsdom: "npm:^23.2.0" markdown-to-jsx: "npm:^7.4.0" million: "npm:^2.6.4" @@ -6680,6 +7402,7 @@ __metadata: stylelint-order: "npm:^6.0.4" stylelint-scss: "npm:^6.0.0" swiper: "npm:^6.8.4" + ts-jest: "npm:^29.1.1" ts-unused-exports: "npm:^10.0.1" tsx: "npm:^4.7.0" typed-scss-modules: "npm:^8.0.0" @@ -6831,7 +7554,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": +"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -6850,7 +7573,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -6920,6 +7643,13 @@ __metadata: languageName: node linkType: hard +"get-package-type@npm:^0.1.0": + version: 0.1.0 + resolution: "get-package-type@npm:0.1.0" + checksum: e34cdf447fdf1902a1f6d5af737eaadf606d2ee3518287abde8910e04159368c268568174b2e71102b87b26c2020486f126bfca9c4fb1ceb986ff99b52ecd1be + languageName: node + linkType: hard + "get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -7010,7 +7740,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.3, glob@npm:^7.2.0": +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -7108,7 +7838,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.10, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.8": +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.10, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.8, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -7309,7 +8039,7 @@ __metadata: languageName: node linkType: hard -"html-escaper@npm:^2.0.2": +"html-escaper@npm:^2.0.0, html-escaper@npm:^2.0.2": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" checksum: 208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 @@ -7478,6 +8208,18 @@ __metadata: languageName: node linkType: hard +"import-local@npm:^3.0.2": + version: 3.1.0 + resolution: "import-local@npm:3.1.0" + dependencies: + pkg-dir: "npm:^4.2.0" + resolve-cwd: "npm:^3.0.0" + bin: + import-local-fixture: fixtures/cli.js + checksum: c67ecea72f775fe8684ca3d057e54bdb2ae28c14bf261d2607c269c18ea0da7b730924c06262eca9aed4b8ab31e31d65bc60b50e7296c85908a56e2f7d41ecd2 + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -7644,6 +8386,13 @@ __metadata: languageName: node linkType: hard +"is-generator-fn@npm:^2.0.0": + version: 2.1.0 + resolution: "is-generator-fn@npm:2.1.0" + checksum: 2957cab387997a466cd0bf5c1b6047bd21ecb32bdcfd8996b15747aa01002c1c88731802f1b3d34ac99f4f6874b626418bd118658cf39380fe5fff32a3af9c4d + languageName: node + linkType: hard + "is-generator-function@npm:^1.0.10": version: 1.0.10 resolution: "is-generator-function@npm:1.0.10" @@ -7905,6 +8654,71 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b + languageName: node + linkType: hard + +"istanbul-lib-instrument@npm:^5.0.4": + version: 5.2.1 + resolution: "istanbul-lib-instrument@npm:5.2.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^6.3.0" + checksum: 8a1bdf3e377dcc0d33ec32fe2b6ecacdb1e4358fd0eb923d4326bb11c67622c0ceb99600a680f3dad5d29c66fc1991306081e339b4d43d0b8a2ab2e1d910a6ee + languageName: node + linkType: hard + +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^7.5.4" + checksum: 313d61aca3f82a04ad9377841d05061d603ea3d4a4dd281fdda2479ec4ddbc86dc1792c73651f21c93480570d1ecadc5f63011e2df86f30ee662b62c0c00e3d8 + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0": + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" + dependencies: + istanbul-lib-coverage: "npm:^3.0.0" + make-dir: "npm:^4.0.0" + supports-color: "npm:^7.1.0" + checksum: 84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7 + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" + dependencies: + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + source-map: "npm:^0.6.1" + checksum: 19e4cc405016f2c906dff271a76715b3e881fa9faeb3f09a86cb99b8512b3a5ed19cadfe0b54c17ca0e54c1142c9c6de9330d65506e35873994e06634eebeb66 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.3": + version: 3.1.6 + resolution: "istanbul-reports@npm:3.1.6" + dependencies: + html-escaper: "npm:^2.0.0" + istanbul-lib-report: "npm:^3.0.0" + checksum: ec3f1bdbc51b3e0b325a5b9f4ad31a247697f31001df4e81075f7980413f14da1b5adfec574fd156efd3b0464023f61320f6718efc66ee72b32d89611cef99dd + languageName: node + linkType: hard + "iterator.prototype@npm:^1.1.2": version: 1.1.2 resolution: "iterator.prototype@npm:1.1.2" @@ -7938,6 +8752,445 @@ __metadata: languageName: node linkType: hard +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" + dependencies: + execa: "npm:^5.0.0" + jest-util: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + checksum: e071384d9e2f6bb462231ac53f29bff86f0e12394c1b49ccafbad225ce2ab7da226279a8a94f421949920bef9be7ef574fd86aee22e8adfa149be73554ab828b + languageName: node + linkType: hard + +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + co: "npm:^4.6.0" + dedent: "npm:^1.0.0" + is-generator-fn: "npm:^2.0.0" + jest-each: "npm:^29.7.0" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + pretty-format: "npm:^29.7.0" + pure-rand: "npm:^6.0.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.3" + checksum: 8d15344cf7a9f14e926f0deed64ed190c7a4fa1ed1acfcd81e4cc094d3cc5bf7902ebb7b874edc98ada4185688f90c91e1747e0dfd7ac12463b097968ae74b5e + languageName: node + linkType: hard + +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" + dependencies: + "@jest/core": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + create-jest: "npm:^29.7.0" + exit: "npm:^0.1.2" + import-local: "npm:^3.0.2" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + yargs: "npm:^17.3.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: a658fd55050d4075d65c1066364595962ead7661711495cfa1dfeecf3d6d0a8ffec532f3dbd8afbb3e172dd5fd2fb2e813c5e10256e7cf2fea766314942fb43a + languageName: node + linkType: hard + +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@jest/test-sequencer": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-jest: "npm:^29.7.0" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + deepmerge: "npm:^4.2.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-circus: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + parse-json: "npm:^5.2.0" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-json-comments: "npm:^3.1.1" + peerDependencies: + "@types/node": "*" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + ts-node: + optional: true + checksum: bab23c2eda1fff06e0d104b00d6adfb1d1aabb7128441899c9bff2247bd26710b050a5364281ce8d52b46b499153bf7e3ee88b19831a8f3451f1477a0246a0f1 + languageName: node + linkType: hard + +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + diff-sequences: "npm:^29.6.3" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 89a4a7f182590f56f526443dde69acefb1f2f0c9e59253c61d319569856c4931eae66b8a3790c443f529267a0ddba5ba80431c585deed81827032b2b2a1fc999 + languageName: node + linkType: hard + +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" + dependencies: + detect-newline: "npm:^3.0.0" + checksum: d932a8272345cf6b6142bb70a2bb63e0856cc0093f082821577ea5bdf4643916a98744dfc992189d2b1417c38a11fa42466f6111526bc1fb81366f56410f3be9 + languageName: node + linkType: hard + +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + pretty-format: "npm:^29.7.0" + checksum: f7f9a90ebee80cc688e825feceb2613627826ac41ea76a366fa58e669c3b2403d364c7c0a74d862d469b103c843154f8456d3b1c02b487509a12afa8b59edbb4 + languageName: node + linkType: hard + +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 61f04fec077f8b1b5c1a633e3612fc0c9aa79a0ab7b05600683428f1e01a4d35346c474bde6f439f9fcc1a4aa9a2861ff852d079a43ab64b02105d1004b2592b + languageName: node + linkType: hard + +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 552e7a97a983d3c2d4e412a44eb7de0430ff773dd99f7500962c268d6dfbfa431d7d08f919c9d960530e5f7f78eb47f267ad9b318265e5092b3ff9ede0db7c2b + languageName: node + linkType: hard + +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/graceful-fs": "npm:^4.1.3" + "@types/node": "npm:*" + anymatch: "npm:^3.0.3" + fb-watchman: "npm:^2.0.0" + fsevents: "npm:^2.3.2" + graceful-fs: "npm:^4.2.9" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + walker: "npm:^1.0.8" + dependenciesMeta: + fsevents: + optional: true + checksum: 2683a8f29793c75a4728787662972fedd9267704c8f7ef9d84f2beed9a977f1cf5e998c07b6f36ba5603f53cb010c911fe8cd0ac9886e073fe28ca66beefd30c + languageName: node + linkType: hard + +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" + dependencies: + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 71bb9f77fc489acb842a5c7be030f2b9acb18574dc9fb98b3100fc57d422b1abc55f08040884bd6e6dbf455047a62f7eaff12aa4058f7cbdc11558718ca6a395 + languageName: node + linkType: hard + +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 0d0e70b28fa5c7d4dce701dc1f46ae0922102aadc24ed45d594dd9b7ae0a8a6ef8b216718d1ab79e451291217e05d4d49a82666e1a3cc2b428b75cd9c933244e + languageName: node + linkType: hard + +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" + dependencies: + "@babel/code-frame": "npm:^7.12.13" + "@jest/types": "npm:^29.6.3" + "@types/stack-utils": "npm:^2.0.0" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.3" + checksum: 850ae35477f59f3e6f27efac5215f706296e2104af39232bb14e5403e067992afb5c015e87a9243ec4d9df38525ef1ca663af9f2f4766aa116f127247008bd22 + languageName: node + linkType: hard + +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-util: "npm:^29.7.0" + checksum: 7b9f8349ee87695a309fe15c46a74ab04c853369e5c40952d68061d9dc3159a0f0ed73e215f81b07ee97a9faaf10aebe5877a9d6255068a0977eae6a9ff1d5ac + languageName: node + linkType: hard + +"jest-pnp-resolver@npm:^1.2.2": + version: 1.2.3 + resolution: "jest-pnp-resolver@npm:1.2.3" + peerDependencies: + jest-resolve: "*" + peerDependenciesMeta: + jest-resolve: + optional: true + checksum: 86eec0c78449a2de733a6d3e316d49461af6a858070e113c97f75fb742a48c2396ea94150cbca44159ffd4a959f743a47a8b37a792ef6fdad2cf0a5cba973fac + languageName: node + linkType: hard + +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 4e33fb16c4f42111159cafe26397118dcfc4cf08bc178a67149fb05f45546a91928b820894572679d62559839d0992e21080a1527faad65daaae8743a5705a3b + languageName: node + linkType: hard + +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" + dependencies: + jest-regex-util: "npm:^29.6.3" + jest-snapshot: "npm:^29.7.0" + checksum: b6e9ad8ae5b6049474118ea6441dfddd385b6d1fc471db0136f7c8fbcfe97137a9665e4f837a9f49f15a29a1deb95a14439b7aec812f3f99d08f228464930f0d + languageName: node + linkType: hard + +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-pnp-resolver: "npm:^1.2.2" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + resolve: "npm:^1.20.0" + resolve.exports: "npm:^2.0.0" + slash: "npm:^3.0.0" + checksum: 59da5c9c5b50563e959a45e09e2eace783d7f9ac0b5dcc6375dea4c0db938d2ebda97124c8161310082760e8ebbeff9f6b177c15ca2f57fb424f637a5d2adb47 + languageName: node + linkType: hard + +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/environment": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + emittery: "npm:^0.13.1" + graceful-fs: "npm:^4.2.9" + jest-docblock: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-leak-detector: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-resolve: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + source-map-support: "npm:0.5.13" + checksum: 2194b4531068d939f14c8d3274fe5938b77fa73126aedf9c09ec9dec57d13f22c72a3b5af01ac04f5c1cf2e28d0ac0b4a54212a61b05f10b5d6b47f2a1097bb4 + languageName: node + linkType: hard + +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/globals": "npm:^29.7.0" + "@jest/source-map": "npm:^29.6.3" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + cjs-module-lexer: "npm:^1.0.0" + collect-v8-coverage: "npm:^1.0.0" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-bom: "npm:^4.0.0" + checksum: 7cd89a1deda0bda7d0941835434e44f9d6b7bd50b5c5d9b0fc9a6c990b2d4d2cab59685ab3cb2850ed4cc37059f6de903af5a50565d7f7f1192a77d3fd6dd2a6 + languageName: node + linkType: hard + +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@babel/generator": "npm:^7.7.2" + "@babel/plugin-syntax-jsx": "npm:^7.7.2" + "@babel/plugin-syntax-typescript": "npm:^7.7.2" + "@babel/types": "npm:^7.3.3" + "@jest/expect-utils": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + chalk: "npm:^4.0.0" + expect: "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + natural-compare: "npm:^1.4.0" + pretty-format: "npm:^29.7.0" + semver: "npm:^7.5.3" + checksum: 6e9003c94ec58172b4a62864a91c0146513207bedf4e0a06e1e2ac70a4484088a2683e3a0538d8ea913bcfd53dc54a9b98a98cdfa562e7fe1d1339aeae1da570 + languageName: node + linkType: hard + +"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + graceful-fs: "npm:^4.2.9" + picomatch: "npm:^2.2.3" + checksum: bc55a8f49fdbb8f51baf31d2a4f312fb66c9db1483b82f602c9c990e659cdd7ec529c8e916d5a89452ecbcfae4949b21b40a7a59d4ffc0cd813a973ab08c8150 + languageName: node + linkType: hard + +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + camelcase: "npm:^6.2.0" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + leven: "npm:^3.1.0" + pretty-format: "npm:^29.7.0" + checksum: a20b930480c1ed68778c739f4739dce39423131bc070cd2505ddede762a5570a256212e9c2401b7ae9ba4d7b7c0803f03c5b8f1561c62348213aba18d9dbece2 + languageName: node + linkType: hard + +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" + dependencies: + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + emittery: "npm:^0.13.1" + jest-util: "npm:^29.7.0" + string-length: "npm:^4.0.1" + checksum: ec6c75030562fc8f8c727cb8f3b94e75d831fc718785abfc196e1f2a2ebc9a2e38744a15147170039628a853d77a3b695561ce850375ede3a4ee6037a2574567 + languageName: node + linkType: hard + +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" + dependencies: + "@types/node": "npm:*" + jest-util: "npm:^29.7.0" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.0.0" + checksum: 5570a3a005b16f46c131968b8a5b56d291f9bbb85ff4217e31c80bd8a02e7de799e59a54b95ca28d5c302f248b54cbffde2d177c2f0f52ffcee7504c6eabf660 + languageName: node + linkType: hard + +"jest@npm:^29.7.0": + version: 29.7.0 + resolution: "jest@npm:29.7.0" + dependencies: + "@jest/core": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + import-local: "npm:^3.0.2" + jest-cli: "npm:^29.7.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: f40eb8171cf147c617cc6ada49d062fbb03b4da666cb8d39cdbfb739a7d75eea4c3ca150fb072d0d273dce0c753db4d0467d54906ad0293f59c54f9db4a09d8b + languageName: node + linkType: hard + "jiti@npm:^1.19.1": version: 1.21.0 resolution: "jiti@npm:1.21.0" @@ -7972,6 +9225,18 @@ __metadata: languageName: node linkType: hard +"js-yaml@npm:^3.13.1": + version: 3.14.1 + resolution: "js-yaml@npm:3.14.1" + dependencies: + argparse: "npm:^1.0.7" + esprima: "npm:^4.0.0" + bin: + js-yaml: bin/js-yaml.js + checksum: 6746baaaeac312c4db8e75fa22331d9a04cccb7792d126ed8ce6a0bbcfef0cedaddd0c5098fade53db067c09fe00aa1c957674b4765610a8b06a5a189e46433b + languageName: node + linkType: hard + "jsdom@npm:^23.1.0, jsdom@npm:^23.2.0": version: 23.2.0 resolution: "jsdom@npm:23.2.0" @@ -8178,6 +9443,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^3.0.3": + version: 3.0.3 + resolution: "kleur@npm:3.0.3" + checksum: cd3a0b8878e7d6d3799e54340efe3591ca787d9f95f109f28129bdd2915e37807bf8918bb295ab86afb8c82196beec5a1adcaf29042ce3f2bd932b038fe3aa4b + languageName: node + linkType: hard + "kleur@npm:^4.1.5": version: 4.1.5 resolution: "kleur@npm:4.1.5" @@ -8215,6 +9487,13 @@ __metadata: languageName: node linkType: hard +"leven@npm:^3.1.0": + version: 3.1.0 + resolution: "leven@npm:3.1.0" + checksum: cd778ba3fbab0f4d0500b7e87d1f6e1f041507c56fdcd47e8256a3012c98aaee371d4c15e0a76e0386107af2d42e2b7466160a2d80688aaa03e66e49949f42df + languageName: node + linkType: hard + "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" @@ -8357,6 +9636,13 @@ __metadata: languageName: node linkType: hard +"lodash.memoize@npm:4.x": + version: 4.1.2 + resolution: "lodash.memoize@npm:4.1.2" + checksum: c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -8472,6 +9758,22 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68 + languageName: node + linkType: hard + +"make-error@npm:1.x": + version: 1.3.6 + resolution: "make-error@npm:1.3.6" + checksum: 171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f + languageName: node + linkType: hard + "make-fetch-happen@npm:^13.0.0": version: 13.0.0 resolution: "make-fetch-happen@npm:13.0.0" @@ -8491,6 +9793,15 @@ __metadata: languageName: node linkType: hard +"makeerror@npm:1.0.12": + version: 1.0.12 + resolution: "makeerror@npm:1.0.12" + dependencies: + tmpl: "npm:1.0.5" + checksum: b0e6e599780ce6bab49cc413eba822f7d1f0dfebd1c103eaa3785c59e43e22c59018323cf9e1708f0ef5329e94a745d163fcbb6bff8e4c6742f9be9e86f3500c + languageName: node + linkType: hard + "map-obj@npm:^1.0.0": version: 1.0.1 resolution: "map-obj@npm:1.0.1" @@ -9007,6 +10318,13 @@ __metadata: languageName: node linkType: hard +"node-int64@npm:^0.4.0": + version: 0.4.0 + resolution: "node-int64@npm:0.4.0" + checksum: a6a4d8369e2f2720e9c645255ffde909c0fbd41c92ea92a5607fc17055955daac99c1ff589d421eee12a0d24e99f7bfc2aabfeb1a4c14742f6c099a51863f31a + languageName: node + linkType: hard + "node-releases@npm:^2.0.14": version: 2.0.14 resolution: "node-releases@npm:2.0.14" @@ -9242,7 +10560,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2": +"p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -9414,13 +10732,29 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be languageName: node linkType: hard +"pirates@npm:^4.0.4": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 + languageName: node + linkType: hard + +"pkg-dir@npm:^4.2.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" + dependencies: + find-up: "npm:^4.0.0" + checksum: c56bda7769e04907a88423feb320babaed0711af8c436ce3e56763ab1021ba107c7b0cafb11cde7529f669cfc22bffcaebffb573645cbd63842ea9fb17cd7728 + languageName: node + linkType: hard + "playwright-core@npm:1.41.0": version: 1.41.0 resolution: "playwright-core@npm:1.41.0" @@ -9614,6 +10948,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: edc5ff89f51916f036c62ed433506b55446ff739358de77207e63e88a28ca2894caac6e73dcb68166a606e51c8087d32d400473e6a9fdd2dbe743f46c9c0276f + languageName: node + linkType: hard + "proc-log@npm:^3.0.0": version: 3.0.0 resolution: "proc-log@npm:3.0.0" @@ -9645,6 +10990,16 @@ __metadata: languageName: node linkType: hard +"prompts@npm:^2.0.1": + version: 2.4.2 + resolution: "prompts@npm:2.4.2" + dependencies: + kleur: "npm:^3.0.3" + sisteransi: "npm:^1.0.5" + checksum: 16f1ac2977b19fe2cf53f8411cc98db7a3c8b115c479b2ca5c82b5527cd937aa405fa04f9a5960abeb9daef53191b53b4d13e35c1f5d50e8718c76917c5f1ea4 + languageName: node + linkType: hard + "prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" @@ -9701,6 +11056,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^6.0.0": + version: 6.0.4 + resolution: "pure-rand@npm:6.0.4" + checksum: 0fe7b12f25b10ea5b804598a6f37e4bcf645d2be6d44fe963741f014bf0095bdb6ff525106d6da6e76addc8142358fd380f1a9b8c62ea4d5516bf26a96a37c95 + languageName: node + linkType: hard + "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -9766,7 +11128,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.2.0": +"react-is@npm:^18.0.0, react-is@npm:^18.2.0": version: 18.2.0 resolution: "react-is@npm:18.2.0" checksum: 6eb5e4b28028c23e2bfcf73371e72cd4162e4ac7ab445ddae2afe24e347a37d6dc22fae6e1748632cd43c6d4f9b8f86dcf26bf9275e1874f436d129952528ae0 @@ -10033,6 +11395,15 @@ __metadata: languageName: node linkType: hard +"resolve-cwd@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-cwd@npm:3.0.0" + dependencies: + resolve-from: "npm:^5.0.0" + checksum: e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 + languageName: node + linkType: hard + "resolve-from@npm:5.0.0, resolve-from@npm:^5.0.0": version: 5.0.0 resolution: "resolve-from@npm:5.0.0" @@ -10072,7 +11443,14 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.22.4": +"resolve.exports@npm:^2.0.0": + version: 2.0.2 + resolution: "resolve.exports@npm:2.0.2" + checksum: cc4cffdc25447cf34730f388dca5021156ba9302a3bad3d7f168e790dc74b2827dff603f1bc6ad3d299bac269828dca96dd77e036dc9fba6a2a1807c47ab5c98 + languageName: node + linkType: hard + +"resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.4": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -10098,7 +11476,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": +"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -10309,7 +11687,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.5.4, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.4": +"semver@npm:7.5.4, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -10320,7 +11698,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.3.1": +"semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -10459,7 +11837,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.3": +"signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: 25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 @@ -10493,6 +11871,13 @@ __metadata: languageName: node linkType: hard +"sisteransi@npm:^1.0.5": + version: 1.0.5 + resolution: "sisteransi@npm:1.0.5" + checksum: 230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -10565,6 +11950,16 @@ __metadata: languageName: node linkType: hard +"source-map-support@npm:0.5.13": + version: 0.5.13 + resolution: "source-map-support@npm:0.5.13" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 137539f8c453fa0f496ea42049ab5da4569f96781f6ac8e5bfda26937be9494f4e8891f523c5f98f0e85f71b35d74127a00c46f83f6a4f54672b58d53202565e + languageName: node + linkType: hard + "source-map@npm:^0.5.0, source-map@npm:^0.5.7": version: 0.5.7 resolution: "source-map@npm:0.5.7" @@ -10572,6 +11967,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.6.0, source-map@npm:^0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + "spdx-correct@npm:^3.0.0": version: 3.2.0 resolution: "spdx-correct@npm:3.2.0" @@ -10629,6 +12031,13 @@ __metadata: languageName: node linkType: hard +"sprintf-js@npm:~1.0.2": + version: 1.0.3 + resolution: "sprintf-js@npm:1.0.3" + checksum: ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb + languageName: node + linkType: hard + "ssr-window@npm:^3.0.0, ssr-window@npm:^3.0.0-alpha.1": version: 3.0.0 resolution: "ssr-window@npm:3.0.0" @@ -10645,6 +12054,15 @@ __metadata: languageName: node linkType: hard +"stack-utils@npm:^2.0.3": + version: 2.0.6 + resolution: "stack-utils@npm:2.0.6" + dependencies: + escape-string-regexp: "npm:^2.0.0" + checksum: 651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a + languageName: node + linkType: hard + "stream-composer@npm:^1.0.2": version: 1.0.2 resolution: "stream-composer@npm:1.0.2" @@ -10678,6 +12096,16 @@ __metadata: languageName: node linkType: hard +"string-length@npm:^4.0.1": + version: 4.0.2 + resolution: "string-length@npm:4.0.2" + dependencies: + char-regex: "npm:^1.0.2" + strip-ansi: "npm:^6.0.0" + checksum: 1cd77409c3d7db7bc59406f6bcc9ef0783671dcbabb23597a1177c166906ef2ee7c8290f78cae73a8aec858768f189d2cb417797df5e15ec4eb5e16b3346340c + languageName: node + linkType: hard + "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -10800,6 +12228,13 @@ __metadata: languageName: node linkType: hard +"strip-bom@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-bom@npm:4.0.0" + checksum: 26abad1172d6bc48985ab9a5f96c21e440f6e7e476686de49be813b5a59b3566dccb5c525b831ec54fe348283b47f3ffb8e080bc3f965fde12e84df23f6bb7ef + languageName: node + linkType: hard + "strip-final-newline@npm:^2.0.0": version: 2.0.0 resolution: "strip-final-newline@npm:2.0.0" @@ -10992,6 +12427,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^8.0.0": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 + languageName: node + linkType: hard + "supports-hyperlinks@npm:^3.0.0": version: 3.0.0 resolution: "supports-hyperlinks@npm:3.0.0" @@ -11114,6 +12558,17 @@ __metadata: languageName: node linkType: hard +"test-exclude@npm:^6.0.0": + version: 6.0.0 + resolution: "test-exclude@npm:6.0.0" + dependencies: + "@istanbuljs/schema": "npm:^0.1.2" + glob: "npm:^7.1.4" + minimatch: "npm:^3.0.4" + checksum: 019d33d81adff3f9f1bfcff18125fb2d3c65564f437d9be539270ee74b994986abb8260c7c2ce90e8f30162178b09dbbce33c6389273afac4f36069c48521f57 + languageName: node + linkType: hard + "text-extensions@npm:^2.0.0": version: 2.4.0 resolution: "text-extensions@npm:2.4.0" @@ -11164,6 +12619,13 @@ __metadata: languageName: node linkType: hard +"tmpl@npm:1.0.5": + version: 1.0.5 + resolution: "tmpl@npm:1.0.5" + checksum: f935537799c2d1922cb5d6d3805f594388f75338fe7a4a9dac41504dd539704ca4db45b883b52e7b0aa5b2fd5ddadb1452bf95cd23a69da2f793a843f9451cc9 + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -11233,6 +12695,39 @@ __metadata: languageName: node linkType: hard +"ts-jest@npm:^29.1.1": + version: 29.1.1 + resolution: "ts-jest@npm:29.1.1" + dependencies: + bs-logger: "npm:0.x" + fast-json-stable-stringify: "npm:2.x" + jest-util: "npm:^29.0.0" + json5: "npm:^2.2.3" + lodash.memoize: "npm:4.x" + make-error: "npm:1.x" + semver: "npm:^7.5.3" + yargs-parser: "npm:^21.0.1" + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/types": ^29.0.0 + babel-jest: ^29.0.0 + jest: ^29.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: 6c45e0aeeff9cc54a64f931c43e1b99f4a1f0ddf44786cc128e7e55603ab7473c8c8f62fd83bd7e51bfe83e3c0c683132152efaeb844516bf7c923f4e92d157d + languageName: node + linkType: hard + "ts-toolbelt@npm:^9.6.0": version: 9.6.0 resolution: "ts-toolbelt@npm:9.6.0" @@ -11301,6 +12796,13 @@ __metadata: languageName: node linkType: hard +"type-detect@npm:4.0.8": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: 8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd + languageName: node + linkType: hard + "type-fest@npm:^0.18.0": version: 0.18.1 resolution: "type-fest@npm:0.18.1" @@ -11315,6 +12817,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: 902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 + languageName: node + linkType: hard + "type-fest@npm:^0.6.0": version: 0.6.0 resolution: "type-fest@npm:0.6.0" @@ -11636,6 +13145,17 @@ __metadata: languageName: node linkType: hard +"v8-to-istanbul@npm:^9.0.1": + version: 9.2.0 + resolution: "v8-to-istanbul@npm:9.2.0" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.12" + "@types/istanbul-lib-coverage": "npm:^2.0.1" + convert-source-map: "npm:^2.0.0" + checksum: e691ba4dd0dea4a884e52c37dbda30cce6f9eeafe9b26721e449429c6bb0f4b6d1e33fabe7711d0f67f7a34c3bfd56c873f7375bba0b1534e6a2843ce99550e5 + languageName: node + linkType: hard + "validate-npm-package-license@npm:^3.0.1": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -11750,6 +13270,15 @@ __metadata: languageName: node linkType: hard +"walker@npm:^1.0.8": + version: 1.0.8 + resolution: "walker@npm:1.0.8" + dependencies: + makeerror: "npm:1.0.12" + checksum: a17e037bccd3ca8a25a80cb850903facdfed0de4864bd8728f1782370715d679fa72e0a0f5da7c1c1379365159901e5935f35be531229da53bbfc0efdabdb48e + languageName: node + linkType: hard + "webidl-conversions@npm:^7.0.0": version: 7.0.0 resolution: "webidl-conversions@npm:7.0.0" @@ -11947,6 +13476,16 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^4.0.2": + version: 4.0.2 + resolution: "write-file-atomic@npm:4.0.2" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^3.0.7" + checksum: a2c282c95ef5d8e1c27b335ae897b5eca00e85590d92a3fd69a437919b7b93ff36a69ea04145da55829d2164e724bc62202cdb5f4b208b425aba0807889375c7 + languageName: node + linkType: hard + "write-file-atomic@npm:^5.0.1": version: 5.0.1 resolution: "write-file-atomic@npm:5.0.1" @@ -12069,7 +13608,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2