feat(explorer): proposal transaction view (#3060)

This commit is contained in:
Edd 2023-03-03 14:27:24 +00:00 committed by GitHub
parent acd96fbf21
commit 4542e25ec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1468 additions and 6 deletions

View File

@ -0,0 +1,5 @@
function ReactMarkdown({ children }) {
return <div>{children}</div>;
}
export default ReactMarkdown;

View File

@ -4,13 +4,14 @@ import { ENV } from '../../../config/env';
import Hash from '../hash';
export type ProposalLinkProps = {
id: string;
text?: string;
};
/**
* Given a proposal ID, generates an external link over to
* the Governance page for more information
*/
const ProposalLink = ({ id }: ProposalLinkProps) => {
const ProposalLink = ({ id, text }: ProposalLinkProps) => {
const { data } = useExplorerProposalQuery({
variables: { id },
});
@ -20,7 +21,7 @@ const ProposalLink = ({ id }: ProposalLinkProps) => {
return (
<ExternalLink href={`${base}/proposals/${id}`}>
<Hash text={label} />
{text ? text : <Hash text={label} />}
</ExternalLink>
);
};

View File

@ -0,0 +1,7 @@
query ExplorerProposalStatus($id: ID!) {
proposal(id: $id) {
id
state
rejectionReason
}
}

View File

@ -0,0 +1,19 @@
query ExplorerNewAssetSignatureBundle($id: ID!) {
erc20ListAssetBundle(assetId: $id) {
signatures
nonce
}
asset(id: $id) {
status
}
}
query ExplorerUpdateAssetSignatureBundle($id: ID!) {
erc20SetAssetLimitsBundle(proposalId: $id) {
signatures
nonce
}
asset(id: $id) {
status
}
}

View File

@ -0,0 +1,50 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerProposalStatusQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
export const ExplorerProposalStatusDocument = gql`
query ExplorerProposalStatus($id: ID!) {
proposal(id: $id) {
id
state
rejectionReason
}
}
`;
/**
* __useExplorerProposalStatusQuery__
*
* To run a query within a React component, call `useExplorerProposalStatusQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerProposalStatusQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerProposalStatusQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerProposalStatusQuery(baseOptions: Apollo.QueryHookOptions<ExplorerProposalStatusQuery, ExplorerProposalStatusQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerProposalStatusQuery, ExplorerProposalStatusQueryVariables>(ExplorerProposalStatusDocument, options);
}
export function useExplorerProposalStatusLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerProposalStatusQuery, ExplorerProposalStatusQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerProposalStatusQuery, ExplorerProposalStatusQueryVariables>(ExplorerProposalStatusDocument, options);
}
export type ExplorerProposalStatusQueryHookResult = ReturnType<typeof useExplorerProposalStatusQuery>;
export type ExplorerProposalStatusLazyQueryHookResult = ReturnType<typeof useExplorerProposalStatusLazyQuery>;
export type ExplorerProposalStatusQueryResult = Apollo.QueryResult<ExplorerProposalStatusQuery, ExplorerProposalStatusQueryVariables>;

View File

@ -0,0 +1,98 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerNewAssetSignatureBundleQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerNewAssetSignatureBundleQuery = { __typename?: 'Query', erc20ListAssetBundle?: { __typename?: 'Erc20ListAssetBundle', signatures: string, nonce: string } | null, asset?: { __typename?: 'Asset', status: Types.AssetStatus } | null };
export type ExplorerUpdateAssetSignatureBundleQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerUpdateAssetSignatureBundleQuery = { __typename?: 'Query', erc20SetAssetLimitsBundle: { __typename?: 'ERC20SetAssetLimitsBundle', signatures: string, nonce: string }, asset?: { __typename?: 'Asset', status: Types.AssetStatus } | null };
export const ExplorerNewAssetSignatureBundleDocument = gql`
query ExplorerNewAssetSignatureBundle($id: ID!) {
erc20ListAssetBundle(assetId: $id) {
signatures
nonce
}
asset(id: $id) {
status
}
}
`;
/**
* __useExplorerNewAssetSignatureBundleQuery__
*
* To run a query within a React component, call `useExplorerNewAssetSignatureBundleQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerNewAssetSignatureBundleQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerNewAssetSignatureBundleQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerNewAssetSignatureBundleQuery(baseOptions: Apollo.QueryHookOptions<ExplorerNewAssetSignatureBundleQuery, ExplorerNewAssetSignatureBundleQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerNewAssetSignatureBundleQuery, ExplorerNewAssetSignatureBundleQueryVariables>(ExplorerNewAssetSignatureBundleDocument, options);
}
export function useExplorerNewAssetSignatureBundleLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerNewAssetSignatureBundleQuery, ExplorerNewAssetSignatureBundleQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerNewAssetSignatureBundleQuery, ExplorerNewAssetSignatureBundleQueryVariables>(ExplorerNewAssetSignatureBundleDocument, options);
}
export type ExplorerNewAssetSignatureBundleQueryHookResult = ReturnType<typeof useExplorerNewAssetSignatureBundleQuery>;
export type ExplorerNewAssetSignatureBundleLazyQueryHookResult = ReturnType<typeof useExplorerNewAssetSignatureBundleLazyQuery>;
export type ExplorerNewAssetSignatureBundleQueryResult = Apollo.QueryResult<ExplorerNewAssetSignatureBundleQuery, ExplorerNewAssetSignatureBundleQueryVariables>;
export const ExplorerUpdateAssetSignatureBundleDocument = gql`
query ExplorerUpdateAssetSignatureBundle($id: ID!) {
erc20SetAssetLimitsBundle(proposalId: $id) {
signatures
nonce
}
asset(id: $id) {
status
}
}
`;
/**
* __useExplorerUpdateAssetSignatureBundleQuery__
*
* To run a query within a React component, call `useExplorerUpdateAssetSignatureBundleQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerUpdateAssetSignatureBundleQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerUpdateAssetSignatureBundleQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerUpdateAssetSignatureBundleQuery(baseOptions: Apollo.QueryHookOptions<ExplorerUpdateAssetSignatureBundleQuery, ExplorerUpdateAssetSignatureBundleQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerUpdateAssetSignatureBundleQuery, ExplorerUpdateAssetSignatureBundleQueryVariables>(ExplorerUpdateAssetSignatureBundleDocument, options);
}
export function useExplorerUpdateAssetSignatureBundleLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerUpdateAssetSignatureBundleQuery, ExplorerUpdateAssetSignatureBundleQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerUpdateAssetSignatureBundleQuery, ExplorerUpdateAssetSignatureBundleQueryVariables>(ExplorerUpdateAssetSignatureBundleDocument, options);
}
export type ExplorerUpdateAssetSignatureBundleQueryHookResult = ReturnType<typeof useExplorerUpdateAssetSignatureBundleQuery>;
export type ExplorerUpdateAssetSignatureBundleLazyQueryHookResult = ReturnType<typeof useExplorerUpdateAssetSignatureBundleLazyQuery>;
export type ExplorerUpdateAssetSignatureBundleQueryResult = Apollo.QueryResult<ExplorerUpdateAssetSignatureBundleQuery, ExplorerUpdateAssetSignatureBundleQueryVariables>;

View File

@ -0,0 +1,70 @@
import { t } from '@vegaprotocol/i18n';
import { Lozenge } from '@vegaprotocol/ui-toolkit';
import type { components } from '../../../../../types/explorer';
import type { ExplorerProposalStatusQuery } from './__generated__/Proposal';
import { useExplorerProposalStatusQuery } from './__generated__/Proposal';
type Terms = components['schemas']['vegaProposalTerms'];
export function format(date: string | undefined, def: string) {
if (!date) {
return def;
}
return new Date().toLocaleDateString() || def;
}
export function getDate(
data: ExplorerProposalStatusQuery | undefined,
terms: Terms
): string {
const DEFAULT = t('Unknown');
if (!data?.proposal?.state) {
return DEFAULT;
}
switch (data.proposal.state) {
case 'STATE_DECLINED':
return `${t('Rejected on')}: ${format(terms.closingTimestamp, DEFAULT)}`;
case 'STATE_ENACTED':
return `${t('Vote passed on')}: ${format(
terms.enactmentTimestamp,
DEFAULT
)}`;
case 'STATE_FAILED':
return `${t('Failed on')}: ${format(terms.validationTimestamp, DEFAULT)}`;
case 'STATE_OPEN':
return `${t('Open until')}: ${format(terms.closingTimestamp, DEFAULT)}`;
case 'STATE_PASSED':
return `${t('Passed on')}: ${format(terms.closingTimestamp, DEFAULT)}`;
case 'STATE_REJECTED':
return `${t('Rejected on submission')}`;
case 'STATE_WAITING_FOR_NODE_VOTE':
return `${t('Opening')}...`;
default:
return DEFAULT;
}
}
interface ProposalDateProps {
id: string;
terms: Terms;
}
/**
* Shows the most relevant date for the proposal summary view. Depending on the
* state returned by GraphQL, we show either the validation, closing or enactment
* timestamp
*/
export const ProposalDate = ({ terms, id }: ProposalDateProps) => {
const { data } = useExplorerProposalStatusQuery({
variables: {
id,
},
});
return (
<Lozenge className="font-sans text-xs float-right">
{getDate(data, terms)}
</Lozenge>
);
};

View File

@ -0,0 +1,117 @@
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
import type { IconProps } from '@vegaprotocol/ui-toolkit';
import { useExplorerProposalStatusQuery } from './__generated__/Proposal';
import type { ExplorerProposalStatusQuery } from './__generated__/Proposal';
import type * as Apollo from '@apollo/client';
import type * as Types from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
type ProposalQueryResult = Apollo.QueryResult<
ExplorerProposalStatusQuery,
Types.Exact<{
id: string;
}>
>;
interface ProposalStatusIconProps {
id: string;
}
type IconAndLabel = {
icon: IconProps['name'];
label: string;
};
/**
* Select an icon for a given query result. Tolerates queries that don't return
* any data
*
* @param data a data result from useExplorerProposalStatusQuery
* @returns Icon name
*/
export function getIconAndLabelForStatus(
res: ProposalQueryResult
): IconAndLabel {
const DEFAULT: IconAndLabel = {
icon: 'error',
label: t('Proposal state unknown'),
};
if (res.loading) {
return {
icon: 'more',
label: t('Loading data'),
};
}
if (!res?.data?.proposal || res.error) {
return {
icon: 'error',
label: res.error?.message || DEFAULT.label,
};
}
switch (res.data.proposal.state) {
case 'STATE_DECLINED':
return {
icon: 'stop',
label: t('Proposal did not have enough participation to be valid'),
};
case 'STATE_ENACTED':
return {
icon: 'tick-circle',
label: t('Vote passed and the proposal has been enacted'),
};
case 'STATE_FAILED':
return {
icon: 'thumbs-down',
label: t('Proposal became invalid and was not processed'),
};
case 'STATE_OPEN':
return {
// A checklist to indicate in progress
icon: 'form',
label: t('Voting is in progress'),
};
case 'STATE_PASSED':
return {
icon: 'thumbs-up',
label: t(
'Voting is complete and this proposal was approved. It is not yet enacted.'
),
};
case 'STATE_REJECTED':
return {
icon: 'disable',
label: t('The proposal was invalid'),
};
case 'STATE_WAITING_FOR_NODE_VOTE':
return {
// A sparkly thing indicating it's new
icon: 'clean',
label: t('Proposal is being checked by validators'),
};
default:
return DEFAULT;
}
}
/**
*/
export const ProposalStatusIcon = ({ id }: ProposalStatusIconProps) => {
const { icon, label } = getIconAndLabelForStatus(
useExplorerProposalStatusQuery({
variables: {
id,
},
})
);
return (
<div className="float-left mr-3">
<Tooltip description={<p>{label}</p>}>
<Icon name={icon} />
</Tooltip>
</div>
);
};

View File

@ -0,0 +1,42 @@
import { Loader } from '@vegaprotocol/ui-toolkit';
import { BundleError } from './signature-bundle/bundle-error';
import { BundleExists } from './signature-bundle/bundle-exists';
import { useExplorerNewAssetSignatureBundleQuery } from './__generated__/SignatureBundle';
export interface ProposalSignatureBundleByTypeProps {
id: string;
}
/**
* If a proposal needs a signature bundle, AND that signature bundle exists
* AND that proposal is a New Asset Proposal, render an overview of the
* signature bundle. Or an error.
*
* There is an almost identical component, ProposalSignatureBundleUpdateAsset
*/
export const ProposalSignatureBundleNewAsset = ({
id,
}: ProposalSignatureBundleByTypeProps) => {
const { data, error, loading } = useExplorerNewAssetSignatureBundleQuery({
variables: {
id,
},
});
if (loading) {
return <Loader />;
}
if (data?.erc20ListAssetBundle?.signatures) {
return (
<BundleExists
signatures={data.erc20ListAssetBundle.signatures}
nonce={data.erc20ListAssetBundle.nonce}
status={data.asset?.status}
proposalId={id}
/>
);
} else {
return <BundleError status={data?.asset?.status} error={error} />;
}
};

View File

@ -0,0 +1,39 @@
import { Loader } from '@vegaprotocol/ui-toolkit';
import type { ProposalSignatureBundleByTypeProps } from './signature-bundle-new';
import { BundleError } from './signature-bundle/bundle-error';
import { BundleExists } from './signature-bundle/bundle-exists';
import { useExplorerUpdateAssetSignatureBundleQuery } from './__generated__/SignatureBundle';
/**
* If a proposal needs a signature bundle, AND that signature bundle exists
* AND that proposal is a Asset Limits Proposal, render an overview of the
* signature bundle. Or an error.
*
* There is an almost identical component, ProposalSignatureBundleNewAsset
*/
export const ProposalSignatureBundleUpdateAsset = ({
id,
}: ProposalSignatureBundleByTypeProps) => {
const { data, error, loading } = useExplorerUpdateAssetSignatureBundleQuery({
variables: {
id,
},
});
if (loading) {
return <Loader />;
}
if (data?.erc20SetAssetLimitsBundle?.signatures) {
return (
<BundleExists
signatures={data.erc20SetAssetLimitsBundle.signatures}
nonce={data.erc20SetAssetLimitsBundle.nonce}
status={data.asset?.status}
proposalId={id}
/>
);
} else {
return <BundleError status={data?.asset?.status} error={error} />;
}
};

View File

@ -0,0 +1,31 @@
import { ProposalSignatureBundleNewAsset } from './signature-bundle-new';
import { ProposalSignatureBundleUpdateAsset } from './signature-bundle-update';
export function format(date: string | undefined, def: string) {
if (!date) {
return def;
}
return new Date().toLocaleDateString() || def;
}
interface ProposalSignatureBundleProps {
id: string;
type: 'NewAsset' | 'UpdateAsset';
}
/**
* Some proposals, if enacted, generate a signature bundle.
* The queries have to be split due to the way the API returns
* errors, hence this slightly redundant feeling switcher.
*/
export const ProposalSignatureBundle = ({
id,
type,
}: ProposalSignatureBundleProps) => {
return type === 'NewAsset' ? (
<ProposalSignatureBundleNewAsset id={id} />
) : (
<ProposalSignatureBundleUpdateAsset id={id} />
);
};

View File

@ -0,0 +1,67 @@
import type { ApolloError } from '@apollo/client';
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { AssetStatus } from '@vegaprotocol/types';
import { MemoryRouter } from 'react-router-dom';
import { BundleError } from './bundle-error';
describe('Bundle Error', () => {
const NON_ENABLED_STATUS: AssetStatus[] = [
AssetStatus.STATUS_PENDING_LISTING,
AssetStatus.STATUS_PROPOSED,
AssetStatus.STATUS_REJECTED,
];
const ENABLED_STATUS: AssetStatus[] = [AssetStatus.STATUS_ENABLED];
it.each(NON_ENABLED_STATUS)(
'shows the apollo error if not enabled and a message is provided',
(status) => {
const screen = render(
<MemoryRouter>
<MockedProvider>
<BundleError
error={{ message: 'test-error-message' } as ApolloError}
status={status}
/>
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('test-error-message')).toBeInTheDocument();
}
);
it.each(NON_ENABLED_STATUS)(
'shows some fallback error message if the bundle has not been used and there is no error',
(status) => {
const screen = render(
<MemoryRouter>
<MockedProvider>
<BundleError status={status} />
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('No bundle for proposal ID')).toBeInTheDocument();
}
);
it.each(ENABLED_STATUS)(
'hides ProposalLink if the status is enabled',
(status) => {
const screen = render(
<MemoryRouter>
<MockedProvider>
<BundleError
error={{ message: 'irrelevant ' } as ApolloError}
status={status}
/>
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText('Asset already enabled')).toBeInTheDocument();
}
);
});

View File

@ -0,0 +1,34 @@
import type { ApolloError } from '@apollo/client';
import type { AssetStatus } from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
import Hash from '../../../../links/hash';
import { IconForBundleStatus } from './bundle-icon';
export interface BundleErrorProps {
status?: AssetStatus;
error?: ApolloError;
}
/**
* Renders if a proposal signature bundle cannot be found.
* It is also possible that a data node has dropped the bundle
* from its retention so there is a backup case where we check
* the status - if it's already enabled, pretend this isn't an error
*/
export const BundleError = ({ status, error }: BundleErrorProps) => {
return (
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
<IconForBundleStatus status={status} />
<h1 className="text-xl pb-1">{t('No signature bundle found')}</h1>
<p>
{status === 'STATUS_ENABLED' ? (
t('Asset already enabled')
) : (
<Hash text={error ? error.message : t('No bundle for proposal ID')} />
)}
</p>
</div>
);
};

View File

@ -0,0 +1,64 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { AssetStatus } from '@vegaprotocol/types';
import { MemoryRouter } from 'react-router-dom';
import { BundleExists } from './bundle-exists';
describe('Bundle Exists', () => {
const NON_ENABLED_STATUS: AssetStatus[] = [
AssetStatus.STATUS_PENDING_LISTING,
AssetStatus.STATUS_PROPOSED,
AssetStatus.STATUS_REJECTED,
];
const ENABLED_STATUS: AssetStatus[] = [AssetStatus.STATUS_ENABLED];
const MOCK_SIGNATURES =
'0x1760ce4efec01d5bb9fe57c0660a39a27e0b5a1bd39b4d3fc0452bf142a4ef733baaf4dbedd74cd676c759f96ac7f412ec05e136bbff8a81d9f0c2428df8e099004c4d166ece0527d86e202386e8758580790d0a46f6429baaa268b4bf5c11c9e3402e175a9022ae8295e543853c01383ef17cd58458ddfc9c706fca0ecffa0d3d0160eb5234e63a78b9649428ca7659eb693fdde86edfc6c2707ef2e8fbf4c76a9f0eebe183da571a58837e2fa995d7b10955ecf04c138dc0ce964f17c3de1f5c6d0060ec132cc515cf974ead1da38e2ff8d4b870335865e8d4d8c982182bddea18bb513e2d37fd32de7fedc0c4f694e6bcdcf20f8547f19e7d9d25ce32c6ca9ea51e01ad3b92475778d9da7251d3943071f59107c9cd7ad9dd1923c06e88d3352869d919a591bf30732bad2c3fcf30a9f664dbb7a9c65a64875a48cbeb5741bd0a853901';
const MOCK_NONCE =
'18250011763873610289536200551900545467959221115607409799241178172533618346952';
const MOCK_PROPOSAL_ID =
'285923fed8c66ffb416b163e8ec72d3a87b9b8e2570e7ee7fe97d7092a918bc8';
const PROPOSAL_LINK_TEXT = 'Visit our Governance site to submit this';
it.each(NON_ENABLED_STATUS)(
'shows a handy ProposalLink if the status is anything except enabled',
(status) => {
const screen = render(
<MemoryRouter>
<MockedProvider>
<BundleExists
nonce={MOCK_NONCE}
proposalId={MOCK_PROPOSAL_ID}
signatures={MOCK_SIGNATURES}
status={status}
/>
</MockedProvider>
</MemoryRouter>
);
expect(screen.getByText(PROPOSAL_LINK_TEXT)).toBeInTheDocument();
}
);
it.each(ENABLED_STATUS)(
'hides ProposalLink if the status is enabled',
(status) => {
const screen = render(
<MemoryRouter>
<MockedProvider>
<BundleExists
nonce={MOCK_NONCE}
proposalId={MOCK_PROPOSAL_ID}
signatures={MOCK_SIGNATURES}
status={status}
/>
</MockedProvider>
</MemoryRouter>
);
expect(screen.queryAllByText(PROPOSAL_LINK_TEXT)).toEqual([]);
}
);
});

View File

@ -0,0 +1,46 @@
import { t } from '@vegaprotocol/i18n';
import type { AssetStatus } from '@vegaprotocol/types';
import ProposalLink from '../../../../links/proposal-link/proposal-link';
import { IconForBundleStatus } from './bundle-icon';
import { ProposalSignatureBundleDetails } from './details';
export interface BundleExistsProps {
signatures: string;
nonce: string;
status?: AssetStatus;
proposalId: string;
}
/**
* If a proposal needs a signature bundle, AND that signature bundle exists,
* this component renders that signature bundle.
*
*/
export const BundleExists = ({
signatures,
nonce,
status,
proposalId,
}: BundleExistsProps) => {
return (
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
<IconForBundleStatus status={status} />
<h1 className="text-xl pb-1">
{status === 'STATUS_ENABLED'
? t('Asset added to bridge')
: t('Signature bundle generated')}
</h1>
<ProposalSignatureBundleDetails signatures={signatures} nonce={nonce} />
{status !== 'STATUS_ENABLED' ? (
<p className="mt-5">
<ProposalLink
id={proposalId}
text={t('Visit our Governance site to submit this')}
/>
</p>
) : null}
</div>
);
};

View File

@ -0,0 +1,33 @@
import { render } from '@testing-library/react';
import { AssetStatus } from '@vegaprotocol/types';
import { IconForBundleStatus } from './bundle-icon';
describe('Bundle status icon', () => {
const NON_ENABLED_STATUS: AssetStatus[] = [
AssetStatus.STATUS_PENDING_LISTING,
AssetStatus.STATUS_PROPOSED,
AssetStatus.STATUS_REJECTED,
];
const ENABLED_STATUS: AssetStatus[] = [AssetStatus.STATUS_ENABLED];
it.each(NON_ENABLED_STATUS)(
'show a sparkle icon if the bundle is unused',
(status) => {
const screen = render(<IconForBundleStatus status={status} />);
const i = screen.getByRole('img');
expect(i).toHaveAttribute('aria-label');
expect(i.getAttribute('aria-label')).toMatch(/clean/);
}
);
it.each(ENABLED_STATUS)(
'shows a tick if the bundle is already used',
(status) => {
const screen = render(<IconForBundleStatus status={status} />);
const i = screen.getByRole('img');
expect(i).toHaveAttribute('aria-label');
expect(i.getAttribute('aria-label')).toMatch(/tick-circle/);
}
);
});

View File

@ -0,0 +1,17 @@
import type { AssetStatus } from '@vegaprotocol/types';
import type { IconName } from '@vegaprotocol/ui-toolkit';
import { Icon } from '@vegaprotocol/ui-toolkit';
export interface IconForBundleStatusProps {
status?: AssetStatus;
}
/**
* Naively select an icon for an asset. If it is enabled, we show a tick - anything
* else is assumed to be 'in progress'. There should only be a signature bundle or the
* asset should not exist
*/
export const IconForBundleStatus = ({ status }: IconForBundleStatusProps) => {
const i: IconName = status === 'STATUS_ENABLED' ? 'tick-circle' : 'clean';
return <Icon className="float-left mt-2 mr-3" name={i} />;
};

View File

@ -0,0 +1,41 @@
import { t } from '@vegaprotocol/i18n';
export interface ProposalSignatureBundleDetailsProps {
signatures: string;
nonce: string;
}
export const ProposalSignatureBundleDetails = ({
signatures,
nonce,
}: ProposalSignatureBundleDetailsProps) => {
return (
<details className="mt-5">
<summary>{t('Signature bundle details')}</summary>
<div className="ml-4">
<h2 className="text-lg mt-2 mb-2">{t('Signatures')}</h2>
<p>
<textarea
className="font-mono bg-neutral-300 text-[11px] leading-3 text-gray-900 w-full p-2 max-w-[615px]"
readOnly={true}
rows={12}
cols={120}
value={signatures}
/>
</p>
<h2 className="text-lg mt-5 mb-2">{t('Nonce')}</h2>
<p>
<textarea
className="font-mono bg-neutral-300 text-[11px] leading-3 text-gray-900 w-full p-2 max-w-[615px]"
readOnly={true}
rows={2}
cols={120}
value={nonce}
/>
</p>
</div>
</details>
);
};

View File

@ -0,0 +1,90 @@
import type { ProposalTerms } from '../tx-proposal';
import { useState } from 'react';
import type { components } from '../../../../../types/explorer';
import { JsonViewerDialog } from '../../../dialogs/json-viewer-dialog';
import ProposalLink from '../../../links/proposal-link/proposal-link';
import truncate from 'lodash/truncate';
import { ProposalStatusIcon } from './proposal-status-icon';
import ReactMarkdown from 'react-markdown';
import { ProposalDate } from './proposal-date';
import { t } from '@vegaprotocol/i18n';
type Rationale = components['schemas']['vegaProposalRationale'];
type ProposalTermsDialog = {
open: boolean;
title: string;
content: unknown;
};
interface ProposalSummaryProps {
id: string;
rationale?: Rationale;
terms?: ProposalTerms;
}
/**
* Effectively a 'preview' for what the proposal is about, and a link to the
* Token site for full breakdown of votes
*/
export const ProposalSummary = ({
id,
rationale,
terms,
}: ProposalSummaryProps) => {
const [dialog, setDialog] = useState<ProposalTermsDialog>({
open: false,
title: '',
content: null,
});
const openDialog = () => {
if (!terms) return;
setDialog({
open: true,
title: rationale?.title || t('Proposal details'),
content: terms ? terms : {},
});
};
const md =
rationale && rationale.description
? truncate(rationale.description, {
// Limits the description to roughly 5 lines, maximum
length: 350,
})
: '';
return (
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5">
{id && <ProposalStatusIcon id={id} />}
{rationale?.title && <h1 className="text-xl pb-1">{rationale.title}</h1>}
{rationale?.description && (
<p className="pt-2 text-sm leading-tight">
<ReactMarkdown
className="react-markdown-container"
skipHtml={true}
disallowedElements={['img']}
linkTarget="_blank"
>
{md}
</ReactMarkdown>
</p>
)}
<p className="pt-5">
<button className="underline max-md:hidden mr-5" onClick={openDialog}>
{t('View terms')}
</button>{' '}
<ProposalLink id={id} text={t('Full details')} />
{terms && <ProposalDate terms={terms} id={id} />}
</p>
<JsonViewerDialog
open={dialog.open}
onChange={(isOpen) => setDialog({ ...dialog, open: isOpen })}
title={dialog.title}
content={dialog.content}
/>
</div>
);
};

View File

@ -55,7 +55,7 @@ export const TxDetailsShared = ({
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Hash')}</TableCell>
<TableCell>
<Hash text={txData.hash} />
<Hash text={txData.hash.toLowerCase()} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">

View File

@ -24,6 +24,7 @@ import { TxDetailsProtocolUpgrade } from './tx-details-protocol-upgrade';
import { TxDetailsIssueSignatures } from './tx-issue-signatures';
import { TxDetailsNodeAnnounce } from './tx-node-announce';
import { TxDetailsStateVariable } from './tx-state-variable-proposal';
import { TxProposal } from './tx-proposal';
import { TxDetailsTransfer } from './tx-transfer';
interface TxDetailsWrapperProps {
@ -88,6 +89,8 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsOrderAmend;
case 'Validator Heartbeat':
return TxDetailsHeartbeat;
case 'Proposal':
return TxProposal;
case 'Vote on Proposal':
return TxProposalVote;
case 'Batch Market Instructions':

View File

@ -0,0 +1,115 @@
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { sharedHeaderProps, TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import has from 'lodash/has';
import { ProposalSummary } from './proposal/summary';
import Hash from '../../links/hash';
import { ProposalSignatureBundle } from './proposal/signature-bundle';
import { t } from '@vegaprotocol/i18n';
export type Proposal = components['schemas']['v1ProposalSubmission'];
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
interface TxProposalProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Detects if a given proposal requires a signature bundle to be emitted by the validators for
* the proposal to be completed after it is accepted by the governance vote
*
* @param t The details of the proposal
* @returns boolean True if a signature bundle is required. Used to fetch a signature bundle
*/
export function proposalRequiresSignatureBundle(proposal?: Proposal): boolean {
if (!proposal?.terms) {
return false;
}
return !!['newAsset', 'updateAsset'].filter((requiredIfExists) =>
has(proposal.terms, requiredIfExists)
);
}
/**
* Given a proposal, returns a nice text label for the proposal type
*
* @param terms The details of the proposal
* @returns string Proposal type
*/
export function proposalTypeLabel(terms?: ProposalTerms): string {
if (has(terms, 'newAsset')) {
return t('New asset proposal');
} else if (has(terms, 'updateAsset')) {
return t('Update asset proposal');
} else if (has(terms, 'newMarket')) {
return t('New market proposal');
} else if (has(terms, 'updateMarket')) {
return t('Update market proposal');
} else if (has(terms, 'updateNetworkParameter')) {
return t('Update network parameter');
} else if (has(terms, 'newFreeform')) {
return t('Freeform proposal');
}
// The list above contains all currently known types. This will be triggered if a new
// unrecognised proposal type is added.
return t('Governance proposal');
}
/**
* A proposal. It's more simplistic than man other views because there are already other, better
* ways to view things about a proposal!
*
*/
export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
if (!txData || !txData.command.proposalSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
let deterministicId = '';
const proposal: Proposal = txData.command.proposalSubmission;
const sig = txData?.signature?.value;
if (sig) {
deterministicId = txSignatureToDeterministicId(sig);
}
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{proposalTypeLabel(proposal.terms)}</TableCell>
</TableRow>
{/* TODO: Disable type row */}
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
hideTypeRow={true}
/>
<TableRow modifier="bordered">
<TableCell>{t('Proposal ID')}</TableCell>
<TableCell>
<Hash text={deterministicId} />
</TableCell>
</TableRow>
</TableWithTbody>
<ProposalSummary
id={deterministicId}
rationale={proposal.rationale}
terms={proposal?.terms}
/>
{proposalRequiresSignatureBundle(proposal) && (
<ProposalSignatureBundle
id={deterministicId}
type={proposal.terms?.newAsset ? 'NewAsset' : 'UpdateAsset'}
/>
)}
</>
);
};

View File

@ -9,7 +9,7 @@ import type {
// Note: Long enough that there is a truncated output and a full output
const pubKey =
'67755549e43e95f0697f83b2bf419c6ccc18eee32a8a61b8ba6f59471b86fbef';
const hash = '7416753A30622A9E24A06F0172D6C33A95186B36806D96345C6DC5A23FA3F283';
const hash = '7416753a30622a9e24a06f0172d6c33a95186b36806d96345c6dc5a23fa3f283';
const height = '52987';
const txData: BlockExplorerTransactionResult = {

View File

@ -2,3 +2,12 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.react-markdown-container a {
color: #ff077f;
text-decoration: underline;
}
.react-markdown-container a:before {
content: '🔗 ';
}

View File

@ -68,6 +68,7 @@
"react-hook-form": "^7.27.0",
"react-i18next": "^11.11.4",
"react-intersection-observer": "^9.2.2",
"react-markdown": "^8.0.5",
"react-router-dom": "6.3.0",
"react-syntax-highlighter": "^15.4.5",
"react-use-websocket": "^3.0.0",

467
yarn.lock
View File

@ -6501,6 +6501,13 @@
"@types/d3-interpolate" "^2"
"@types/d3-selection" "^2"
"@types/debug@^4.0.0":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==
dependencies:
"@types/ms" "*"
"@types/eslint-scope@^3.7.3":
version "3.7.4"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
@ -6734,6 +6741,11 @@
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node-fetch@^2.5.10", "@types/node-fetch@^2.5.7":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
@ -6812,7 +6824,7 @@
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.0.tgz#a1c3809b0ad61c62cac6d4e0c56d610c910b7654"
integrity sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==
"@types/prop-types@*":
"@types/prop-types@*", "@types/prop-types@^15.0.0":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
@ -9008,6 +9020,11 @@ bail@^1.0.0:
resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776"
integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
bail@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -9733,6 +9750,11 @@ character-entities@^1.0.0:
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b"
integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==
character-entities@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
character-reference-invalid@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
@ -10137,6 +10159,11 @@ comma-separated-tokens@^1.0.0:
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea"
integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==
comma-separated-tokens@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee"
integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
commander@^2.19.0, commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@ -11135,7 +11162,7 @@ debug@3.2.7, debug@^3.0.0, debug@^3.1.0, debug@^3.2.7:
dependencies:
ms "^2.1.1"
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -11172,6 +11199,13 @@ decimal.js@^10.2.1:
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.1.tgz#be75eeac4a2281aace80c1a8753587c27ef053e7"
integrity sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==
decode-named-character-reference@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e"
integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==
dependencies:
character-entities "^2.0.0"
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@ -11304,6 +11338,11 @@ dependency-graph@0.11.0, dependency-graph@^0.11.0:
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27"
integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==
dequal@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
des.js@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
@ -11398,6 +11437,11 @@ diff@^4.0.1:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diff@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -13808,6 +13852,11 @@ hast-util-to-parse5@^6.0.0:
xtend "^4.0.0"
zwitch "^1.0.0"
hast-util-whitespace@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz#0ec64e257e6fc216c7d14c8a1b74d27d650b4557"
integrity sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==
hastscript@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640"
@ -14769,6 +14818,11 @@ is-plain-obj@^3.0.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
is-plain-obj@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
is-plain-object@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
@ -15895,6 +15949,11 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
kleur@^4.0.3:
version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
klona@^2.0.4, klona@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
@ -16528,6 +16587,33 @@ mdast-util-definitions@^4.0.0:
dependencies:
unist-util-visit "^2.0.0"
mdast-util-definitions@^5.0.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7"
integrity sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==
dependencies:
"@types/mdast" "^3.0.0"
"@types/unist" "^2.0.0"
unist-util-visit "^4.0.0"
mdast-util-from-markdown@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz#0214124154f26154a2b3f9d401155509be45e894"
integrity sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g==
dependencies:
"@types/mdast" "^3.0.0"
"@types/unist" "^2.0.0"
decode-named-character-reference "^1.0.0"
mdast-util-to-string "^3.1.0"
micromark "^3.0.0"
micromark-util-decode-numeric-character-reference "^1.0.0"
micromark-util-decode-string "^1.0.0"
micromark-util-normalize-identifier "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
unist-util-stringify-position "^3.0.0"
uvu "^0.5.0"
mdast-util-to-hast@10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb"
@ -16542,11 +16628,32 @@ mdast-util-to-hast@10.0.1:
unist-util-position "^3.0.0"
unist-util-visit "^2.0.0"
mdast-util-to-hast@^12.1.0:
version "12.3.0"
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz#045d2825fb04374e59970f5b3f279b5700f6fb49"
integrity sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==
dependencies:
"@types/hast" "^2.0.0"
"@types/mdast" "^3.0.0"
mdast-util-definitions "^5.0.0"
micromark-util-sanitize-uri "^1.1.0"
trim-lines "^3.0.0"
unist-util-generated "^2.0.0"
unist-util-position "^4.0.0"
unist-util-visit "^4.0.0"
mdast-util-to-string@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527"
integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==
mdast-util-to-string@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.1.tgz#db859050d79d48cf9896d294de06f3ede7474d16"
integrity sha512-tGvhT94e+cVnQt8JWE9/b3cUQZWS732TJxXHktvP+BYo62PpYD53Ls/6cC60rW21dW+txxiM4zMdc6abASvZKA==
dependencies:
"@types/mdast" "^3.0.0"
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
@ -16680,6 +16787,201 @@ microevent.ts@~0.1.1:
resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0"
integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==
micromark-core-commonmark@^1.0.1:
version "1.0.6"
resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz#edff4c72e5993d93724a3c206970f5a15b0585ad"
integrity sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==
dependencies:
decode-named-character-reference "^1.0.0"
micromark-factory-destination "^1.0.0"
micromark-factory-label "^1.0.0"
micromark-factory-space "^1.0.0"
micromark-factory-title "^1.0.0"
micromark-factory-whitespace "^1.0.0"
micromark-util-character "^1.0.0"
micromark-util-chunked "^1.0.0"
micromark-util-classify-character "^1.0.0"
micromark-util-html-tag-name "^1.0.0"
micromark-util-normalize-identifier "^1.0.0"
micromark-util-resolve-all "^1.0.0"
micromark-util-subtokenize "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.1"
uvu "^0.5.0"
micromark-factory-destination@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz#fef1cb59ad4997c496f887b6977aa3034a5a277e"
integrity sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==
dependencies:
micromark-util-character "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
micromark-factory-label@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz#6be2551fa8d13542fcbbac478258fb7a20047137"
integrity sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==
dependencies:
micromark-util-character "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-factory-space@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz#cebff49968f2b9616c0fcb239e96685cb9497633"
integrity sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==
dependencies:
micromark-util-character "^1.0.0"
micromark-util-types "^1.0.0"
micromark-factory-title@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz#7e09287c3748ff1693930f176e1c4a328382494f"
integrity sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==
dependencies:
micromark-factory-space "^1.0.0"
micromark-util-character "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-factory-whitespace@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c"
integrity sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==
dependencies:
micromark-factory-space "^1.0.0"
micromark-util-character "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
micromark-util-character@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86"
integrity sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==
dependencies:
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
micromark-util-chunked@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06"
integrity sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==
dependencies:
micromark-util-symbol "^1.0.0"
micromark-util-classify-character@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20"
integrity sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==
dependencies:
micromark-util-character "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
micromark-util-combine-extensions@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5"
integrity sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==
dependencies:
micromark-util-chunked "^1.0.0"
micromark-util-types "^1.0.0"
micromark-util-decode-numeric-character-reference@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946"
integrity sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==
dependencies:
micromark-util-symbol "^1.0.0"
micromark-util-decode-string@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz#942252ab7a76dec2dbf089cc32505ee2bc3acf02"
integrity sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==
dependencies:
decode-named-character-reference "^1.0.0"
micromark-util-character "^1.0.0"
micromark-util-decode-numeric-character-reference "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-encode@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz#2c1c22d3800870ad770ece5686ebca5920353383"
integrity sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==
micromark-util-html-tag-name@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz#eb227118befd51f48858e879b7a419fc0df20497"
integrity sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==
micromark-util-normalize-identifier@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828"
integrity sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==
dependencies:
micromark-util-symbol "^1.0.0"
micromark-util-resolve-all@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88"
integrity sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==
dependencies:
micromark-util-types "^1.0.0"
micromark-util-sanitize-uri@^1.0.0, micromark-util-sanitize-uri@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz#f12e07a85106b902645e0364feb07cf253a85aee"
integrity sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==
dependencies:
micromark-util-character "^1.0.0"
micromark-util-encode "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-subtokenize@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz#ff6f1af6ac836f8bfdbf9b02f40431760ad89105"
integrity sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==
dependencies:
micromark-util-chunked "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-util-symbol@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz#b90344db62042ce454f351cf0bebcc0a6da4920e"
integrity sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==
micromark-util-types@^1.0.0, micromark-util-types@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.2.tgz#f4220fdb319205812f99c40f8c87a9be83eded20"
integrity sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==
micromark@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.1.0.tgz#eeba0fe0ac1c9aaef675157b52c166f125e89f62"
integrity sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==
dependencies:
"@types/debug" "^4.0.0"
debug "^4.0.0"
decode-named-character-reference "^1.0.0"
micromark-core-commonmark "^1.0.1"
micromark-factory-space "^1.0.0"
micromark-util-character "^1.0.0"
micromark-util-chunked "^1.0.0"
micromark-util-combine-extensions "^1.0.0"
micromark-util-decode-numeric-character-reference "^1.0.0"
micromark-util-encode "^1.0.0"
micromark-util-normalize-identifier "^1.0.0"
micromark-util-resolve-all "^1.0.0"
micromark-util-sanitize-uri "^1.0.0"
micromark-util-subtokenize "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.1"
uvu "^0.5.0"
micromatch@^3.1.10, micromatch@^3.1.4:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@ -16946,6 +17248,11 @@ move-concurrently@^1.0.1:
rimraf "^2.5.4"
run-queue "^1.0.3"
mri@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -18713,6 +19020,11 @@ property-information@^5.0.0, property-information@^5.3.0:
dependencies:
xtend "^4.0.0"
property-information@^6.0.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.2.0.tgz#b74f522c31c097b5149e3c3cb8d7f3defd986a1d"
integrity sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==
protocols@^1.4.0:
version "1.4.8"
resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"
@ -19047,6 +19359,27 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-markdown@^8.0.5:
version "8.0.5"
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.5.tgz#c9a70a33ca9aeeafb769c6582e7e38843b9d70ad"
integrity sha512-jGJolWWmOWAvzf+xMdB9zwStViODyyFQhNB/bwCerbBKmrTmgmA599CGiOlP58OId1IMoIRsA8UdI1Lod4zb5A==
dependencies:
"@types/hast" "^2.0.0"
"@types/prop-types" "^15.0.0"
"@types/unist" "^2.0.0"
comma-separated-tokens "^2.0.0"
hast-util-whitespace "^2.0.0"
prop-types "^15.0.0"
property-information "^6.0.0"
react-is "^18.0.0"
remark-parse "^10.0.0"
remark-rehype "^10.0.0"
space-separated-tokens "^2.0.0"
style-to-object "^0.4.0"
unified "^10.0.0"
unist-util-visit "^4.0.0"
vfile "^5.0.0"
react-refresh@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
@ -19523,6 +19856,25 @@ remark-parse@8.0.3:
vfile-location "^3.0.0"
xtend "^4.0.1"
remark-parse@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.1.tgz#6f60ae53edbf0cf38ea223fe643db64d112e0775"
integrity sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==
dependencies:
"@types/mdast" "^3.0.0"
mdast-util-from-markdown "^1.0.0"
unified "^10.0.0"
remark-rehype@^10.0.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279"
integrity sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==
dependencies:
"@types/hast" "^2.0.0"
"@types/mdast" "^3.0.0"
mdast-util-to-hast "^12.1.0"
unified "^10.0.0"
remark-slug@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce"
@ -19872,6 +20224,13 @@ rxjs@^7.5.1, rxjs@^7.5.5:
dependencies:
tslib "^2.1.0"
sade@^1.7.3:
version "1.8.1"
resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
dependencies:
mri "^1.1.0"
safe-buffer@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
@ -20503,6 +20862,11 @@ space-separated-tokens@^1.0.0:
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==
space-separated-tokens@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f"
integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
@ -20975,6 +21339,13 @@ style-to-object@0.3.0, style-to-object@^0.3.0:
dependencies:
inline-style-parser "0.1.1"
style-to-object@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.4.1.tgz#53cf856f7cf7f172d72939d9679556469ba5de37"
integrity sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==
dependencies:
inline-style-parser "0.1.1"
styled-jsx@5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729"
@ -21468,6 +21839,11 @@ tree-kill@1.2.2:
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
trim-lines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"
integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@ -21493,6 +21869,11 @@ trough@^1.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
trough@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876"
integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==
ts-dedent@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
@ -21819,6 +22200,19 @@ unified@9.2.0:
trough "^1.0.0"
vfile "^4.0.0"
unified@^10.0.0:
version "10.1.2"
resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df"
integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==
dependencies:
"@types/unist" "^2.0.0"
bail "^2.0.0"
extend "^3.0.0"
is-buffer "^2.0.0"
is-plain-obj "^4.0.0"
trough "^2.0.0"
vfile "^5.0.0"
union-value@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
@ -21867,16 +22261,33 @@ unist-util-generated@^1.0.0:
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b"
integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==
unist-util-generated@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.1.tgz#e37c50af35d3ed185ac6ceacb6ca0afb28a85cae"
integrity sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==
unist-util-is@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797"
integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==
unist-util-is@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.2.0.tgz#37eed0617b76c114fd34d44c201aa96fd928b309"
integrity sha512-Glt17jWwZeyqrFqOK0pF1Ded5U3yzJnFr8CG1GMjCWTp9zDo2p+cmD6pWbZU8AgM5WU3IzRv6+rBwhzsGh6hBQ==
unist-util-position@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47"
integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==
unist-util-position@^4.0.0:
version "4.0.4"
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-4.0.4.tgz#93f6d8c7d6b373d9b825844645877c127455f037"
integrity sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==
dependencies:
"@types/unist" "^2.0.0"
unist-util-remove-position@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc"
@ -21898,6 +22309,13 @@ unist-util-stringify-position@^2.0.0:
dependencies:
"@types/unist" "^2.0.2"
unist-util-stringify-position@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d"
integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==
dependencies:
"@types/unist" "^2.0.0"
unist-util-visit-parents@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6"
@ -21906,6 +22324,14 @@ unist-util-visit-parents@^3.0.0:
"@types/unist" "^2.0.0"
unist-util-is "^4.0.0"
unist-util-visit-parents@^5.1.1:
version "5.1.3"
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb"
integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==
dependencies:
"@types/unist" "^2.0.0"
unist-util-is "^5.0.0"
unist-util-visit@2.0.3, unist-util-visit@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c"
@ -21915,6 +22341,15 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0:
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"
unist-util-visit@^4.0.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2"
integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==
dependencies:
"@types/unist" "^2.0.0"
unist-util-is "^5.0.0"
unist-util-visit-parents "^5.1.1"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@ -22192,6 +22627,16 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uvu@^0.5.0:
version "0.5.6"
resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df"
integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==
dependencies:
dequal "^2.0.0"
diff "^5.0.0"
kleur "^4.0.3"
sade "^1.7.3"
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
@ -22260,6 +22705,14 @@ vfile-message@^2.0.0:
"@types/unist" "^2.0.0"
unist-util-stringify-position "^2.0.0"
vfile-message@^3.0.0:
version "3.1.4"
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea"
integrity sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==
dependencies:
"@types/unist" "^2.0.0"
unist-util-stringify-position "^3.0.0"
vfile@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624"
@ -22270,6 +22723,16 @@ vfile@^4.0.0:
unist-util-stringify-position "^2.0.0"
vfile-message "^2.0.0"
vfile@^5.0.0:
version "5.3.7"
resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.7.tgz#de0677e6683e3380fafc46544cfe603118826ab7"
integrity sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==
dependencies:
"@types/unist" "^2.0.0"
is-buffer "^2.0.0"
unist-util-stringify-position "^3.0.0"
vfile-message "^3.0.0"
vm-browserify@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"