Commit 95bd3e72 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #484 from blockscout/contract-creation-code

contract creation code
parents a8fb54aa 7222b975
...@@ -14,6 +14,7 @@ import type { ...@@ -14,6 +14,7 @@ import type {
} from 'types/api/address'; } from 'types/api/address';
import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters } from 'types/api/block'; import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters } from 'types/api/block';
import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts'; import type { ChartMarketResponse, ChartTransactionResponse } from 'types/api/charts';
import type { SmartContract } from 'types/api/contract';
import type { IndexingStatus } from 'types/api/indexingStatus'; import type { IndexingStatus } from 'types/api/indexingStatus';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction'; import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { JsonRpcUrlResponse } from 'types/api/jsonRpcUrl'; import type { JsonRpcUrlResponse } from 'types/api/jsonRpcUrl';
...@@ -163,6 +164,11 @@ export const RESOURCES = { ...@@ -163,6 +164,11 @@ export const RESOURCES = {
filterFields: [ ], filterFields: [ ],
}, },
// CONTRACT
contract: {
path: '/api/v2/smart-contracts/:id',
},
// TOKEN // TOKEN
token: { token: {
path: '/api/v2/tokens/:hash', path: '/api/v2/tokens/:hash',
...@@ -270,6 +276,7 @@ Q extends 'address_logs' ? LogsResponseAddress : ...@@ -270,6 +276,7 @@ Q extends 'address_logs' ? LogsResponseAddress :
Q extends 'token' ? TokenInfo : Q extends 'token' ? TokenInfo :
Q extends 'token_counters' ? TokenCounters : Q extends 'token_counters' ? TokenCounters :
Q extends 'config_json_rpc' ? JsonRpcUrlResponse : Q extends 'config_json_rpc' ? JsonRpcUrlResponse :
Q extends 'contract' ? SmartContract :
never; never;
/* eslint-enable @typescript-eslint/indent */ /* eslint-enable @typescript-eslint/indent */
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"apps": "/apps", "apps": "/apps",
"app_index": "/apps/:id", "app_index": "/apps/:id",
"search_results": "/search-results", "search_results": "/search-results",
"other": "/search-results",
"auth": "/auth/auth0", "auth": "/auth/auth0",
"stats": "/stats" "stats": "/stats",
"visualize_sol2uml": "/visualize/sol2uml"
} }
...@@ -94,9 +94,9 @@ export const ROUTES = { ...@@ -94,9 +94,9 @@ export const ROUTES = {
pattern: PATHS.search_results, pattern: PATHS.search_results,
}, },
// ??? what URL will be here // VISUALIZE
other: { visualize_sol2uml: {
pattern: PATHS.other, pattern: PATHS.visualize_sol2uml,
}, },
// AUTH // AUTH
......
...@@ -6,6 +6,8 @@ import { mode } from '@chakra-ui/theme-tools'; ...@@ -6,6 +6,8 @@ import { mode } from '@chakra-ui/theme-tools';
const { defineMultiStyleConfig, definePartsStyle } = const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys); createMultiStyleConfigHelpers(parts.keys);
import Button from './Button/Button';
const variantSoftRounded = definePartsStyle((props) => { const variantSoftRounded = definePartsStyle((props) => {
return { return {
tab: { tab: {
...@@ -26,11 +28,32 @@ const variantSoftRounded = definePartsStyle((props) => { ...@@ -26,11 +28,32 @@ const variantSoftRounded = definePartsStyle((props) => {
}; };
}); });
const variantOutline = definePartsStyle((props) => {
return {
tab: {
...Button.variants?.outline(props),
...Button.baseStyle,
_selected: Button.variants?.outline(props)._active,
},
};
});
const sizes = {
sm: definePartsStyle({
tab: Button.sizes?.sm,
}),
md: definePartsStyle({
tab: Button.sizes?.md,
}),
};
const variants = { const variants = {
'soft-rounded': variantSoftRounded, 'soft-rounded': variantSoftRounded,
outline: variantOutline,
}; };
const Tabs = defineMultiStyleConfig({ const Tabs = defineMultiStyleConfig({
sizes,
variants, variants,
}); });
......
export interface SmartContract {
deployed_bytecode: string | null;
creation_bytecode: string | null;
is_self_destructed: boolean;
abi: Array<Record<string, unknown>> | null;
compiler_version: string | null;
evm_version: string | null;
optimization_enabled: boolean | null;
optimization_runs: number | null;
name: string | null;
verified_at: string | null;
is_verified: boolean | null;
source_code: string | null;
can_be_visualized_via_sol2uml: boolean | null;
}
import React from 'react';
import type { RoutedSubTab } from 'ui/shared/RoutedTabs/types';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
interface Props {
tabs: Array<RoutedSubTab>;
}
const AddressContract = ({ tabs }: Props) => {
return <RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={{ columnGap: 3 }}/>;
};
export default React.memo(AddressContract);
...@@ -24,6 +24,10 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => { ...@@ -24,6 +24,10 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
if (!items?.length) {
return null;
}
return ( return (
<ChartWidget <ChartWidget
chartHeight="200px" chartHeight="200px"
......
import { Flex, Skeleton, Button, Grid, GridItem, Text } from '@chakra-ui/react';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import link from 'lib/link/link';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
const DynamicContractSourceCode = dynamic(
() => import('./ContractSourceCode'),
{ ssr: false },
);
const InfoItem = ({ label, value }: { label: string; value: string }) => (
<GridItem display="flex" columnGap={ 6 }>
<Text w="170px" flexShrink={ 0 } fontWeight={ 500 }>{ label }</Text>
<Text wordBreak="break-all">{ value }</Text>
</GridItem>
);
const ContractCode = () => {
const router = useRouter();
const { data, isLoading, isError } = useApiQuery('contract', {
pathParams: { id: router.query.id?.toString() },
queryOptions: {
enabled: Boolean(router.query.id),
},
});
if (isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
return (
<>
<Flex justifyContent="space-between" mb={ 2 }>
<Skeleton w="180px" h={ 5 } borderRadius="full"/>
<Skeleton w={ 5 } h={ 5 }/>
</Flex>
<Skeleton w="100%" h="250px" borderRadius="md"/>
<Flex justifyContent="space-between" mb={ 2 } mt={ 6 }>
<Skeleton w="180px" h={ 5 } borderRadius="full"/>
<Skeleton w={ 5 } h={ 5 }/>
</Flex>
<Skeleton w="100%" h="400px" borderRadius="md"/>
</>
);
}
const verificationButton = (
<Button
size="sm"
ml="auto"
mr={ 3 }
as="a"
href={ link('address_contract_verification', { id: router.query.id?.toString() }) }
>
Verify & publish
</Button>
);
return (
<>
{ data.is_verified && (
<Grid templateColumns={{ base: '1fr', lg: '1fr 1fr' }} rowGap={ 4 } columnGap={ 6 } mb={ 8 }>
{ data.name && <InfoItem label="Contract name" value={ data.name }/> }
{ data.compiler_version && <InfoItem label="Compiler version" value={ data.compiler_version }/> }
{ data.evm_version && <InfoItem label="EVM version" value={ data.evm_version }/> }
{ typeof data.optimization_enabled === 'boolean' && <InfoItem label="Optimization enabled" value={ data.optimization_enabled ? 'true' : 'false' }/> }
{ data.optimization_runs && <InfoItem label="Optimization runs" value={ String(data.optimization_runs) }/> }
{ data.verified_at && <InfoItem label="Verified at" value={ data.verified_at }/> }
</Grid>
) }
<Flex flexDir="column" rowGap={ 6 }>
{ data.source_code && (
<DynamicContractSourceCode
data={ data.source_code }
hasSol2Yml={ Boolean(data.can_be_visualized_via_sol2uml) }
address={ router.query.id?.toString() }
/>
) }
{ data.abi && (
<RawDataSnippet
data={ JSON.stringify(data.abi) }
title="Contract ABI"
textareaMinHeight="200px"
/>
) }
{ data.creation_bytecode && (
<RawDataSnippet
data={ data.creation_bytecode }
title="Contract creation code"
rightSlot={ data.is_verified ? null : verificationButton }
/>
) }
{ data.deployed_bytecode && (
<RawDataSnippet
data={ data.deployed_bytecode }
title="Deployed ByteCode"
/>
) }
</Flex>
</>
);
};
export default ContractCode;
import { Box, Flex, Link, Text, Tooltip } from '@chakra-ui/react';
import React from 'react';
import link from 'lib/link/link';
import CodeEditor from 'ui/shared/CodeEditor';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
interface Props {
data: string;
hasSol2Yml: boolean;
address?: string;
}
const ContractSourceCode = ({ data, hasSol2Yml, address }: Props) => {
return (
<Box>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
<Text fontWeight={ 500 }>Contract source code</Text>
{ hasSol2Yml && address && (
<Tooltip label="Visualize contract code using Sol2Uml JS library">
<Link
href={ link('visualize_sol2uml', undefined, { address }) }
ml="auto"
mr={ 3 }
>
View Sol2uml
</Link>
</Tooltip>
) }
<CopyToClipboard text={ data }/>
</Flex>
<CodeEditor value={ data } id="source_code"/>
</Box>
);
};
export default React.memo(ContractSourceCode);
...@@ -8,17 +8,28 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -8,17 +8,28 @@ import useApiQuery from 'lib/api/useApiQuery';
import notEmpty from 'lib/notEmpty'; import notEmpty from 'lib/notEmpty';
import AddressBlocksValidated from 'ui/address/AddressBlocksValidated'; import AddressBlocksValidated from 'ui/address/AddressBlocksValidated';
import AddressCoinBalance from 'ui/address/AddressCoinBalance'; import AddressCoinBalance from 'ui/address/AddressCoinBalance';
import AddressContract from 'ui/address/AddressContract';
import AddressDetails from 'ui/address/AddressDetails'; import AddressDetails from 'ui/address/AddressDetails';
import AddressInternalTxs from 'ui/address/AddressInternalTxs'; import AddressInternalTxs from 'ui/address/AddressInternalTxs';
import AddressLogs from 'ui/address/AddressLogs';
import AddressTokenTransfers from 'ui/address/AddressTokenTransfers'; import AddressTokenTransfers from 'ui/address/AddressTokenTransfers';
import AddressTxs from 'ui/address/AddressTxs'; import AddressTxs from 'ui/address/AddressTxs';
import AddressLogs from 'ui/address/logs/AddressLogs'; import ContractCode from 'ui/address/contract/ContractCode';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs'; import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs';
const CONTRACT_TABS = [
{ id: 'contact_code', title: 'Code', component: <ContractCode/> },
{ id: 'contact_decompiled_code', title: 'Decompiled code', component: <div>Decompiled code</div> },
{ id: 'read_contract', title: 'Read contract', component: <div>Read contract</div> },
{ id: 'read_proxy', title: 'Read proxy', component: <div>Read proxy</div> },
{ id: 'write_contract', title: 'Write contract', component: <div>Write contract</div> },
{ id: 'write_proxy', title: 'Write proxy', component: <div>Write proxy</div> },
];
const AddressPageContent = () => { const AddressPageContent = () => {
const router = useRouter(); const router = useRouter();
...@@ -46,6 +57,12 @@ const AddressPageContent = () => { ...@@ -46,6 +57,12 @@ const AddressPageContent = () => {
// later api will return info about available tabs // later api will return info about available tabs
{ id: 'blocks_validated', title: 'Blocks validated', component: <AddressBlocksValidated/> }, { id: 'blocks_validated', title: 'Blocks validated', component: <AddressBlocksValidated/> },
isContract ? { id: 'logs', title: 'Logs', component: <AddressLogs/> } : undefined, isContract ? { id: 'logs', title: 'Logs', component: <AddressLogs/> } : undefined,
isContract ? {
id: 'contract',
title: 'Contract',
component: <AddressContract tabs={ CONTRACT_TABS }/>,
subTabs: CONTRACT_TABS,
} : undefined,
].filter(notEmpty); ].filter(notEmpty);
}, [ isContract ]); }, [ isContract ]);
......
import { chakra, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/theme-tomorrow';
import 'ace-builds/src-noconflict/theme-tomorrow_night';
import 'ace-builds/src-noconflict/ext-language_tools';
interface Props {
id: string;
value: string;
className?: string;
}
const CodeEditorBase = chakra(({ id, value, className }: Props) => {
const theme = useColorModeValue('tomorrow', 'tomorrow_night');
return (
<AceEditor
className={ className }
mode="javascript" // TODO need to find mode for solidity
theme={ theme }
value={ value }
name={ id }
editorProps={{ $blockScrolling: true }}
readOnly
width="100%"
showPrintMargin={ false }
maxLines={ 25 }
/>
);
});
const CodeEditor = ({ id, value }: Props) => {
// see theme/components/Textarea.ts variantFilledInactive
const bgColor = useColorModeValue('#f5f5f6', '#1a1b1b');
const gutterBgColor = useColorModeValue('gray.100', '#25282c');
return (
<CodeEditorBase
id={ id }
value={ value }
bgColor={ bgColor }
borderRadius="md"
overflow="hidden"
sx={{
'.ace_gutter': {
backgroundColor: gutterBgColor,
},
}}
/>
);
};
export default React.memo(CodeEditor);
import { Box, Flex, Text, Textarea, chakra } from '@chakra-ui/react';
import React from 'react';
import CopyToClipboard from './CopyToClipboard';
interface Props {
data: string;
title?: string;
className?: string;
rightSlot?: React.ReactNode;
textareaMinHeight?: string;
}
const RawDataSnippet = ({ data, className, title, rightSlot, textareaMinHeight }: Props) => {
return (
<Box className={ className }>
<Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }>
{ title && <Text fontWeight={ 500 }>{ title }</Text> }
{ rightSlot }
<CopyToClipboard text={ data }/>
</Flex>
<Textarea
variant="filledInactive"
p={ 4 }
minHeight={ textareaMinHeight || '400px' }
value={ data }
fontSize="sm"
borderRadius="md"
readOnly
/>
</Box>
);
};
export default React.memo(chakra(RawDataSnippet));
import type { ChakraProps } from '@chakra-ui/react'; import type { ChakraProps, ThemingProps } from '@chakra-ui/react';
import { import {
Tab, Tab,
Tabs, Tabs,
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
TabPanels, TabPanels,
Box, Box,
useColorModeValue, useColorModeValue,
chakra,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import type { StyleProps } from '@chakra-ui/styled-system'; import type { StyleProps } from '@chakra-ui/styled-system';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -28,14 +29,15 @@ const hiddenItemStyles: StyleProps = { ...@@ -28,14 +29,15 @@ const hiddenItemStyles: StyleProps = {
visibility: 'hidden', visibility: 'hidden',
}; };
interface Props { interface Props extends ThemingProps<'Tabs'> {
tabs: Array<RoutedTab>; tabs: Array<RoutedTab>;
tabListProps?: ChakraProps; tabListProps?: ChakraProps;
rightSlot?: React.ReactNode; rightSlot?: React.ReactNode;
stickyEnabled?: boolean; stickyEnabled?: boolean;
className?: string;
} }
const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => { const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, ...themeProps }: Props) => {
const router = useRouter(); const router = useRouter();
const scrollDirection = useScrollDirection(); const scrollDirection = useScrollDirection();
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1); const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
...@@ -58,8 +60,9 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => ...@@ -58,8 +60,9 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
useEffect(() => { useEffect(() => {
if (router.isReady) { if (router.isReady) {
let tabIndex = 0; let tabIndex = 0;
if (router.query.tab) { const tabFromRoute = router.query.tab;
tabIndex = tabs.findIndex(({ id }) => id === router.query.tab); if (tabFromRoute) {
tabIndex = tabs.findIndex(({ id, subTabs }) => id === tabFromRoute || subTabs?.some(({ id }) => id === tabFromRoute));
if (tabIndex < 0) { if (tabIndex < 0) {
tabIndex = 0; tabIndex = 0;
} }
...@@ -89,12 +92,14 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => ...@@ -89,12 +92,14 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
return ( return (
<Tabs <Tabs
variant="soft-rounded" className={ className }
colorScheme="blue" variant={ themeProps.variant || 'soft-rounded' }
colorScheme={ themeProps.colorScheme || 'blue' }
isLazy isLazy
onChange={ handleTabChange } onChange={ handleTabChange }
index={ activeTabIndex } index={ activeTabIndex }
position="relative" position="relative"
size={ themeProps.size || 'md' }
> >
<TabList <TabList
marginBottom={{ base: 6, lg: 8 }} marginBottom={{ base: 6, lg: 8 }}
...@@ -155,6 +160,7 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => ...@@ -155,6 +160,7 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
ref={ tabsRefs[index] } ref={ tabsRefs[index] }
{ ...(index < tabsCut ? {} : hiddenItemStyles) } { ...(index < tabsCut ? {} : hiddenItemStyles) }
scrollSnapAlign="start" scrollSnapAlign="start"
flexShrink={ 0 }
> >
{ tab.title } { tab.title }
</Tab> </Tab>
...@@ -169,4 +175,4 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => ...@@ -169,4 +175,4 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
); );
}; };
export default React.memo(RoutedTabs); export default React.memo(chakra(RoutedTabs));
...@@ -2,8 +2,11 @@ export interface RoutedTab { ...@@ -2,8 +2,11 @@ export interface RoutedTab {
id: string; id: string;
title: string; title: string;
component: React.ReactNode; component: React.ReactNode;
subTabs?: Array<RoutedSubTab>;
} }
export type RoutedSubTab = Omit<RoutedTab, 'subTabs'>;
export interface MenuButton { export interface MenuButton {
id: null; id: null;
title: string; title: string;
......
import { Flex, Textarea, Skeleton } from '@chakra-ui/react'; import { Flex, Skeleton } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
import TxPendingAlert from 'ui/tx/TxPendingAlert'; import TxPendingAlert from 'ui/tx/TxPendingAlert';
import TxSocketAlert from 'ui/tx/TxSocketAlert'; import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo'; import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
...@@ -41,24 +41,12 @@ const TxRawTrace = () => { ...@@ -41,24 +41,12 @@ const TxRawTrace = () => {
} }
if (data.length === 0) { if (data.length === 0) {
return <span>There is no raw trace for this transaction.</span>; return <span>No trace entries found.</span>;
} }
const text = JSON.stringify(data, undefined, 4); const text = JSON.stringify(data, undefined, 4);
return ( return <RawDataSnippet data={ text }/>;
<>
<Flex justifyContent="end" mb={ 2 }>
<CopyToClipboard text={ text }/>
</Flex>
<Textarea
variant="filledInactive"
minHeight="500px"
p={ 4 }
value={ text }
/>
</>
);
}; };
export default TxRawTrace; export default TxRawTrace;
...@@ -3864,6 +3864,11 @@ abort-controller@^3.0.0: ...@@ -3864,6 +3864,11 @@ abort-controller@^3.0.0:
dependencies: dependencies:
event-target-shim "^5.0.0" event-target-shim "^5.0.0"
ace-builds@^1.14.0, ace-builds@^1.4.14:
version "1.14.0"
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.14.0.tgz#85a6733b4fa17b0abc3dbfe38cd8d823cad79716"
integrity sha512-3q8LvawomApRCt4cC0OzxVjDsZ609lDbm8l0Xl9uqG06dKEq4RT0YXLUyk7J2SxmqIp5YXzZNw767Dr8GKUruw==
acorn-globals@^7.0.0: acorn-globals@^7.0.0:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3"
...@@ -5012,6 +5017,11 @@ detect-node-es@^1.1.0: ...@@ -5012,6 +5017,11 @@ detect-node-es@^1.1.0:
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
diff-match-patch@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37"
integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==
diff-sequences@^29.3.1: diff-sequences@^29.3.1:
version "29.3.1" version "29.3.1"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e"
...@@ -7244,6 +7254,16 @@ lodash.debounce@^4.0.8: ...@@ -7244,6 +7254,16 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
lodash.memoize@4.x: lodash.memoize@4.x:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
...@@ -8035,6 +8055,17 @@ quick-format-unescaped@^4.0.3: ...@@ -8035,6 +8055,17 @@ quick-format-unescaped@^4.0.3:
resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==
react-ace@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-10.1.0.tgz#d348eac2b16475231779070b6cd16768deed565f"
integrity sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==
dependencies:
ace-builds "^1.4.14"
diff-match-patch "^1.0.5"
lodash.get "^4.4.2"
lodash.isequal "^4.5.0"
prop-types "^15.7.2"
react-clientside-effect@^1.2.6: react-clientside-effect@^1.2.6:
version "1.2.6" version "1.2.6"
resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment