diff --git a/apps/trading/.env b/apps/trading/.env index 7f273cac4..c4df25faa 100644 --- a/apps/trading/.env +++ b/apps/trading/.env @@ -24,6 +24,7 @@ NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=true +NX_REFERRALS=true NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket diff --git a/apps/trading/.env.capsule b/apps/trading/.env.capsule index 6d0e681ed..99187803b 100644 --- a/apps/trading/.env.capsule +++ b/apps/trading/.env.capsule @@ -24,6 +24,7 @@ NX_STOP_ORDERS=false # NX_ICEBERG_ORDERS # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=false +NX_REFERRALS=false NX_TENDERMINT_URL=http://localhost:26617 NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket diff --git a/apps/trading/.env.devnet b/apps/trading/.env.devnet index 99d465c04..d315419a8 100644 --- a/apps/trading/.env.devnet +++ b/apps/trading/.env.devnet @@ -22,6 +22,7 @@ NX_STOP_ORDERS=true # NX_ICEBERG_ORDERS # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=true +NX_REFERRALS=true NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/ NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket diff --git a/apps/trading/.env.mainnet b/apps/trading/.env.mainnet index 9b43a8c28..6f3d84478 100644 --- a/apps/trading/.env.mainnet +++ b/apps/trading/.env.mainnet @@ -27,6 +27,7 @@ NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=true +NX_REFERRALS=false NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket diff --git a/apps/trading/.env.mainnet-mirror b/apps/trading/.env.mainnet-mirror index bde8e334b..78e1f9f7e 100644 --- a/apps/trading/.env.mainnet-mirror +++ b/apps/trading/.env.mainnet-mirror @@ -24,6 +24,7 @@ NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=false +NX_REFERRALS=false NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket diff --git a/apps/trading/.env.stagnet1 b/apps/trading/.env.stagnet1 index fc93d40cb..2e26587b0 100644 --- a/apps/trading/.env.stagnet1 +++ b/apps/trading/.env.stagnet1 @@ -24,3 +24,4 @@ NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=true +NX_REFERRALS=true \ No newline at end of file diff --git a/apps/trading/.env.testnet b/apps/trading/.env.testnet index 78caf98d6..53a7c10b2 100644 --- a/apps/trading/.env.testnet +++ b/apps/trading/.env.testnet @@ -24,6 +24,7 @@ NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=true +NX_REFERRALS=true NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket diff --git a/apps/trading/.env.validators-testnet b/apps/trading/.env.validators-testnet index f36a8c454..5ac9738ac 100644 --- a/apps/trading/.env.validators-testnet +++ b/apps/trading/.env.validators-testnet @@ -25,6 +25,7 @@ NX_STOP_ORDERS=true NX_ICEBERG_ORDERS=true # NX_PRODUCT_PERPETUALS NX_METAMASK_SNAPS=false +NX_REFERRALS=false NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket diff --git a/apps/trading/client-pages/markets/use-column-defs.tsx b/apps/trading/client-pages/markets/use-column-defs.tsx index 09898ab9d..e2b506348 100644 --- a/apps/trading/client-pages/markets/use-column-defs.tsx +++ b/apps/trading/client-pages/markets/use-column-defs.tsx @@ -28,7 +28,6 @@ export const useColumnDefs = () => { { headerName: t('Market'), field: 'tradableInstrument.instrument.code', - flex: 2, cellRenderer: ({ value, data, @@ -50,7 +49,6 @@ export const useColumnDefs = () => { { headerName: t('Description'), field: 'tradableInstrument.instrument.name', - flex: 2, }, { headerName: t('Trading mode'), diff --git a/apps/trading/client-pages/referrals/apply-code-form.tsx b/apps/trading/client-pages/referrals/apply-code-form.tsx new file mode 100644 index 000000000..2e62aae78 --- /dev/null +++ b/apps/trading/client-pages/referrals/apply-code-form.tsx @@ -0,0 +1,102 @@ +import { + Input, + InputError, + VegaIcon, + VegaIconNames, +} from '@vegaprotocol/ui-toolkit'; +import type { FieldValues } from 'react-hook-form'; +import { useForm } from 'react-hook-form'; +import classNames from 'classnames'; +import { useSearchParams } from 'react-router-dom'; +import { useEffect, useState } from 'react'; +import { Button } from './buttons'; +import { useVegaWallet } from '@vegaprotocol/wallet'; + +export const ApplyCodeForm = () => { + const [finalized, setFinalized] = useState(false); + const { isReadOnly, pubKey, sendTx } = useVegaWallet(); + const { + register, + handleSubmit, + formState: { errors }, + setValue, + setError, + } = useForm(); + const [params] = useSearchParams(); + + useEffect(() => { + const code = params.get('code'); + if (code) setValue('code', code); + }, [params, setValue]); + + const onSubmit = ({ code }: FieldValues) => { + if (isReadOnly || !pubKey || !code || code.length === 0) { + return; + } + + sendTx(pubKey, { + applyReferralCode: { + id: code as string, + }, + }) + .then((res) => { + setFinalized(true); + }) + .catch((err) => { + setError('code', { + type: 'required', + message: 'Your code has been rejected', + }); + }); + }; + + if (finalized) { + return ( +
+

+ + + {' '} + Code applied +

+
+ ); + } + + return ( +
+

+ Apply a referral code +

+

Enter a referral code

+
+ + +
+ {errors.code && ( + {errors.code.message?.toString()} + )} +
+ ); +}; diff --git a/apps/trading/client-pages/referrals/buttons.tsx b/apps/trading/client-pages/referrals/buttons.tsx new file mode 100644 index 000000000..c4b3376ba --- /dev/null +++ b/apps/trading/client-pages/referrals/buttons.tsx @@ -0,0 +1,83 @@ +import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit'; +import classNames from 'classnames'; +import type { ComponentProps, ButtonHTMLAttributes } from 'react'; +import { forwardRef } from 'react'; +import { NavLink } from 'react-router-dom'; + +type RainbowButtonProps = { + variant?: 'full' | 'border'; +}; + +export const RainbowButton = ({ + variant = 'full', + children, + className, + ...props +}: RainbowButtonProps & ButtonHTMLAttributes) => ( + +); + +const RAINBOW_TAB_STYLE = classNames( + 'inline-block', + 'bg-vega-clight-500 dark:bg-vega-cdark-500 hover:bg-vega-clight-400 dark:hover:bg-vega-cdark-400', + 'data-[state="active"]:text-white data-[state="active"]:bg-rainbow data-[state="active"]:hover:bg-none data-[state="active"]:hover:bg-vega-pink-500 dark:data-[state="active"]:hover:bg-vega-pink-500', + '[&.active]:text-white [&.active]:bg-rainbow [&.active]:hover:bg-none [&.active]:hover:bg-vega-pink-500 dark:[&.active]:hover:bg-vega-pink-500', + 'px-5 py-3', + 'first:rounded-tl-lg last:rounded-tr-lg' +); + +export const RainbowTabButton = forwardRef< + HTMLButtonElement, + ButtonHTMLAttributes +>(({ children, ...props }, ref) => ( + +)); +RainbowTabButton.displayName = 'RainbowTabButton'; + +export const RainbowTabLink = ({ + to, + children, + ...props +}: ComponentProps) => ( + + {children} + +); + +export const Button = forwardRef< + HTMLButtonElement, + ComponentProps +>(({ children, intent, type, ...props }, ref) => { + return ( + + {children} + + ); +}); +Button.displayName = 'TradingButton'; diff --git a/apps/trading/client-pages/referrals/constants.ts b/apps/trading/client-pages/referrals/constants.ts new file mode 100644 index 000000000..3dacbb257 --- /dev/null +++ b/apps/trading/client-pages/referrals/constants.ts @@ -0,0 +1,6 @@ +export const BORDER_COLOR = 'border-vega-clight-500 dark:border-vega-cdark-500'; +export const GRADIENT = + 'bg-gradient-to-b from-vega-clight-800 dark:from-vega-cdark-800 to-transparent'; + +export const SKY_BACKGROUND = + 'bg-[url(/sky-light.png)] dark:bg-[url(/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat bg-local'; diff --git a/apps/trading/client-pages/referrals/create-code-form.tsx b/apps/trading/client-pages/referrals/create-code-form.tsx new file mode 100644 index 000000000..fb211549c --- /dev/null +++ b/apps/trading/client-pages/referrals/create-code-form.tsx @@ -0,0 +1,225 @@ +import { + useVegaWallet, + useVegaWalletDialogStore, + determineId, +} from '@vegaprotocol/wallet'; +import { RainbowButton } from './buttons'; +import { useState } from 'react'; +import { + CopyWithTooltip, + Dialog, + ExternalLink, + InputError, + Intent, + TradingAnchorButton, + TradingButton, + VegaIcon, + VegaIconNames, +} from '@vegaprotocol/ui-toolkit'; +import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; +import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment'; +import { useStakeAvailable } from './hooks/use-stake-available'; + +export const CreateCodeContainer = () => { + const { stakeAvailable, requiredStake } = useStakeAvailable(); + if (stakeAvailable == null || requiredStake == null) { + return null; + } + + return ( + + ); +}; + +export const CreateCodeForm = ({ + currentStakeAvailable, + requiredStake, +}: { + currentStakeAvailable: bigint; + requiredStake: bigint; +}) => { + const [dialogOpen, setDialogOpen] = useState(false); + const openWalletDialog = useVegaWalletDialogStore( + (store) => store.openVegaWalletDialog + ); + const { pubKey } = useVegaWallet(); + + return ( +
+

+ Create a referral code +

+

+ Generate a referral code to share with your friends and start earning + commission. +

+
+
+ { + if (pubKey) { + setDialogOpen(true); + } else { + openWalletDialog(); + } + }} + > + {pubKey ? 'Create a referral code' : 'Connect wallet'} + +
+
+ setDialogOpen(false)} + size="small" + > + + +
+ ); +}; + +const CreateCodeDialog = ({ + setDialogOpen, + currentStakeAvailable, + requiredStake, +}: { + setDialogOpen: (open: boolean) => void; + currentStakeAvailable: bigint; + requiredStake: bigint; +}) => { + const createLink = useLinks(DApp.Governance); + const { isReadOnly, pubKey, sendTx } = useVegaWallet(); + const [err, setErr] = useState(null); + const [code, setCode] = useState(null); + const [status, setStatus] = useState< + 'idle' | 'loading' | 'success' | 'error' + >('idle'); + + const onSubmit = () => { + if (isReadOnly || !pubKey) { + setErr('Not connected'); + } else { + setErr(null); + setStatus('loading'); + setCode(null); + sendTx(pubKey, { + createReferralSet: { + isTeam: false, + }, + }) + .then((res) => { + if (!res) { + setErr(`Invalid response: ${JSON.stringify(res)}`); + return; + } + const code = determineId(res.signature); + setCode(code); + setStatus('success'); + }) + .catch((err) => { + if (err.message.includes('user rejected')) { + setStatus('idle'); + return; + } + setStatus('error'); + setErr(err.message); + }); + } + }; + + const getButtonProps = () => { + if (status === 'idle' || status === 'error') { + return { + children: 'Generate code', + onClick: () => onSubmit(), + }; + } + + if (status === 'loading') { + return { + children: 'Confirm in wallet...', + disabled: true, + }; + } + + if (status === 'success') { + return { + children: 'Close', + intent: Intent.Success, + onClick: () => setDialogOpen(false), + }; + } + }; + + // TODO: Add when network parameters are updated + if ( + currentStakeAvailable === BigInt(0) || + currentStakeAvailable < requiredStake + ) { + return ( +
+

+ You need at least{' '} + {addDecimalsFormatNumber(requiredStake.toString(), 18)} VEGA staked to + generate a referral code and participate in the referral program. +

+ + Stake some $VEGA now + +
+ ); + } + + return ( +
+ {(status === 'idle' || status === 'loading' || status === 'error') && ( +

+ Generate a referral code to share with your friends and start earning + commission. +

+ )} + {status === 'success' && code && ( +
+
+

+ {code} +

+
+ + } + > + Copy + + +
+ )} + + {err && {err}} + {/* TODO: Add links */} +
+ About the referral program + Disclaimer +
+
+ ); +}; diff --git a/apps/trading/client-pages/referrals/error-boundary.tsx b/apps/trading/client-pages/referrals/error-boundary.tsx new file mode 100644 index 000000000..0a45df843 --- /dev/null +++ b/apps/trading/client-pages/referrals/error-boundary.tsx @@ -0,0 +1,77 @@ +import { isRouteErrorResponse, useNavigate, useRouteError } from 'react-router'; +import { RainbowButton } from './buttons'; +import { AnimatedDudeWithWire } from './graphics/dude'; +import { LayoutWithSky } from './layout'; +import { Routes } from '../../lib/links'; + +export const ErrorBoundary = () => { + const error = useRouteError(); + const navigate = useNavigate(); + + const title = isRouteErrorResponse(error) + ? `${error.status} ${error.statusText}` + : 'Something went wrong'; + + const code = isRouteErrorResponse(error) ? error.status : 0; + + const messages: Record = { + 0: 'An unknown error occurred.', + 404: "The page you're looking for doesn't exists.", + }; + + return ( + +
+ +
+

{title}

+ + {Object.keys(messages).includes(code.toString()) ? ( +

{messages[code]}

+ ) : null} + +

+ navigate('..')} + variant="border" + className="text-xs" + > + Go back and try again + +

+
+ ); +}; + +export const NotFound = () => { + const navigate = useNavigate(); + + return ( +
+
+ +
+

{'Not found'}

+ +

+ {"The page you're looking for doesn't exists."} +

+ +

+ navigate(Routes.REFERRALS)} + variant="border" + className="text-xs" + > + Go back and try again + +

+
+ ); +}; diff --git a/apps/trading/client-pages/referrals/graphics/dude.tsx b/apps/trading/client-pages/referrals/graphics/dude.tsx new file mode 100644 index 000000000..2884f8b02 --- /dev/null +++ b/apps/trading/client-pages/referrals/graphics/dude.tsx @@ -0,0 +1,70 @@ +import classNames from 'classnames'; +import type { HTMLAttributes } from 'react'; + +export const Dude = ({ className }: HTMLAttributes) => { + return ( + + + + + + + + + ); +}; + +export const Wire = ({ className }: HTMLAttributes) => { + return ( + + + + ); +}; + +export const AnimatedDudeWithWire = ({ className }: { className?: string }) => ( +
+ + +
+); diff --git a/apps/trading/client-pages/referrals/hooks/use-referral-program.ts b/apps/trading/client-pages/referrals/hooks/use-referral-program.ts new file mode 100644 index 000000000..2ac61a6f4 --- /dev/null +++ b/apps/trading/client-pages/referrals/hooks/use-referral-program.ts @@ -0,0 +1,133 @@ +import { gql, useQuery } from '@apollo/client'; +import { getNumberFormat } from '@vegaprotocol/utils'; +import { addDays } from 'date-fns'; +import sortBy from 'lodash/sortBy'; +import omit from 'lodash/omit'; + +// TODO: Generate query +// eslint-disable-next-line +const REFERRAL_PROGRAM_QUERY = gql` + query ReferralProgram { + currentReferralProgram { + id + version + endOfProgramTimestamp + windowLength + endedAt + benefitTiers { + minimumEpochs + minimumRunningNotionalTakerVolume + referralDiscountFactor + referralRewardFactor + } + stakingTiers { + minimumStakedTokens + referralRewardMultiplier + } + } + } +`; + +const STAKING_TIERS_MAPPING: Record = { + 1: 'Tradestarter', + 2: 'Mid level degen', + 3: 'Reward hoarder', +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const MOCK = { + data: { + currentReferralProgram: { + id: 'abc', + version: 1, + endOfProgramTimestamp: addDays(new Date(), 10).toISOString(), + windowLength: 10, + benefitTiers: [ + { + minimumEpochs: 5, + minimumRunningNotionalTakerVolume: '30000', + referralDiscountFactor: '0.01', + referralRewardFactor: '0.01', + }, + { + minimumEpochs: 5, + minimumRunningNotionalTakerVolume: '20000', + referralDiscountFactor: '0.05', + referralRewardFactor: '0.05', + }, + { + minimumEpochs: 5, + minimumRunningNotionalTakerVolume: '10000', + referralDiscountFactor: '0.001', + referralRewardFactor: '0.001', + }, + ], + stakingTiers: [ + { + minimumStakedTokens: '10000', + referralRewardMultiplier: '1', + }, + { + minimumStakedTokens: '20000', + referralRewardMultiplier: '2', + }, + { + minimumStakedTokens: '30000', + referralRewardMultiplier: '3', + }, + ], + }, + }, + loading: false, + error: undefined, +}; + +export const useReferralProgram = () => { + const { data, loading, error } = useQuery(REFERRAL_PROGRAM_QUERY, { + fetchPolicy: 'cache-and-network', + }); + + if (!data) { + return { + benefitTiers: [], + stakingTiers: [], + details: undefined, + loading, + error, + }; + } + + const benefitTiers = sortBy(data.currentReferralProgram.benefitTiers, (t) => + Number(t.referralRewardFactor) + ) + .reverse() + .map((t, i) => { + return { + tier: i + 1, + commission: Number(t.referralRewardFactor) * 100 + '%', + discount: Number(t.referralDiscountFactor) * 100 + '%', + volume: getNumberFormat(0).format( + Number(t.minimumRunningNotionalTakerVolume) + ), + }; + }); + + const stakingTiers = sortBy( + data.currentReferralProgram.stakingTiers, + (t) => t.referralRewardMultiplier + ).map((t, i) => { + return { + tier: i + 1, + label: STAKING_TIERS_MAPPING[i + 1], + ...t, + }; + }); + + return { + benefitTiers, + stakingTiers, + details: omit(data.currentReferralProgram, 'benefitTiers', 'stakingTiers'), + loading, + error, + }; +}; diff --git a/apps/trading/client-pages/referrals/hooks/use-referral.ts b/apps/trading/client-pages/referrals/hooks/use-referral.ts new file mode 100644 index 000000000..2c6b89f97 --- /dev/null +++ b/apps/trading/client-pages/referrals/hooks/use-referral.ts @@ -0,0 +1,114 @@ +import { gql, useQuery } from '@apollo/client'; +import { removePaginationWrapper } from '@vegaprotocol/utils'; + +const REFERRER_QUERY = gql` + query ReferralSets($partyId: ID!) { + referralSets(referrer: $partyId) { + edges { + node { + id + referrer + createdAt + updatedAt + } + } + } + } +`; + +const REFEREE_QUERY = gql` + query ReferralSets($partyId: ID!) { + referralSets(referee: $partyId) { + edges { + node { + id + referrer + createdAt + updatedAt + } + } + } + } +`; + +const REFEREES_QUERY = gql` + query ReferralSets($code: ID!) { + referralSetReferees(id: $code) { + edges { + node { + referralSetId + refereeId + joinedAt + atEpoch + } + } + } + } +`; + +// TODO: generate types after perps work is merged +export type ReferralData = { + code: string; + referees: Array<{ + refereeId: string; + joinedAt: string; + atEpoch: number; + }>; +}; + +export const useReferral = ( + pubKey: string | null, + role: 'referrer' | 'referee' +) => { + const query = { + referrer: REFERRER_QUERY, + referee: REFEREE_QUERY, + }; + + const { + data: referralData, + loading: referralLoading, + error: referralError, + } = useQuery(query[role], { + variables: { + partyId: pubKey, + }, + skip: !pubKey, + fetchPolicy: 'cache-and-network', + }); + + // A user can only have 1 active referral program at a time + const referral = referralData?.referralSets.edges.length + ? referralData.referralSets.edges[0].node + : undefined; + + const { + data: refereesData, + loading: refereesLoading, + error: refereesError, + } = useQuery(REFEREES_QUERY, { + variables: { + code: referral?.id, + }, + skip: !referral?.id, + fetchPolicy: 'cache-and-network', + }); + + const referees = removePaginationWrapper( + refereesData?.referralSetReferees.edges + ); + + const data = + referral && refereesData + ? { + code: referral.id, + referees, + } + : undefined; + + return { + data: data as ReferralData | undefined, + loading: referralLoading || refereesLoading, + error: referralError || refereesError, + }; +}; diff --git a/apps/trading/client-pages/referrals/hooks/use-stake-available.ts b/apps/trading/client-pages/referrals/hooks/use-stake-available.ts new file mode 100644 index 000000000..af0b8836c --- /dev/null +++ b/apps/trading/client-pages/referrals/hooks/use-stake-available.ts @@ -0,0 +1,34 @@ +import { gql, useQuery } from '@apollo/client'; +import { useVegaWallet } from '@vegaprotocol/wallet'; + +const STAKE_QUERY = gql` + query CreateCode($partyId: ID!) { + party(id: $partyId) { + stakingSummary { + currentStakeAvailable + } + } + networkParameter(key: "referralProgram.minStakedVegaTokens") { + value + } + } +`; + +export const useStakeAvailable = () => { + const { pubKey } = useVegaWallet(); + const { data } = useQuery(STAKE_QUERY, { + variables: { partyId: pubKey || '' }, + skip: !pubKey, + // TODO: remove when network params available + errorPolicy: 'ignore', + }); + + return { + stakeAvailable: data + ? BigInt(data.party?.stakingSummary.currentStakeAvailable || '0') + : undefined, + requiredStake: data + ? BigInt(data.networkParameter?.value || '0') + : undefined, + }; +}; diff --git a/apps/trading/client-pages/referrals/how-it-works-table.tsx b/apps/trading/client-pages/referrals/how-it-works-table.tsx new file mode 100644 index 000000000..453ee09b7 --- /dev/null +++ b/apps/trading/client-pages/referrals/how-it-works-table.tsx @@ -0,0 +1,52 @@ +import { Table } from './table'; + +export const HowItWorksTable = () => ( + + 1 + + ), + step: 'Referrers generate a code assigned to their key via an on chain transaction', + }, + { + number: ( + + 2 + + ), + step: 'Anyone with the referral link can apply it to their key(s) of choice via an on chain transaction', + }, + { + number: ( + + 3 + + ), + step: 'Discounts are applied automatically during trading based on the key(s) used', + }, + { + number: ( + + 4 + + ), + step: 'Referrers earn commission based on a percentage of the taker fees their referees pay', + }, + { + number: ( + + 5 + + ), + step: 'The commission is taken from the infrastructure fee, maker fee, and liquidity provider fee, not from the referee', + }, + ]} + >
+); diff --git a/apps/trading/client-pages/referrals/landing-banner.tsx b/apps/trading/client-pages/referrals/landing-banner.tsx new file mode 100644 index 000000000..56331b34e --- /dev/null +++ b/apps/trading/client-pages/referrals/landing-banner.tsx @@ -0,0 +1,31 @@ +import classNames from 'classnames'; +import { AnimatedDudeWithWire } from './graphics/dude'; + +export const LandingBanner = () => { + return ( +
+
+
+ +
+
+

+ Earn commission & stake rewards +

+

+ Invite friends and earn commission in the form of Vega rewards from + the trading fees they pay. Stake those rewards to earn multipliers + on future rewards. +

+

+ Any friends that join using the code will receive discounts off + trading fees. +

+
+
+
+ ); +}; diff --git a/apps/trading/client-pages/referrals/layout.tsx b/apps/trading/client-pages/referrals/layout.tsx new file mode 100644 index 000000000..8db550870 --- /dev/null +++ b/apps/trading/client-pages/referrals/layout.tsx @@ -0,0 +1,35 @@ +import classNames from 'classnames'; +import type { HTMLAttributes } from 'react'; +import { SKY_BACKGROUND } from './constants'; +import { Outlet } from 'react-router-dom'; + +export const Layout = ({ + className, + children, + ...props +}: HTMLAttributes) => { + return ( +
+ {children || } +
+ ); +}; + +export const LayoutWithSky = ({ + className, + ...props +}: HTMLAttributes) => { + return ( +
+ +
+ ); +}; diff --git a/apps/trading/client-pages/referrals/referral-statistics.tsx b/apps/trading/client-pages/referrals/referral-statistics.tsx new file mode 100644 index 000000000..ed250b9e6 --- /dev/null +++ b/apps/trading/client-pages/referrals/referral-statistics.tsx @@ -0,0 +1,113 @@ +import { Tile } from './tile'; +import { + CopyWithTooltip, + Input, + VegaIcon, + VegaIconNames, +} from '@vegaprotocol/ui-toolkit'; +import { Button, RainbowButton } from './buttons'; + +import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet'; +import type { ReferralData } from './hooks/use-referral'; +import { useReferral } from './hooks/use-referral'; +import { CreateCodeContainer } from './create-code-form'; +import classNames from 'classnames'; + +const CodeTile = ({ + code, + as, +}: { + code: string; + as: 'referrer' | 'referee'; +}) => { + return ( + +

Your referral code

+ {as === 'referrer' && ( +

+ Share this code with friends +

+ )} +
+ + + + +
+
+ ); +}; + +export const ReferralStatistics = () => { + const openWalletDialog = useVegaWalletDialogStore( + (store) => store.openVegaWalletDialog + ); + const { pubKey } = useVegaWallet(); + + const { data: referee } = useReferral(pubKey, 'referee'); + const { data: referrer } = useReferral(pubKey, 'referrer'); + + if (!pubKey) { + return ( +
+ openWalletDialog()}> + Connect wallet + +
+ ); + } + if (referee?.code) { + return ; + } + + if (referrer?.code) { + return ; + } + + return ; +}; + +const Statistics = ({ + data, + as, +}: { + data: ReferralData; + as: 'referrer' | 'referee'; +}) => { + return ( +
+
+ {as === 'referrer' && data?.referees && ( + +
+

+ {data.referees.length} +

+

+ {data.referees.length === 1 + ? 'Trader referred' + : 'Total traders referred'} +

+
+
+ )} + +
+
+ ); +}; diff --git a/apps/trading/client-pages/referrals/referrals.tsx b/apps/trading/client-pages/referrals/referrals.tsx new file mode 100644 index 000000000..590e6fb60 --- /dev/null +++ b/apps/trading/client-pages/referrals/referrals.tsx @@ -0,0 +1,50 @@ +import { + TradingAnchorButton, + VegaIcon, + VegaIconNames, +} from '@vegaprotocol/ui-toolkit'; +import { HowItWorksTable } from './how-it-works-table'; +import { LandingBanner } from './landing-banner'; +import { TiersContainer } from './tiers'; +import { RainbowTabLink } from './buttons'; +import { Outlet } from 'react-router-dom'; +import { Routes } from '../../lib/links'; + +export const Referrals = () => { + return ( + <> + +
+
+ + Your referrals + + + Apply a code + +
+
+ +
+
+ + + +
+

How it works

+
+
+ +
+ + Read the terms + +
+
+ + ); +}; diff --git a/apps/trading/client-pages/referrals/table.tsx b/apps/trading/client-pages/referrals/table.tsx new file mode 100644 index 000000000..855ba0521 --- /dev/null +++ b/apps/trading/client-pages/referrals/table.tsx @@ -0,0 +1,116 @@ +import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit'; +import classNames from 'classnames'; +import type { HTMLAttributes } from 'react'; +import { BORDER_COLOR, GRADIENT } from './constants'; + +type TableColumnDefinition = { + displayName?: string; + name: string; + tooltip?: string; + className?: string; +}; + +type TableProps = { + columns: TableColumnDefinition[]; + data: Record[]; + noHeader?: boolean; + noCollapse?: boolean; +}; + +const INNER_BORDER_STYLE = `border-b ${BORDER_COLOR}`; + +export const Table = ({ + columns, + data, + noHeader = false, + noCollapse = false, + className, + ...props +}: TableProps & HTMLAttributes) => { + const header = ( + + + {columns.map(({ displayName, name, tooltip }) => ( + + + {displayName} + {tooltip ? ( + + + + ) : null} + + + ))} + + + ); + return ( + + {!noHeader && header} + + {data.map((d, i) => ( + + {columns.map(({ name, displayName, className }, j) => ( + + ))} + + ))} + +
+ {/** display column name in mobile view */} + {!noCollapse && + !noHeader && + displayName && + displayName.length > 0 && ( + + {displayName} + + )} + {d[name]} +
+ ); +}; diff --git a/apps/trading/client-pages/referrals/tag.tsx b/apps/trading/client-pages/referrals/tag.tsx new file mode 100644 index 000000000..3fd1ba712 --- /dev/null +++ b/apps/trading/client-pages/referrals/tag.tsx @@ -0,0 +1,32 @@ +import type { HTMLAttributes } from 'react'; +import classNames from 'classnames'; + +type TagProps = { + color?: 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'orange' | 'none'; +}; +export const Tag = ({ + color = 'none', + children, + className, + ...props +}: TagProps & HTMLAttributes) => ( +
+ {children} +
+); diff --git a/apps/trading/client-pages/referrals/tiers.tsx b/apps/trading/client-pages/referrals/tiers.tsx new file mode 100644 index 000000000..50e3f46d4 --- /dev/null +++ b/apps/trading/client-pages/referrals/tiers.tsx @@ -0,0 +1,172 @@ +import { getDateTimeFormat } from '@vegaprotocol/utils'; +import { useReferralProgram } from './hooks/use-referral-program'; +import { Table } from './table'; +import classNames from 'classnames'; +import { BORDER_COLOR, GRADIENT } from './constants'; +import { Tag } from './tag'; +import type { ComponentProps } from 'react'; + +const Loading = ({ variant }: { variant: 'large' | 'inline' }) => ( +
+); + +const StakingTier = ({ + tier, + label, + referralRewardMultiplier, + minimumStakedTokens, +}: { + tier: number; + label: string; + referralRewardMultiplier: string; + minimumStakedTokens: string; +}) => { + const color: Record['color']> = { + 1: 'green', + 2: 'blue', + 3: 'pink', + }; + return ( +
+
+ {tier < 4 && ( + // eslint-disable-next-line @next/next/no-img-element + {`${referralRewardMultiplier}x + )} +
+
+

{label}

+

+ Stake a minimum of {minimumStakedTokens} $VEGA tokens +

+ + Reward multiplier {referralRewardMultiplier}x + +
+
+ ); +}; + +export const TiersContainer = () => { + const { benefitTiers, stakingTiers, details, loading } = useReferralProgram(); + + const ends = details?.endOfProgramTimestamp + ? getDateTimeFormat().format(new Date(details.endOfProgramTimestamp)) + : undefined; + + return ( + <> +
+

Referral tiers

+ {ends && ( + + + Program ends: + {' '} + {ends} + + )} +
+
+ {loading || !benefitTiers || benefitTiers.length === 0 ? ( + + ) : ( + + )} +
+ +
+

Staking multipliers

+
+
+ {loading || !stakingTiers || stakingTiers.length === 0 ? ( + <> + + + + + ) : ( + + )} +
+ + ); +}; + +const StakingTiers = ({ + data, +}: { + data: ReturnType['stakingTiers']; +}) => ( + <> + {data.map( + ({ tier, label, referralRewardMultiplier, minimumStakedTokens }, i) => ( + + ) + )} + +); + +const TiersTable = ({ + data, +}: { + data: Array<{ + tier: number; + commission: string; + discount: string; + volume: string; + }>; +}) => { + return ( + ({ + ...d, + className: classNames({ + 'from-vega-pink-400 dark:from-vega-pink-600 to-20% bg-highlight': + d.tier === 1, + 'from-vega-purple-400 dark:from-vega-purple-600 to-20% bg-highlight': + d.tier === 2, + 'from-vega-blue-400 dark:from-vega-blue-600 to-20% bg-highlight': + d.tier === 3, + 'from-vega-orange-400 dark:from-vega-orange-600 to-20% bg-highlight': + d.tier > 3, + }), + }))} + /> + ); +}; diff --git a/apps/trading/client-pages/referrals/tile.tsx b/apps/trading/client-pages/referrals/tile.tsx new file mode 100644 index 000000000..e329dc0d0 --- /dev/null +++ b/apps/trading/client-pages/referrals/tile.tsx @@ -0,0 +1,39 @@ +import classNames from 'classnames'; +import type { HTMLAttributes } from 'react'; +import { BORDER_COLOR, GRADIENT } from './constants'; + +type TileProps = { + variant?: 'rainbow' | 'default'; +}; + +export const Tile = ({ + variant = 'default', + className, + children, +}: TileProps & HTMLAttributes) => { + return ( +
+
+ {children} +
+
+ ); +}; diff --git a/apps/trading/components/navbar/navbar.spec.tsx b/apps/trading/components/navbar/navbar.spec.tsx index 941f8f140..de12f8db2 100644 --- a/apps/trading/components/navbar/navbar.spec.tsx +++ b/apps/trading/components/navbar/navbar.spec.tsx @@ -5,6 +5,7 @@ import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet'; import { Navbar } from './navbar'; import { useGlobalStore } from '../../stores'; +import { ENV, FLAGS } from '@vegaprotocol/environment'; jest.mock('@vegaprotocol/proposals', () => ({ ProtocolUpgradeCountdown: () => null, @@ -47,6 +48,14 @@ describe('Navbar', () => { beforeAll(() => { useGlobalStore.setState({ marketId }); + const mockedFLAGS = jest.mocked(FLAGS); + mockedFLAGS.REFERRALS = true; + const mockedENV = jest.mocked(ENV); + mockedENV.VEGA_TOKEN_URL = 'governance'; + }); + + afterAll(() => { + jest.clearAllMocks(); }); it('should be properly rendered', () => { @@ -57,6 +66,7 @@ describe('Navbar', () => { ['/markets/all', 'Markets'], [`/markets/${marketId}`, 'Trading'], ['/portfolio', 'Portfolio'], + ['/referrals', 'Referrals'], [expect.stringContaining('governance'), 'Governance'], ]; @@ -89,6 +99,7 @@ describe('Navbar', () => { ['/markets/all', 'Markets'], [`/markets/${marketId}`, 'Trading'], ['/portfolio', 'Portfolio'], + ['/referrals', 'Referrals'], [expect.stringContaining('governance'), 'Governance'], ]; const links = menu.getAllByRole('link'); diff --git a/apps/trading/components/navbar/navbar.tsx b/apps/trading/components/navbar/navbar.tsx index e2b8b05cc..c49009b81 100644 --- a/apps/trading/components/navbar/navbar.tsx +++ b/apps/trading/components/navbar/navbar.tsx @@ -6,6 +6,7 @@ import { Networks, DApp, useLinks, + FLAGS, } from '@vegaprotocol/environment'; import { t } from '@vegaprotocol/i18n'; import { useGlobalStore } from '../../stores'; @@ -140,10 +141,6 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => { const { VEGA_ENV, VEGA_NETWORKS, GITHUB_FEEDBACK_URL } = useEnvironment(); const marketId = useGlobalStore((store) => store.marketId); - // If we have a stored marketId make Trade link go to that market - // otherwise always go to /markets/all - const tradingPath = marketId ? Links.MARKET(marketId) : Links.MARKETS(); - return (
@@ -174,7 +171,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => { - + {t('Trading')} @@ -183,6 +180,13 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => { {t('Portfolio')} + {FLAGS.REFERRALS && ( + + + {t('Referrals')} + + + )} {t('Governance')} diff --git a/apps/trading/lib/links.ts b/apps/trading/lib/links.ts index 283347b90..2439b99ef 100644 --- a/apps/trading/lib/links.ts +++ b/apps/trading/lib/links.ts @@ -13,6 +13,10 @@ export const Routes = { DEPOSIT: '/portfolio/assets/deposit', WITHDRAW: '/portfolio/assets/withdraw', TRANSFER: '/portfolio/assets/transfer', + REFERRALS: '/referrals', + REFERRALS_APPLY_CODE: '/referrals/apply-code', + REFERRALS_CREATE_CODE: '/referrals/create-code', + TEAMS: '/teams', } as const; type ConsoleLinks = { @@ -32,4 +36,8 @@ export const Links: ConsoleLinks = { DEPOSIT: () => Routes.DEPOSIT, WITHDRAW: () => Routes.WITHDRAW, TRANSFER: () => Routes.TRANSFER, + REFERRALS: () => Routes.REFERRALS, + REFERRALS_APPLY_CODE: () => Routes.REFERRALS_APPLY_CODE, + REFERRALS_CREATE_CODE: () => Routes.REFERRALS_CREATE_CODE, + TEAMS: () => Routes.TEAMS, }; diff --git a/apps/trading/pages/client-router.tsx b/apps/trading/pages/client-router.tsx index 880ef8732..be49f038f 100644 --- a/apps/trading/pages/client-router.tsx +++ b/apps/trading/pages/client-router.tsx @@ -14,6 +14,14 @@ import { Deposit } from '../client-pages/deposit'; import { Withdraw } from '../client-pages/withdraw'; import { Transfer } from '../client-pages/transfer'; import { Routes } from '../lib/links'; +import { LayoutWithSky } from '../client-pages/referrals/layout'; +import { Referrals } from '../client-pages/referrals/referrals'; +import { ReferralStatistics } from '../client-pages/referrals/referral-statistics'; +import { ApplyCodeForm } from '../client-pages/referrals/apply-code-form'; +import { CreateCodeContainer } from '../client-pages/referrals/create-code-form'; +import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-boundary'; +import { compact } from 'lodash'; +import { FLAGS } from '@vegaprotocol/environment'; // These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM // Using dynamic imports is a workaround for this until pennant is published as ESM @@ -26,8 +34,8 @@ const NotFound = () => ( ); -export const routerConfig: RouteObject[] = [ - // Pages that dont use the LayoutWithSidebar must come first +export const routerConfig: RouteObject[] = compact([ + // Pages that don't use the LayoutWithSidebar must come first // to ensure they are matched before the catch all route '/*' { path: 'disclaimer', @@ -35,7 +43,36 @@ export const routerConfig: RouteObject[] = [ id: Routes.DISCLAIMER, children: [{ index: true, element: }], }, - + // Referrals routing (the pages should be available if the feature flag is on) + FLAGS.REFERRALS + ? { + path: Routes.REFERRALS, + element: , + children: [ + { + element: , + children: [ + { + index: true, + element: , + }, + { + path: Routes.REFERRALS_CREATE_CODE, + element: , + }, + { + path: Routes.REFERRALS_APPLY_CODE, + element: , + }, + ], + }, + { + path: '*', + element: , + }, + ], + } + : undefined, // All other pages will use the sidebar { path: '/*', @@ -93,7 +130,6 @@ export const routerConfig: RouteObject[] = [ element: , id: Routes.LIQUIDITY, }, - // NotFound page is here so its caught within parent '/*' route { path: '*', @@ -101,7 +137,7 @@ export const routerConfig: RouteObject[] = [ }, ], }, -]; +]); export const ClientRouter = () => { const routes = useRoutes(routerConfig); diff --git a/apps/trading/public/1x.png b/apps/trading/public/1x.png new file mode 100644 index 000000000..ca3fb8391 Binary files /dev/null and b/apps/trading/public/1x.png differ diff --git a/apps/trading/public/2x.png b/apps/trading/public/2x.png new file mode 100644 index 000000000..8e883d974 Binary files /dev/null and b/apps/trading/public/2x.png differ diff --git a/apps/trading/public/3x.png b/apps/trading/public/3x.png new file mode 100644 index 000000000..d13ebde3c Binary files /dev/null and b/apps/trading/public/3x.png differ diff --git a/apps/trading/public/sky-dark.png b/apps/trading/public/sky-dark.png new file mode 100644 index 000000000..7c7d6e322 Binary files /dev/null and b/apps/trading/public/sky-dark.png differ diff --git a/apps/trading/public/sky-light.png b/apps/trading/public/sky-light.png new file mode 100644 index 000000000..4b1923486 Binary files /dev/null and b/apps/trading/public/sky-light.png differ diff --git a/apps/trading/tailwind.config.js b/apps/trading/tailwind.config.js index c903e5890..c095846c7 100644 --- a/apps/trading/tailwind.config.js +++ b/apps/trading/tailwind.config.js @@ -13,7 +13,37 @@ module.exports = { ], darkMode: 'class', theme: { - extend: theme, + extend: { + ...theme, + colors: { + transparent: 'transparent', + current: 'currentColor', + ...theme.colors, + }, + backgroundImage: { + ...theme.backgroundImage, + rainbow: + 'linear-gradient(103.47deg, #FF077F 1.68%, #8028FF 47.49%, #0075FF 100%)', + 'rainbow-shifted': + 'linear-gradient(103.47deg, #0075FF 1.68%, #8028FF 47.49%, #FF077F 100%)', + highlight: + 'linear-gradient(170deg, var(--tw-gradient-from), transparent var(--tw-gradient-to-position))', + }, + keyframes: { + ...theme.keyframes, + shake: { + '0%': { transform: 'translateX(0)' }, + '25%': { transform: 'translateX(5px)' }, + '50%': { transform: 'translateX(-5px)' }, + '75%': { transform: 'translateX(5px)' }, + '100%': { transform: 'translateX(0)' }, + }, + }, + animation: { + ...theme.animation, + shake: 'shake 200ms linear', + }, + }, }, plugins: [vegaCustomClasses], }; diff --git a/libs/environment/src/hooks/use-environment.ts b/libs/environment/src/hooks/use-environment.ts index deb5b4b2b..b7ad4b9ec 100644 --- a/libs/environment/src/hooks/use-environment.ts +++ b/libs/environment/src/hooks/use-environment.ts @@ -414,6 +414,9 @@ function compileFeatureFlags(): FeatureFlags { process.env['NX_METAMASK_SNAPS'] ) as string ), + REFERRALS: TRUTHY.includes( + windowOrDefault('NX_REFERRALS', process.env['NX_REFERRALS']) as string + ), }; const EXPLORER_FLAGS = { EXPLORER_ASSETS: TRUTHY.includes( diff --git a/libs/environment/src/hooks/use-links.ts b/libs/environment/src/hooks/use-links.ts index 0c76a3056..c9ea45195 100644 --- a/libs/environment/src/hooks/use-links.ts +++ b/libs/environment/src/hooks/use-links.ts @@ -1,7 +1,7 @@ import trim from 'lodash/trim'; import { useCallback } from 'react'; import { Networks } from '../types'; -import { useEnvironment } from './use-environment'; +import { ENV, useEnvironment } from './use-environment'; import { stripFullStops } from '@vegaprotocol/utils'; const VEGA_DOCS_URL = @@ -84,8 +84,7 @@ export const DocsLinks = VEGA_DOCS_URL : undefined; export const useLinks = (dapp: DApp, network?: Net) => { - const { VEGA_ENV, VEGA_EXPLORER_URL, VEGA_TOKEN_URL, VEGA_CONSOLE_URL } = - useEnvironment(); + const { VEGA_ENV, VEGA_EXPLORER_URL, VEGA_TOKEN_URL, VEGA_CONSOLE_URL } = ENV; const fallback = { [DApp.Explorer]: VEGA_EXPLORER_URL, [DApp.Governance]: VEGA_TOKEN_URL, @@ -175,4 +174,5 @@ export const ExternalLinks = { export const TokenStaticLinks = { PROPOSAL_PAGE: ':tokenUrl/proposals/:proposalId', UPDATE_PROPOSAL_PAGE: ':tokenUrl/proposals/propose/update-market', + ASSOCIATE: 'token/associate', }; diff --git a/libs/environment/src/types.ts b/libs/environment/src/types.ts index b1b971888..345871484 100644 --- a/libs/environment/src/types.ts +++ b/libs/environment/src/types.ts @@ -23,6 +23,7 @@ export type CosmicElevatorFlags = Pick< | 'SUCCESSOR_MARKETS' | 'PRODUCT_PERPETUALS' | 'METAMASK_SNAPS' + | 'REFERRALS' >; export type Configuration = z.infer; export const CUSTOM_NODE_KEY = 'custom' as const; diff --git a/libs/environment/src/utils/validate-environment.ts b/libs/environment/src/utils/validate-environment.ts index c0589e73f..5079869ad 100644 --- a/libs/environment/src/utils/validate-environment.ts +++ b/libs/environment/src/utils/validate-environment.ts @@ -78,6 +78,7 @@ const COSMIC_ELEVATOR_FLAGS = { ICEBERG_ORDERS: z.optional(z.boolean()), PRODUCT_PERPETUALS: z.optional(z.boolean()), METAMASK_SNAPS: z.optional(z.boolean()), + REFERRALS: z.optional(z.boolean()), }; const EXPLORER_FLAGS = { diff --git a/libs/wallet/src/connectors/vega-connector.ts b/libs/wallet/src/connectors/vega-connector.ts index 2ee88e2fd..79c46d684 100644 --- a/libs/wallet/src/connectors/vega-connector.ts +++ b/libs/wallet/src/connectors/vega-connector.ts @@ -395,6 +395,24 @@ export interface TransferBody { transfer: Transfer; } +export type ApplyReferralCode = { + applyReferralCode: { + id: string; + }; +}; + +export type CreateReferralSet = { + createReferralSet: { + isTeam: boolean; + team?: { + name: string; + teamUrl?: string; + avatarUrl?: string; + closed: boolean; + }; + }; +}; + export type Transaction = | StopOrdersSubmissionBody | StopOrdersCancellationBody @@ -408,7 +426,9 @@ export type Transaction = | ProposalSubmissionBody | BatchMarketInstructionSubmissionBody | TransferBody - | LiquidityProvisionSubmission; + | LiquidityProvisionSubmission + | ApplyReferralCode + | CreateReferralSet; export const isWithdrawTransaction = ( transaction: Transaction diff --git a/package.json b/package.json index c4a86e549..de47dd707 100644 --- a/package.json +++ b/package.json @@ -213,7 +213,7 @@ "style-loader": "^3.3.0", "stylus": "^0.55.0", "stylus-loader": "^7.1.0", - "tailwindcss": "3.2.7", + "tailwindcss": "3.3.3", "ts-jest": "29.1.0", "ts-node": "10.9.1", "tslib": "^2.3.0", diff --git a/yarn.lock b/yarn.lock index c16018fa1..2d60b3f4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd" integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g== +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + "@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" @@ -9856,16 +9861,7 @@ acorn-jsx@^5.3.1, acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-node@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - -acorn-walk@^7.0.0, acorn-walk@^7.2.0: +acorn-walk@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== @@ -9875,7 +9871,7 @@ acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.0.0, acorn@^7.4.1: +acorn@^7.4.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -10126,6 +10122,11 @@ any-observable@^0.3.0: resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -13199,11 +13200,6 @@ define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ== - defu@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.2.tgz#1217cba167410a1765ba93893c6dbac9ed9d9e5c" @@ -13315,15 +13311,6 @@ detect-port@^1.3.0, detect-port@^1.5.1: address "^1.0.1" debug "4" -detective@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" - integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== - dependencies: - acorn-node "^1.8.2" - defined "^1.0.0" - minimist "^1.2.6" - didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -15557,6 +15544,18 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@7.1.7, glob@~7.1.1: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -16628,6 +16627,13 @@ is-core-module@^2.11.0: dependencies: has "^1.0.3" +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + is-core-module@^2.5.0, is-core-module@^2.8.1: version "2.10.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" @@ -17670,6 +17676,11 @@ jest@29.4.3: import-local "^3.0.2" jest-cli "^29.4.3" +jiti@^1.18.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42" + integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA== + js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -18115,7 +18126,7 @@ lilconfig@^2.0.3, lilconfig@^2.1.0: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -lilconfig@^2.0.5, lilconfig@^2.0.6: +lilconfig@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== @@ -19505,6 +19516,15 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -19857,7 +19877,7 @@ nx@16.4.0: "@nx/nx-win32-arm64-msvc" "16.4.0" "@nx/nx-win32-x64-msvc" "16.4.0" -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -20597,6 +20617,11 @@ pino@7.11.0: sonic-boom "^2.2.1" thread-stream "^0.15.1" +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -20746,7 +20771,16 @@ postcss-discard-overridden@^6.0.0: resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz#49c5262db14e975e349692d9024442de7cd8e234" integrity sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw== -postcss-import@^14.1.0, postcss-import@~14.1.0: +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-import@~14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== @@ -20755,14 +20789,14 @@ postcss-import@^14.1.0, postcss-import@~14.1.0: read-cache "^1.0.0" resolve "^1.1.7" -postcss-js@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00" - integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ== +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== dependencies: camelcase-css "^2.0.1" -postcss-load-config@^3.0.0, postcss-load-config@^3.1.4: +postcss-load-config@^3.0.0: version "3.1.4" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== @@ -20770,6 +20804,14 @@ postcss-load-config@^3.0.0, postcss-load-config@^3.1.4: lilconfig "^2.0.5" yaml "^1.10.2" +postcss-load-config@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" + integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== + dependencies: + lilconfig "^2.0.5" + yaml "^2.1.1" + postcss-loader@^6.1.1: version "6.2.1" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" @@ -20930,12 +20972,12 @@ postcss-modules@^4.0.0: postcss-modules-values "^4.0.0" string-hash "^1.1.1" -postcss-nested@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.0.tgz#1572f1984736578f360cffc7eb7dca69e30d1735" - integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== dependencies: - postcss-selector-parser "^6.0.10" + postcss-selector-parser "^6.0.11" postcss-normalize-charset@^5.1.0: version "5.1.0" @@ -21108,14 +21150,6 @@ postcss-reduce-transforms@^6.0.0: dependencies: postcss-value-parser "^4.2.0" -postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: version "6.0.13" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" @@ -21124,6 +21158,14 @@ postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.5, postcss-selecto cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" @@ -21182,7 +21224,7 @@ postcss@8.4.21: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.0.9, postcss@^8.4.14, postcss@^8.4.21, postcss@^8.4.24: +postcss@^8.4.14, postcss@^8.4.21, postcss@^8.4.24: version "8.4.24" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== @@ -21191,6 +21233,15 @@ postcss@^8.0.9, postcss@^8.4.14, postcss@^8.4.21, postcss@^8.4.24: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.23: + version "8.4.29" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" + integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + preact@10.4.1: version "10.4.1" resolved "https://registry.yarnpkg.com/preact/-/preact-10.4.1.tgz#9b3ba020547673a231c6cf16f0fbaef0e8863431" @@ -22360,6 +22411,15 @@ resolve@^1.17.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.2: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3, resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" @@ -23702,6 +23762,19 @@ stylus@^0.59.0: sax "~1.2.4" source-map "^0.7.3" +sucrase@^3.32.0: + version "3.34.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" + integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "7.1.6" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + superstruct@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" @@ -23827,34 +23900,33 @@ table@6.8.0: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.7.tgz#5936dd08c250b05180f0944500c01dce19188c07" - integrity sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ== +tailwindcss@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.3.tgz#90da807393a2859189e48e9e7000e6880a736daf" + integrity sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w== dependencies: + "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" chokidar "^3.5.3" - color-name "^1.1.4" - detective "^5.2.1" didyoumean "^1.2.2" dlv "^1.1.3" fast-glob "^3.2.12" glob-parent "^6.0.2" is-glob "^4.0.3" - lilconfig "^2.0.6" + jiti "^1.18.2" + lilconfig "^2.1.0" micromatch "^4.0.5" normalize-path "^3.0.0" object-hash "^3.0.0" picocolors "^1.0.0" - postcss "^8.0.9" - postcss-import "^14.1.0" - postcss-js "^4.0.0" - postcss-load-config "^3.1.4" - postcss-nested "6.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" postcss-selector-parser "^6.0.11" - postcss-value-parser "^4.2.0" - quick-lru "^5.1.1" - resolve "^1.22.1" + resolve "^1.22.2" + sucrase "^3.32.0" tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" @@ -24017,6 +24089,20 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + thread-stream@^0.15.1: version "0.15.2" resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" @@ -24181,6 +24267,11 @@ ts-dedent@^2.0.0, ts-dedent@^2.2.0: resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + ts-invariant@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" @@ -25399,7 +25490,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -25439,6 +25530,11 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.1.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" + integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"