Compare commits

...

49 Commits

Author SHA1 Message Date
Valtteri Karesto
7f8f47cb14 Fix modal showing up bug, issue #99 2022-03-09 13:27:42 +02:00
Valtteri Karesto
ddb043c104 Merge pull request #128 from eqlabs/feat/fix-account-button
Fixes issue #101
2022-03-09 12:50:50 +02:00
Valtteri Karesto
d2ad6537d7 Remove console logs 2022-03-09 12:48:53 +02:00
Valtteri Karesto
8f004ee4da Fixes issue #101 2022-03-09 12:29:20 +02:00
Valtteri Karesto
b90bf67c20 Merge pull request #120 from eqlabs/feat/add-hooks-docs
Feat/add hooks docs
2022-03-09 12:06:06 +02:00
Valtteri Karesto
746112e637 fixes issue #115 2022-03-09 11:57:06 +02:00
Valtteri Karesto
13bfd42093 Decorations should now work correctly 2022-03-09 11:14:43 +02:00
Valtteri Karesto
c1f7d7d51c Make sure not all files are defined as C files 2022-03-09 11:14:14 +02:00
muzamil
38a097a8f9 Merge pull request #108 from eqlabs/feat/debug-prettify
Debug stream improvements.
2022-03-08 21:07:13 +05:30
muzam1l
db0ffe999e fix line wrap and timestamp font 2022-03-08 19:18:51 +05:30
Jani Anttonen
6d88c4e546 Merge pull request #100 from eqlabs/feat/persist-splits
Persist splits
2022-03-08 14:15:49 +02:00
Valtteri Karesto
e11ddaffb0 Remove html from md now that the paragraph styling works 2022-03-08 11:36:43 +02:00
Valtteri Karesto
2e88f568b8 Add imports 2022-03-08 11:24:14 +02:00
Valtteri Karesto
237d504f17 Add better styling for markdown 2022-03-08 11:24:02 +02:00
Valtteri Karesto
197fc09e1d delete unused files 2022-03-08 11:23:51 +02:00
Valtteri Karesto
2c74a93aee Add better logic for markdown files 2022-03-08 11:22:58 +02:00
Valtteri Karesto
5209644780 Add raw-loader 2022-03-08 11:22:39 +02:00
Valtteri Karesto
ed37427da8 Add new markdown files 2022-03-08 09:56:07 +02:00
Valtteri Karesto
daee9de96c Update md script and readme 2022-03-08 09:55:40 +02:00
Valtteri Karesto
e4b10d12c2 Remove unused script 2022-03-08 09:54:47 +02:00
Valtteri Karesto
64eabb4502 Remove unused dependencies 2022-03-08 09:54:21 +02:00
Valtteri Karesto
12a24d3d86 Remove rst files 2022-03-08 09:54:11 +02:00
muzam1l
ce91182c7b fix and segrregate debug stream state. 2022-03-07 17:14:53 +05:30
muzam1l
2e3a0e557e Some vertical margin in enhanced logs 2022-03-04 20:37:07 +05:30
Valtteri Karesto
395e02343b Add logic to enrich the hover messages with rst file contents 2022-03-04 12:48:03 +02:00
Valtteri Karesto
3682dd4946 Add readme how to use the script 2022-03-04 12:38:39 +02:00
Valtteri Karesto
3070ed706e Add script that converts rst to json which contains md 2022-03-04 12:38:24 +02:00
Valtteri Karesto
4b73687779 Add lodash.uniqby 2022-03-04 12:37:51 +02:00
muzam1l
6b9a9ef978 better socket error mesage 2022-03-04 15:36:48 +05:30
muzam1l
bc5bb5be39 Fix timesatmp in logs 2022-03-04 15:22:05 +05:30
muzam1l
0fe83811b9 Clickable accounts in logs and formatting fixes 2022-03-04 15:13:56 +05:30
muzam1l
c6359aa853 Add error code to close event message 2022-03-04 14:19:23 +05:30
muzam1l
c9c818c8f3 json data is now collapsible! 2022-03-03 21:03:28 +05:30
muzam1l
c521246393 debug stream state as full global 2022-03-03 16:36:42 +05:30
muzam1l
8936b34361 separate messages for debug stream error and close evensts 2022-03-03 16:16:00 +05:30
muzam1l
5993d2762f Separately format time, json and message of debug stream log. 2022-03-01 21:36:24 +05:30
muzamil
0a44b5b5d1 Merge pull request #94 from eqlabs/feat/wasm-stats
Compiled wasm file stats
2022-03-01 15:14:14 +05:30
JaniAnttonen
810d3b2524 Add a todo 2022-02-25 16:34:09 +02:00
JaniAnttonen
a3393ded1e Fix build 2022-02-25 15:22:09 +02:00
JaniAnttonen
17ede265b1 Remove debug logging 2022-02-25 14:39:06 +02:00
JaniAnttonen
629070edad Save split state 2022-02-25 13:50:56 +02:00
Vaclav Barta
cc83924c27 Merge pull request #98 from eqlabs/bugfix/typos
fix for #97
2022-02-15 08:49:38 +01:00
Vaclav Barta
e3e964f72a fix for #97 2022-02-11 13:54:52 +01:00
muzam
0def1d30a6 compiled wasm file stats 2022-02-09 19:06:29 +05:30
Joni Juup
bdb2c0cf8f Merge pull request #93 from eqlabs/bugfix/monaco-popups
Fix monaco popup issues
2022-02-09 15:10:28 +02:00
muzamil
3e8dbc9793 Merge pull request #91 from eqlabs/fixes
Clearing some issues
2022-02-09 18:32:47 +05:30
Joni Juup
eddf228283 more specific rule for the box itself 2022-02-09 13:14:17 +02:00
Joni Juup
f9d617efdc more specific css rule for hr 2022-02-09 13:09:36 +02:00
Joni Juup
43796021da fix monaco editor issues with popups 2022-02-09 13:04:41 +02:00
74 changed files with 1910 additions and 233 deletions

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
*.md

View File

@@ -27,7 +27,7 @@ const labelStyle = css({
mb: "$0.5",
});
const AccountDialog = ({
export const AccountDialog = ({
activeAccountAddress,
setActiveAccountAddress,
}: {
@@ -36,11 +36,13 @@ const AccountDialog = ({
}) => {
const snap = useSnapshot(state);
const [showSecret, setShowSecret] = useState(false);
const activeAccount = snap.accounts.find(account => account.address === activeAccountAddress);
const activeAccount = snap.accounts.find(
(account) => account.address === activeAccountAddress
);
return (
<Dialog
open={Boolean(activeAccountAddress)}
onOpenChange={open => {
onOpenChange={(open) => {
setShowSecret(false);
!open && setActiveAccountAddress(null);
}}
@@ -135,7 +137,7 @@ const AccountDialog = ({
}}
ghost
size="xs"
onClick={() => setShowSecret(curr => !curr)}
onClick={() => setShowSecret((curr) => !curr)}
>
{showSecret ? "Hide" : "Show"}
</Button>
@@ -221,13 +223,15 @@ interface AccountProps {
showHookStats?: boolean;
}
const Accounts: FC<AccountProps> = props => {
const Accounts: FC<AccountProps> = (props) => {
const snap = useSnapshot(state);
const [activeAccountAddress, setActiveAccountAddress] = useState<string | null>(null);
const [activeAccountAddress, setActiveAccountAddress] = useState<
string | null
>(null);
useEffect(() => {
const fetchAccInfo = async () => {
if (snap.clientStatus === "online") {
const requests = snap.accounts.map(acc =>
const requests = snap.accounts.map((acc) =>
snap.client?.send({
id: acc.address,
command: "account_info",
@@ -239,13 +243,15 @@ const Accounts: FC<AccountProps> = props => {
const address = res?.account_data?.Account as string;
const balance = res?.account_data?.Balance as string;
const sequence = res?.account_data?.Sequence as number;
const accountToUpdate = state.accounts.find(acc => acc.address === address);
const accountToUpdate = state.accounts.find(
(acc) => acc.address === address
);
if (accountToUpdate) {
accountToUpdate.xrp = balance;
accountToUpdate.sequence = sequence;
}
});
const objectRequests = snap.accounts.map(acc => {
const objectRequests = snap.accounts.map((acc) => {
return snap.client?.send({
id: `${acc.address}-hooks`,
command: "account_objects",
@@ -255,7 +261,9 @@ const Accounts: FC<AccountProps> = props => {
const objectResponses = await Promise.all(objectRequests);
objectResponses.forEach((res: any) => {
const address = res?.account as string;
const accountToUpdate = state.accounts.find(acc => acc.address === address);
const accountToUpdate = state.accounts.find(
(acc) => acc.address === address
);
if (accountToUpdate) {
accountToUpdate.hooks = res.account_objects
.filter((ac: any) => ac?.LedgerEntryType === "Hook")
@@ -337,7 +345,7 @@ const Accounts: FC<AccountProps> = props => {
overflowY: "auto",
}}
>
{snap.accounts.map(account => (
{snap.accounts.map((account) => (
<Flex
column
key={account.address + account.name}
@@ -364,7 +372,7 @@ const Accounts: FC<AccountProps> = props => {
<Text>{account.name} </Text>
<Text
css={{
color: "$mauve9",
color: "$textMuted",
wordBreak: "break-word",
}}
>
@@ -383,28 +391,37 @@ const Accounts: FC<AccountProps> = props => {
</Text>
</Box>
{!props.hideDeployBtn && (
<Button
css={{ ml: "auto" }}
size="xs"
uppercase
isLoading={account.isLoading}
disabled={
account.isLoading ||
!snap.files.filter(file => file.compiledWatContent).length
}
variant="secondary"
onClick={e => {
<div
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
deployHook(account);
}}
>
Deploy
</Button>
<Button
css={{ ml: "auto" }}
size="xs"
uppercase
isLoading={account.isLoading}
disabled={
account.isLoading ||
!snap.files.filter((file) => file.compiledWatContent)
.length
}
variant="secondary"
onClick={(e) => {
e.stopPropagation();
deployHook(account);
}}
>
Deploy
</Button>
</div>
)}
</Flex>
{props.showHookStats && (
<Text muted small css={{ mt: "$2" }}>
{account.hooks.length} hook{account.hooks.length === 1 ? "" : "s"} installed
{account.hooks.length} hook
{account.hooks.length === 1 ? "" : "s"} installed
</Text>
)}
</Flex>
@@ -436,7 +453,7 @@ const ImportAccountDialog = () => {
name="secret"
type="password"
value={value}
onChange={e => setValue(e.target.value)}
onChange={(e) => setValue(e.target.value)}
/>
</DialogDescription>

View File

@@ -66,6 +66,12 @@ export const StyledButton = styled("button", {
},
},
variant: {
link: {
textDecoration: "underline",
fontSize: "inherit",
color: "$textMuted",
textUnderlineOffset: "2px",
},
default: {
backgroundColor: "$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve12",
@@ -81,8 +87,7 @@ export const StyledButton = styled("button", {
boxShadow: "inset 0 0 0 1px $colors$mauve11",
},
"&:focus": {
boxShadow:
"inset 0 0 0 1px $colors$mauve12, inset 0 0 0 2px $colors$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve12, inset 0 0 0 2px $colors$mauve12",
},
'&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
{
@@ -137,7 +142,11 @@ export const StyledButton = styled("button", {
},
},
},
muted: {
true: {
color: "$textMuted",
},
},
outline: {
true: {
backgroundColor: "transparent",
@@ -227,21 +236,16 @@ export const StyledButton = styled("button", {
},
});
const CustomButton: React.FC<
React.ComponentProps<typeof StyledButton> & { as?: string }
> = React.forwardRef(({ children, as = "button", ...rest }, ref) => (
// @ts-expect-error
<StyledButton {...rest} ref={ref} as={as}>
<Flex
as="span"
css={{ gap: "$2", alignItems: "center" }}
className="button-content"
>
{children}
</Flex>
{rest.isLoading && <Spinner css={{ position: "absolute" }} />}
</StyledButton>
));
const CustomButton: React.FC<React.ComponentProps<typeof StyledButton> & { as?: string }> =
React.forwardRef(({ children, as = "button", ...rest }, ref) => (
// @ts-expect-error
<StyledButton {...rest} ref={ref} as={as}>
<Flex as="span" css={{ gap: "$2", alignItems: "center" }} className="button-content">
{children}
</Flex>
{rest.isLoading && <Spinner css={{ position: "absolute" }} />}
</StyledButton>
));
CustomButton.displayName = "CustomButton";

View File

@@ -1,84 +1,136 @@
import { useEffect, useState } from "react";
import { useSnapshot } from "valtio";
import { useCallback, useEffect } from "react";
import { proxy, ref, useSnapshot } from "valtio";
import { Select } from ".";
import state from "../state";
import state, { ILog } from "../state";
import { extractJSON } from "../utils/json";
import LogBox from "./LogBox";
import Text from "./Text";
interface ISelect<T = string> {
label: string;
value: T;
}
const streamState = proxy({
selectedAccount: null as ISelect | null,
logs: [] as ILog[],
socket: undefined as WebSocket | undefined,
});
const DebugStream = () => {
const snap = useSnapshot(state);
const { selectedAccount, logs, socket } = useSnapshot(streamState);
const { accounts } = useSnapshot(state);
const accountOptions = snap.accounts.map(acc => ({
const accountOptions = accounts.map(acc => ({
label: acc.name,
value: acc.address,
}));
const [selectedAccount, setSelectedAccount] = useState<typeof accountOptions[0] | null>(null);
const renderNav = () => (
<>
<Text css={{ mx: "$2", fontSize: "inherit" }}>Account: </Text>
<Select
instanceId="debugStreamAccount"
instanceId="DSAccount"
placeholder="Select account"
options={accountOptions}
hideSelectedOptions
value={selectedAccount}
onChange={acc => setSelectedAccount(acc as any)}
css={{ width: "30%" }}
onChange={acc => (streamState.selectedAccount = acc as any)}
css={{ width: "100%" }}
/>
</>
);
const prepareLog = useCallback((str: any): ILog => {
if (typeof str !== "string") throw Error("Unrecognized debug log stream!");
const match = str.match(/([\s\S]+(?:UTC|ISO|GMT[+|-]\d+))\ ?([\s\S]*)/m);
const [_, tm, msg] = match || [];
const extracted = extractJSON(msg);
const timestamp = isNaN(Date.parse(tm || ""))
? tm
: new Date(tm).toLocaleTimeString();
const message = !extracted
? msg
: msg.slice(0, extracted.start) + msg.slice(extracted.end + 1);
const jsonData = extracted
? JSON.stringify(extracted.result, null, 2)
: undefined;
return {
type: "log",
message,
timestamp,
jsonData,
defaultCollapsed: true,
};
}, []);
useEffect(() => {
const account = selectedAccount?.value;
if (!account) {
return;
if (account && (!socket || !socket.url.endsWith(account))) {
socket?.close();
streamState.socket = ref(
new WebSocket(
`wss://hooks-testnet-debugstream.xrpl-labs.com/${account}`
)
);
} else if (!account && socket) {
socket.close();
streamState.socket = undefined;
}
const socket = new WebSocket(`wss://hooks-testnet-debugstream.xrpl-labs.com/${account}`);
}, [selectedAccount?.value, socket]);
useEffect(() => {
const account = selectedAccount?.value;
const socket = streamState.socket;
if (!socket) return;
const onOpen = () => {
state.debugLogs = [];
state.debugLogs.push({
streamState.logs = [];
streamState.logs.push({
type: "success",
message: `Debug stream opened for account ${account}`,
});
};
const onError = () => {
state.debugLogs.push({
streamState.logs.push({
type: "error",
message: "Something went wrong in establishing connection!",
message: "Something went wrong! Check your connection and try again.",
});
setSelectedAccount(null);
};
const onClose = (e: CloseEvent) => {
streamState.logs.push({
type: "error",
message: `Connection was closed. [code: ${e.code}]`,
});
streamState.selectedAccount = null;
};
const onMessage = (event: any) => {
if (!event.data) return;
state.debugLogs.push({
type: "log",
message: event.data,
});
streamState.logs.push(prepareLog(event.data));
};
socket.addEventListener("open", onOpen);
socket.addEventListener("close", onError);
socket.addEventListener("close", onClose);
socket.addEventListener("error", onError);
socket.addEventListener("message", onMessage);
return () => {
socket.removeEventListener("open", onOpen);
socket.removeEventListener("close", onError);
socket.removeEventListener("close", onClose);
socket.removeEventListener("message", onMessage);
socket.close();
socket.removeEventListener("error", onError);
};
}, [selectedAccount]);
}, [prepareLog, selectedAccount?.value, socket]);
return (
<LogBox
enhanced
renderNav={renderNav}
title="Debug stream"
logs={snap.debugLogs}
clearLog={() => (state.debugLogs = [])}
logs={logs}
clearLog={() => (streamState.logs = [])}
/>
);
};

View File

@@ -1,20 +1,22 @@
import React, { useRef } from "react";
import React, { useRef, useState } from "react";
import { useSnapshot, ref } from "valtio";
import Editor, { loader } from "@monaco-editor/react";
import type monaco from "monaco-editor";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import NextLink from "next/link";
import ReactTimeAgo from "react-time-ago";
import filesize from "filesize";
import Box from "./Box";
import Container from "./Container";
import dark from "../theme/editor/amy.json";
import light from "../theme/editor/xcode_default.json";
import state from "../state";
import wat from "../utils/wat-highlight";
import EditorNavigation from "./EditorNavigation";
import Text from "./Text";
import Link from "./Link";
import { Button, Text, Link, Flex } from ".";
loader.config({
paths: {
@@ -22,11 +24,72 @@ loader.config({
},
});
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
const DeployEditor = () => {
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
const snap = useSnapshot(state);
const router = useRouter();
const { theme } = useTheme();
const [showContent, setShowContent] = useState(false);
const activeFile = snap.files[snap.active];
const compiledSize = activeFile?.compiledContent?.byteLength || 0;
const color =
compiledSize > FILESIZE_BREAKPOINTS[1]
? "$error"
: compiledSize > FILESIZE_BREAKPOINTS[0]
? "$warning"
: "$success";
const CompiledStatView = activeFile && (
<Flex
column
align="center"
css={{
fontSize: "$sm",
fontFamily: "$monospace",
textAlign: "center",
}}
>
<Flex row align="center">
<Text css={{ mr: "$1" }}>
Compiled {activeFile.name.split(".")[0] + ".wasm"}
</Text>
{activeFile?.lastCompiled && (
<ReactTimeAgo date={activeFile.lastCompiled} locale="en-US" />
)}
{activeFile.compiledContent?.byteLength && (
<Text css={{ ml: "$2", color }}>
({filesize(activeFile.compiledContent.byteLength)})
</Text>
)}
</Flex>
<Button variant="link" onClick={() => setShowContent(true)}>
View as WAT-file
</Button>
</Flex>
);
const NoContentView = !snap.loading && router.isReady && (
<Text
css={{
mt: "-60px",
fontSize: "$sm",
fontFamily: "$monospace",
maxWidth: "300px",
textAlign: "center",
}}
>
{`You haven't compiled any files yet, compile files on `}
<NextLink shallow href={`/develop/${router.query.slug}`} passHref>
<Link as="a">develop view</Link>
</NextLink>
</Text>
);
const isContent =
snap.files?.filter((file) => file.compiledWatContent).length > 0 &&
router.isReady;
return (
<Box
css={{
@@ -39,60 +102,48 @@ const DeployEditor = () => {
}}
>
<EditorNavigation showWat />
{snap.files?.filter((file) => file.compiledWatContent).length > 0 &&
router.isReady ? (
<Editor
className="hooks-editor"
// keepCurrentModel
defaultLanguage={snap.files?.[snap.active]?.language}
language={snap.files?.[snap.active]?.language}
path={`file://tmp/c/${snap.files?.[snap.active]?.name}.wat`}
value={snap.files?.[snap.active]?.compiledWatContent || ""}
beforeMount={(monaco) => {
if (!state.editorCtx) {
state.editorCtx = ref(monaco.editor);
// @ts-expect-error
monaco.editor.defineTheme("dark", dark);
// @ts-expect-error
monaco.editor.defineTheme("light", light);
}
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
editor.updateOptions({
glyphMargin: true,
readOnly: true,
});
}}
theme={theme === "dark" ? "dark" : "light"}
/>
) : (
<Container
css={{
display: "flex",
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
{!snap.loading && router.isReady && (
<Text
css={{
mt: "-60px",
fontSize: "14px",
fontFamily: "$monospace",
maxWidth: "300px",
textAlign: "center",
}}
>
{`You haven't compiled any files yet, compile files on `}
<NextLink shallow href={`/develop/${router.query.slug}`} passHref>
<Link as="a">develop view</Link>
</NextLink>
</Text>
)}
</Container>
)}
<Container
css={{
display: "flex",
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
{!isContent ? (
NoContentView
) : !showContent ? (
CompiledStatView
) : (
<Editor
className="hooks-editor"
defaultLanguage={"wat"}
language={"wat"}
path={`file://tmp/c/${snap.files?.[snap.active]?.name}.wat`}
value={snap.files?.[snap.active]?.compiledWatContent || ""}
beforeMount={(monaco) => {
monaco.languages.register({ id: "wat" });
monaco.languages.setLanguageConfiguration("wat", wat.config);
monaco.languages.setMonarchTokensProvider("wat", wat.tokens);
if (!state.editorCtx) {
state.editorCtx = ref(monaco.editor);
// @ts-expect-error
monaco.editor.defineTheme("dark", dark);
// @ts-expect-error
monaco.editor.defineTheme("light", light);
}
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
editor.updateOptions({
glyphMargin: true,
readOnly: true,
});
}}
theme={theme === "dark" ? "dark" : "light"}
/>
)}
</Container>
</Box>
);
};

View File

@@ -62,7 +62,7 @@ import { styled } from "../stitches.config";
const DEFAULT_EXTENSION = ".c";
const ErrorText = styled(Text, {
color: "$crimson9",
color: "$error",
mt: "$1",
display: "block",
});

View File

@@ -16,9 +16,37 @@ const Flex = styled(Box, {
},
fluid: {
true: {
width: '100%'
}
}
width: "100%",
},
},
align: {
start: {
alignItems: "start",
},
center: {
alignItems: "center",
},
end: {
alignItems: "end",
},
},
justify: {
start: {
justifyContent: "start",
},
center: {
justifyContent: "center",
},
end: {
justifyContent: "end",
},
"space-between": {
justifyContent: "space-between",
},
"space-around": {
justifyContent: "space-around",
},
},
},
});

View File

@@ -5,6 +5,7 @@ import type monaco from "monaco-editor";
import { ArrowBendLeftUp } from "phosphor-react";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import uniqBy from "lodash.uniqby";
import Box from "./Box";
import Container from "./Container";
@@ -21,6 +22,8 @@ import { createLanguageClient, createWebSocket } from "../utils/languageClient";
import { listen } from "@codingame/monaco-jsonrpc";
import ReconnectingWebSocket from "reconnecting-websocket";
import docs from "../xrpl-hooks-docs/docs";
loader.config({
paths: {
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
@@ -36,8 +39,71 @@ const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
}
};
let decorations: { [key: string]: string[] } = {};
const setMarkers = (monacoE: typeof monaco) => {
// Get all the markers that are active at the moment,
// Also if same error is there twice, we can show the content
// only once (that's why we're using uniqBy)
const markers = uniqBy(
monacoE.editor
.getModelMarkers({})
// Filter out the markers that are hooks specific
.filter(
(marker) =>
typeof marker?.code === "string" &&
// Take only markers that starts with "hooks-"
marker?.code?.includes("hooks-")
),
"code"
);
// Get the active model (aka active file you're editing)
// const model = monacoE.editor?.getModel(
// monacoE.Uri.parse(`file:///work/c/${state.files?.[state.active]?.name}`)
// );
// console.log(state.active);
// Add decoration (aka extra hoverMessages) to markers in the
// exact same range (location) where the markers are
const models = monacoE.editor.getModels();
models.forEach((model) => {
decorations[model.id] = model?.deltaDecorations(
decorations?.[model.id] || [],
markers
.filter((marker) =>
marker?.resource.path
.split("/")
.includes(`${state.files?.[state.active]?.name}`)
)
.map((marker) => ({
range: new monacoE.Range(
marker.startLineNumber,
marker.startColumn,
marker.endLineNumber,
marker.endColumn
),
options: {
hoverMessage: {
value:
// Find the related hover message markdown from the
// /xrpl-hooks-docs/xrpl-hooks-docs-files.json file
// which was generated from rst files
(typeof marker.code === "string" &&
docs[marker?.code]?.toString()) ||
"",
supportHtml: true,
isTrusted: true,
},
},
}))
);
});
};
const HooksEditor = () => {
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
const monacoRef = useRef<typeof monaco>();
const subscriptionRef = useRef<ReconnectingWebSocket | null>(null);
const snap = useSnapshot(state);
const router = useRouter();
@@ -52,6 +118,11 @@ const HooksEditor = () => {
subscriptionRef?.current?.close();
};
}, []);
useEffect(() => {
if (monacoRef.current) {
setMarkers(monacoRef.current);
}
}, [snap.active]);
return (
<Box
css={{
@@ -133,6 +204,7 @@ const HooksEditor = () => {
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
editor.updateOptions({
glyphMargin: true,
lightbulb: {
@@ -145,6 +217,15 @@ const HooksEditor = () => {
saveFile();
}
);
// When the markers (errors/warnings from clangd language server) change
// Lets improve the markers by adding extra content to them from related
// md files
monaco.editor.onDidChangeMarkers(() => {
if (monacoRef.current) {
setMarkers(monacoRef.current);
}
});
validateWritability(editor);
}}
theme={theme === "dark" ? "dark" : "light"}
@@ -173,7 +254,7 @@ const HooksEditor = () => {
fontFamily: "$monospace",
}}
>
Click the link above to create a your file
Click the link above to create your file
</Text>
</Box>
</Box>

View File

@@ -3,6 +3,14 @@ import { styled } from "../stitches.config";
const StyledLink = styled("a", {
color: "CurrentColor",
textDecoration: "underline",
cursor: 'pointer',
variants: {
highlighted: {
true: {
color: '$blue9'
}
}
}
});
export default StyledLink;

View File

@@ -1,17 +1,15 @@
import React, { useRef, useLayoutEffect, ReactNode } from "react";
import { useRef, useLayoutEffect, ReactNode, FC, useState, useCallback } from "react";
import { Notepad, Prohibit } from "phosphor-react";
import useStayScrolled from "react-stay-scrolled";
import NextLink from "next/link";
import Container from "./Container";
import Box from "./Box";
import Flex from "./Flex";
import LogText from "./LogText";
import { ILog } from "../state";
import Text from "./Text";
import Button from "./Button";
import Heading from "./Heading";
import Link from "./Link";
import state, { ILog } from "../state";
import { Pre, Link, Heading, Button, Text, Flex, Box } from ".";
import regexifyString from "regexify-string";
import { useSnapshot } from "valtio";
import { AccountDialog } from "./Accounts";
interface ILogBox {
title: string;
@@ -21,14 +19,7 @@ interface ILogBox {
enhanced?: boolean;
}
const LogBox: React.FC<ILogBox> = ({
title,
clearLog,
logs,
children,
renderNav,
enhanced,
}) => {
const LogBox: FC<ILogBox> = ({ title, clearLog, logs, children, renderNav, enhanced }) => {
const logRef = useRef<HTMLPreElement>(null);
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
@@ -55,6 +46,7 @@ const LogBox: React.FC<ILogBox> = ({
}}
>
<Flex
fluid
css={{
height: "48px",
alignItems: "center",
@@ -78,7 +70,15 @@ const LogBox: React.FC<ILogBox> = ({
>
<Notepad size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
</Heading>
{renderNav?.()}
<Flex
row
align="center"
css={{
width: "50%", // TODO make it max without breaking layout!
}}
>
{renderNav?.()}
</Flex>
<Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
{clearLog && (
<Button ghost size="xs" onClick={clearLog}>
@@ -117,17 +117,11 @@ const LogBox: React.FC<ILogBox> = ({
backgroundColor: enhanced ? "$backgroundAlt" : undefined,
},
},
p: enhanced ? "$2 $1" : undefined,
p: enhanced ? "$1" : undefined,
my: enhanced ? "$1" : undefined,
}}
>
<LogText variant={log.type}>
{log.message}{" "}
{log.link && (
<NextLink href={log.link} shallow passHref>
<Link as="a">{log.linkText}</Link>
</NextLink>
)}
</LogText>
<Log {...log} />
</Box>
))}
{children}
@@ -137,4 +131,74 @@ const LogBox: React.FC<ILogBox> = ({
);
};
export const Log: FC<ILog> = ({
type,
timestamp: timestamp,
message: _message,
link,
linkText,
defaultCollapsed,
jsonData: _jsonData,
}) => {
const [expanded, setExpanded] = useState(!defaultCollapsed);
const { accounts } = useSnapshot(state);
const [dialogAccount, setDialogAccount] = useState<string | null>(null);
const enrichAccounts = useCallback(
(str?: string): ReactNode => {
if (!str || !accounts.length) return null;
const pattern = `(${accounts.map(acc => acc.address).join("|")})`;
const res = regexifyString({
pattern: new RegExp(pattern, "gim"),
decorator: (match, idx) => {
const name = accounts.find(acc => acc.address === match)?.name;
return (
<Link
key={match + idx}
as="a"
onClick={() => setDialogAccount(match)}
title={match}
highlighted
>
{name || match}
</Link>
);
},
input: str,
});
return <>{res}</>;
},
[accounts]
);
_message = _message.trim().replace(/\n /gi, "\n");
const message = enrichAccounts(_message);
const jsonData = enrichAccounts(_jsonData);
return (
<>
<AccountDialog
setActiveAccountAddress={setDialogAccount}
activeAccountAddress={dialogAccount}
/>
<LogText variant={type}>
{timestamp && <Text muted monospace>{timestamp} </Text>}
<Pre>{message} </Pre>
{link && (
<NextLink href={link} shallow passHref>
<Link as="a">{linkText}</Link>
</NextLink>
)}
{jsonData && (
<Link onClick={() => setExpanded(!expanded)} as="a">
{expanded ? "Collapse" : "Expand"}
</Link>
)}
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
</LogText>
</>
);
};
export default LogBox;

View File

@@ -11,13 +11,13 @@ const Text = styled("span", {
color: "$text",
},
warning: {
color: "$amber11",
color: "$warning",
},
error: {
color: "$crimson11",
color: "$error",
},
success: {
color: "$grass11",
color: "$success",
},
},
capitalize: {

View File

@@ -312,7 +312,7 @@ const Navigation = () => {
href={`/develop/${templateFileIds.peggy}`}
>
<Heading>Peggy</Heading>
<Text>An oracle based stabe coin hook</Text>
<Text>An oracle based stable coin hook</Text>
</PanelBox>
</Flex>
</Flex>

27
components/Pre.tsx Normal file
View File

@@ -0,0 +1,27 @@
import { styled } from "../stitches.config";
const Pre = styled("span", {
m: 0,
wordBreak: "break-all",
fontFamily: '$monospace',
whiteSpace: 'pre-wrap',
variants: {
fluid: {
true: {
width: "100%",
},
},
line: {
true: {
whiteSpace: 'pre-line'
}
},
block: {
true: {
display: 'block'
}
}
},
});
export default Pre;

View File

@@ -19,7 +19,7 @@ import { Plus, X } from "phosphor-react";
import { styled } from "../stitches.config";
const ErrorText = styled(Text, {
color: "$crimson9",
color: "$error",
mt: "$1",
display: "block",
});

View File

@@ -14,6 +14,11 @@ const Text = styled("span", {
true: {
color: '$mauve9'
}
},
monospace: {
true: {
fontFamily: '$monospace'
}
}
}
});

View File

@@ -1,15 +1,17 @@
export { default as Flex } from './Flex'
export { default as Container } from './Container'
export { default as Heading } from './Heading'
export { default as Stack } from './Stack'
export { default as Text } from './Text'
export { default as Input } from './Input'
export { default as Select } from './Select'
export * from './Tabs'
export * from './AlertDialog'
export { default as Box } from './Box'
export { default as Button } from './Button'
export { default as ButtonGroup } from './ButtonGroup'
export { default as DeployFooter } from './DeployFooter'
export * from './Dialog'
export * from './DropdownMenu'
export { default as Flex } from "./Flex";
export { default as Link } from "./Link";
export { default as Container } from "./Container";
export { default as Heading } from "./Heading";
export { default as Stack } from "./Stack";
export { default as Text } from "./Text";
export { default as Input } from "./Input";
export { default as Select } from "./Select";
export * from "./Tabs";
export * from "./AlertDialog";
export { default as Box } from "./Box";
export { default as Button } from "./Button";
export { default as Pre } from "./Pre";
export { default as ButtonGroup } from "./ButtonGroup";
export { default as DeployFooter } from "./DeployFooter";
export * from "./Dialog";
export * from "./DropdownMenu";

View File

@@ -11,6 +11,10 @@ module.exports = {
if (!isServer) {
config.resolve.fallback.fs = false;
}
config.module.rules.push({
test: /\.md$/,
use: "raw-loader",
});
return config;
},
};

View File

@@ -23,7 +23,10 @@
"base64-js": "^1.5.1",
"dinero.js": "^1.9.1",
"file-saver": "^2.0.5",
"filesize": "^8.0.7",
"javascript-time-ago": "^2.3.11",
"jszip": "^3.7.1",
"lodash.uniqby": "^4.7.0",
"monaco-editor": "^0.30.1",
"next": "^12.0.4",
"next-auth": "^4.0.0-beta.5",
@@ -43,7 +46,9 @@
"react-select": "^5.2.1",
"react-split": "^2.0.14",
"react-stay-scrolled": "^7.4.0",
"react-time-ago": "^7.1.9",
"reconnecting-websocket": "^4.4.0",
"regexify-string": "^1.0.17",
"valtio": "^1.2.5",
"vscode-languageserver": "^7.0.0",
"vscode-uri": "^3.0.2",
@@ -54,10 +59,12 @@
"devDependencies": {
"@types/dinero.js": "^1.9.0",
"@types/file-saver": "^2.0.4",
"@types/lodash.uniqby": "^4.7.6",
"@types/pako": "^1.0.2",
"@types/react": "17.0.31",
"eslint": "7.32.0",
"eslint-config-next": "11.1.2",
"raw-loader": "^4.0.2",
"typescript": "4.4.4"
}
}
}

View File

@@ -13,6 +13,11 @@ import Navigation from "../components/Navigation";
import { fetchFiles } from "../state/actions";
import state from "../state";
import TimeAgo from "javascript-time-ago";
import en from "javascript-time-ago/locale/en.json";
import { useSnapshot } from "valtio";
TimeAgo.addDefaultLocale(en);
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
const router = useRouter();
const slug = router.query?.slug;
@@ -21,15 +26,29 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
const origin = "https://xrpl-hooks-ide.vercel.app"; // TODO: Change when site is deployed
const shareImg = "/share-image.png";
const snap = useSnapshot(state);
useEffect(() => {
if (gistId && router.isReady) {
fetchFiles(gistId);
} else {
if (!gistId && router.isReady && !router.pathname.includes("/sign-in")) {
if (
!gistId &&
router.isReady &&
!router.pathname.includes("/sign-in") &&
!snap.files.length &&
!snap.mainModalShowed
) {
state.mainModalOpen = true;
state.mainModalShowed = true;
}
}
}, [gistId, router.isReady, router.pathname]);
}, [
gistId,
router.isReady,
router.pathname,
snap.files,
snap.mainModalShowed,
]);
return (
<>

View File

@@ -1,8 +1,9 @@
import React from "react";
import dynamic from "next/dynamic";
import React from "react";
import Split from "react-split";
import { useSnapshot } from "valtio";
import state from "../../state";
import Split from "react-split";
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
const DeployEditor = dynamic(() => import("../../components/DeployEditor"), {
ssr: false,
@@ -17,21 +18,22 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
});
const Deploy = () => {
const snap = useSnapshot(state);
const { deployLogs } = useSnapshot(state);
return (
<Split
direction="vertical"
gutterSize={4}
gutterAlign="center"
sizes={[40, 60]}
sizes={getSplit("deployVertical") || [40, 60]}
style={{ height: "calc(100vh - 60px)" }}
onDragEnd={(e) => saveSplit("deployVertical", e)}
>
<main style={{ display: "flex", flex: 1, position: "relative" }}>
<DeployEditor />
</main>
<Split
direction="horizontal"
sizes={[50, 50]}
sizes={getSplit("deployHorizontal") || [50, 50]}
minSize={[320, 160]}
gutterSize={4}
gutterAlign="center"
@@ -41,6 +43,7 @@ const Deploy = () => {
width: "100%",
height: "100%",
}}
onDragEnd={(e) => saveSplit("deployHorizontal", e)}
>
<div style={{ alignItems: "stretch", display: "flex" }}>
<Accounts />
@@ -48,7 +51,7 @@ const Deploy = () => {
<div>
<LogBox
title="Deploy Log"
logs={snap.deployLogs}
logs={deployLogs}
clearLog={() => (state.deployLogs = [])}
/>
</div>

View File

@@ -1,14 +1,15 @@
import dynamic from "next/dynamic";
import { useSnapshot } from "valtio";
import Hotkeys from "react-hot-keys";
import { Play } from "phosphor-react";
import Split from "react-split";
import type { NextPage } from "next";
import { compileCode } from "../../state/actions";
import state from "../../state";
import Button from "../../components/Button";
import dynamic from "next/dynamic";
import { Play } from "phosphor-react";
import Hotkeys from "react-hot-keys";
import Split from "react-split";
import { useSnapshot } from "valtio";
import Box from "../../components/Box";
import Button from "../../components/Button";
import state from "../../state";
import { compileCode } from "../../state/actions";
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
const HooksEditor = dynamic(() => import("../../components/HooksEditor"), {
ssr: false,
@@ -24,11 +25,12 @@ const Home: NextPage = () => {
return (
<Split
direction="vertical"
sizes={[70, 30]}
sizes={getSplit("developVertical") || [70, 30]}
minSize={[100, 100]}
gutterAlign="center"
gutterSize={4}
style={{ height: "calc(100vh - 60px)" }}
onDragEnd={(e) => saveSplit("developVertical", e)}
>
<main style={{ display: "flex", flex: 1, position: "relative" }}>
<HooksEditor />

View File

@@ -1,22 +1,17 @@
import {
Container,
Flex,
Box,
Tabs,
Tab,
Input,
Select,
Text,
Button,
} from "../../components";
import { Play } from "phosphor-react";
import dynamic from "next/dynamic";
import { useSnapshot } from "valtio";
import { Play } from "phosphor-react";
import { FC, useCallback, useEffect, useState } from "react";
import Split from "react-split";
import { useSnapshot } from "valtio";
import {
Box, Button, Container,
Flex, Input,
Select, Tab, Tabs, Text
} from "../../components";
import transactionsData from "../../content/transactions.json";
import state from "../../state";
import { sendTransaction } from "../../state/actions";
import { useCallback, useEffect, useState, FC } from "react";
import transactionsData from "../../content/transactions.json";
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
const DebugStream = dynamic(() => import("../../components/DebugStream"), {
ssr: false,
@@ -349,16 +344,17 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
};
const Test = () => {
const snap = useSnapshot(state);
const { transactionLogs } = useSnapshot(state);
const [tabHeaders, setTabHeaders] = useState<string[]>(["test1.json"]);
return (
<Container css={{ px: 0 }}>
<Split
direction="vertical"
sizes={[50, 50]}
sizes={getSplit("testVertical") || [50, 50]}
gutterSize={4}
gutterAlign="center"
style={{ height: "calc(100vh - 60px)" }}
onDragEnd={(e) => saveSplit("testVertical", e)}
>
<Flex
row
@@ -370,7 +366,7 @@ const Test = () => {
>
<Split
direction="horizontal"
sizes={[50, 50]}
sizes={getSplit("testHorizontal") || [50, 50]}
minSize={[180, 320]}
gutterSize={4}
gutterAlign="center"
@@ -380,6 +376,7 @@ const Test = () => {
width: "100%",
height: "100%",
}}
onDragEnd={(e) => saveSplit("testHorizontal", e)}
>
<Box css={{ width: "55%", px: "$2" }}>
<Tabs
@@ -428,7 +425,7 @@ const Test = () => {
>
<LogBox
title="Development Log"
logs={snap.transactionLogs}
logs={transactionLogs}
clearLog={() => (state.transactionLogs = [])}
/>
</Box>

4
raw-loader.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module "*.md" {
const content: string;
export default content;
};

View File

@@ -66,6 +66,7 @@ export const compileCode = async (activeId: number) => {
// Decode base64 encoded wasm that is coming back from the endpoint
const bufferData = await decodeBinary(json.output);
state.files[state.active].compiledContent = ref(bufferData);
state.files[state.active].lastCompiled = new Date();
// Import wabt from and create human readable version of wasm file and
// put it into state
import("wabt").then((wabt) => {

View File

@@ -1,8 +1,17 @@
import state, { IFile } from '../index';
/* Initializes empty file to global state */
const languageMapping = {
'ts': 'typescript',
'js': 'javascript',
'md': 'markdown',
'c': 'c',
'h': 'c',
'other': ''
} /* Initializes empty file to global state */
export const createNewFile = (name: string) => {
const emptyFile: IFile = { name, language: "c", content: "" };
const tempName = name.split('.');
const fileExt = tempName[tempName.length - 1] || 'other';
const emptyFile: IFile = { name, language: languageMapping[fileExt as 'ts' | 'js' | 'md' | 'c' | 'h' | 'other'], content: "" };
state.files.push(emptyFile);
state.active = state.files.length - 1;
};

View File

@@ -0,0 +1,15 @@
import { snapshot } from "valtio"
import state from ".."
export type SplitSize = number[]
export const saveSplit = (splitId: string, event: SplitSize) => {
state.splits[splitId] = event
}
export const getSplit = (splitId: string): SplitSize | null => {
const { splits } = snapshot(state)
const split = splits[splitId]
return split ? split : null
}

View File

@@ -1,7 +1,8 @@
import { proxy, ref, subscribe } from "valtio";
import { devtools } from 'valtio/utils'
import type monaco from "monaco-editor";
import { proxy, ref, subscribe } from "valtio";
import { devtools } from 'valtio/utils';
import { XrplClient } from "xrpl-client";
import { SplitSize } from "./actions/persistSplits";
export interface IFile {
name: string;
@@ -9,6 +10,7 @@ export interface IFile {
content: string;
compiledContent?: ArrayBuffer | null;
compiledWatContent?: string | null;
lastCompiled?: Date
}
export interface FaucetAccountRes {
@@ -32,8 +34,11 @@ export interface IAccount {
export interface ILog {
type: "error" | "warning" | "log" | "success";
message: string;
jsonData?: any,
timestamp?: string;
link?: string;
linkText?: string;
defaultCollapsed?: boolean
}
export interface IState {
@@ -50,14 +55,17 @@ export interface IState {
logs: ILog[];
deployLogs: ILog[];
transactionLogs: ILog[];
debugLogs: ILog[];
editorCtx?: typeof monaco.editor;
editorSettings: {
tabSize: number;
};
splits: {
[id: string]: SplitSize
};
client: XrplClient | null;
clientStatus: "offline" | "online";
mainModalOpen: boolean;
mainModalShowed: boolean;
accounts: IAccount[];
}
@@ -73,7 +81,6 @@ let initialState: IState = {
logs: [],
deployLogs: [],
transactionLogs: [],
debugLogs: [],
editorCtx: undefined,
gistId: undefined,
gistOwner: undefined,
@@ -83,14 +90,19 @@ let initialState: IState = {
editorSettings: {
tabSize: 2,
},
splits: {},
client: null,
clientStatus: "offline" as "offline",
mainModalOpen: false,
mainModalShowed: false,
accounts: [],
};
let localStorageAccounts: string | null = null;
let initialAccounts: IAccount[] = [];
// TODO: What exactly should we store in localStorage? editorSettings, splits, accounts?
// Check if there's a persited accounts in localStorage
if (typeof window !== "undefined") {
try {
@@ -139,4 +151,4 @@ if (typeof window !== "undefined") {
}
});
}
export default state
export default state

View File

@@ -19,6 +19,8 @@ import {
mauveDark,
amberDark,
purpleDark,
red,
redDark,
} from "@radix-ui/colors";
export const {
@@ -41,11 +43,16 @@ export const {
...mauve,
...amber,
...purple,
...red,
accent: "#9D2DFF",
background: "$gray1",
backgroundAlt: "$gray4",
text: "$gray12",
textMuted: "$gray10",
primary: "$plum",
error: '$red9',
warning: '$amber11',
success: "$grass11",
white: "white",
black: "black",
deep: "rgb(244, 244, 244)",
@@ -348,6 +355,7 @@ export const darkTheme = createTheme("dark", {
...mauveDark,
...amberDark,
...purpleDark,
...redDark,
deep: "rgb(10, 10, 10)",
// backgroundA: transparentize(0.1, grayDark.gray1),
},

View File

@@ -13,6 +13,10 @@ body,
box-sizing: border-box;
}
.monaco-hover p {
margin-bottom: 10px !important;
}
.gutter {
position: relative;
transition: border-color 0.3s, background-color 0.3s;
@@ -39,3 +43,14 @@ html.light .gutter-vertical:hover {
html.light .gutter-horizontal:hover {
background-color: rgba(0, 0, 0, 0.25);
}
/* Adjust Monaco tooltip stylings */
.markdown-hover h3 {
margin: 0;
}
.monaco-editor .monaco-hover hr {
margin: 8px 0;
}
.monaco-editor .monaco-hover {
z-index: 9999;
}

View File

@@ -26,6 +26,7 @@
"**/*.tsx"
],
"exclude": [
"node_modules"
"node_modules",
"*.md"
]
}

21
utils/json.ts Normal file
View File

@@ -0,0 +1,21 @@
export const extractJSON = (str?: string) => {
if (!str) return
let firstOpen = 0, firstClose = 0, candidate = '';
firstOpen = str.indexOf('{', firstOpen + 1);
do {
firstClose = str.lastIndexOf('}');
if (firstClose <= firstOpen) {
return;
}
do {
candidate = str.substring(firstOpen, firstClose + 1);
try {
let result = JSON.parse(candidate);
return { result, start: firstOpen < 0 ? 0 : firstOpen, end: firstClose }
}
catch (e) { }
firstClose = str.substring(0, firstClose).lastIndexOf('}');
} while (firstClose > firstOpen);
firstOpen = str.indexOf('{', firstOpen + 1);
} while (firstOpen != -1);
}

714
utils/wat-highlight.ts Normal file
View File

@@ -0,0 +1,714 @@
// 'WebAssembly Text Format' Monarch language
import type monaco from 'monaco-editor';
const WebAssemblyTextLanguage: { config: monaco.languages.LanguageConfiguration, tokens: monaco.languages.IMonarchLanguage } = {
config: {
brackets: [
["(", ")"],
["if", "end"],
["loop", "end"],
["block", "end"],
],
autoClosingPairs: [
{ open: "(", close: ")" },
{ open: "if", close: "end" },
{ open: "loop", close: "end" },
{ open: "block", close: "end" },
],
surroundingPairs: [
{ open: "(", close: ")" },
{ open: "if", close: "end" },
{ open: "loop", close: "end" },
{ open: "block", close: "end" },
],
},
tokens: {
keywords: [
"module",
"import",
"export",
"memory",
"data",
"table",
"elem",
"start",
"func",
"tag",
"type",
"param",
"result",
"global",
"local",
"mut",
"struct",
"array",
"field",
],
types: [
"i8",
"i16",
"i32",
"i64",
"f32",
"f64",
"v128",
"i31ref",
"eqref",
"anyref",
"dataref",
"externref",
"funcref",
"exnref",
"extern",
"null",
"any",
"eq",
],
instructions: [
"pop",
"nop",
"drop",
"data.drop",
"elem.drop",
"local.get",
"local.set",
"local.tee",
"global.get",
"global.set",
"tuple.make",
"tuple.extract",
"select",
"v128.const",
"v128.and",
"v128.or",
"v128.xor",
"v128.not",
"v128.andnot",
"v128.bitselect",
"v128.load",
"v128.load8x8_s",
"v128.load8x8_u",
"v128.load16x4_s",
"v128.load16x4_u",
"v128.load32x2_s",
"v128.load32x2_u",
"v128.load8_lane",
"v128.load16_lane",
"v128.load32_lane",
"v128.load64_lane",
"v128.load8_splat",
"v128.load16_splat",
"v128.load32_splat",
"v128.load64_splat",
"v128.load32_zero",
"v128.load64_zero",
"v128.store",
"v128.store8_lane",
"v128.store16_lane",
"v128.store32_lane",
"v128.store64_lane",
"v128.any_true",
"i8x16.shuffle",
"i8x16.swizzle",
"i8x16.bitmask",
"i8x16.splat",
"i8x16.popcnt",
"i8x16.replace_lane",
"i8x16.extract_lane_s",
"i8x16.extract_lane_u",
"i8x16.all_true",
"i8x16.abs",
"i8x16.add",
"i8x16.add_sat_s",
"i8x16.add_sat_u",
"i8x16.sub",
"i8x16.sub_sat_s",
"i8x16.sub_sat_u",
"i8x16.mul",
"i8x16.neg",
"i8x16.shl",
"i8x16.shr_s",
"i8x16.shr_u",
"i8x16.eq",
"i8x16.ne",
"i8x16.lt_s",
"i8x16.lt_u",
"i8x16.le_s",
"i8x16.le_u",
"i8x16.gt_s",
"i8x16.gt_u",
"i8x16.ge_s",
"i8x16.ge_u",
"i8x16.min_s",
"i8x16.min_u",
"i8x16.max_s",
"i8x16.max_u",
"i8x16.avgr_u",
"i8x16.narrow_i16x8_s",
"i8x16.narrow_i16x8_u",
"i16x8.bitmask",
"i16x8.splat",
"i16x8.load_8x8_s",
"i16x8.load_8x8_u",
"i16x8.replace_lane",
"i16x8.extract_lane_s",
"i16x8.extract_lane_u",
"i16x8.extend_low_i8x16_s",
"i16x8.extend_high_i8x16_s",
"i16x8.extend_low_i8x16_u",
"i16x8.extend_high_i8x16_u",
"i16x8.all_true",
"i16x8.abs",
"i16x8.add",
"i16x8.add_sat_s",
"i16x8.add_sat_u",
"i16x8.extadd_pairwise_i8x16_s",
"i16x8.extadd_pairwise_i8x16_u",
"i16x8.sub",
"i16x8.sub_sat_s",
"i16x8.sub_sat_u",
"i16x8.q15mulr_sat_s",
"i16x8.mul",
"i16x8.extmul_low_i8x16_s",
"i16x8.extmul_high_i8x16_s",
"i16x8.extmul_low_i8x16_u",
"i16x8.extmul_high_i8x16_u",
"i16x8.neg",
"i16x8.shl",
"i16x8.shr_s",
"i16x8.shr_u",
"i16x8.eq",
"i16x8.ne",
"i16x8.lt_s",
"i16x8.lt_u",
"i16x8.le_s",
"i16x8.le_u",
"i16x8.gt_s",
"i16x8.gt_u",
"i16x8.ge_s",
"i16x8.ge_u",
"i16x8.min_s",
"i16x8.min_u",
"i16x8.max_s",
"i16x8.max_u",
"i16x8.avgr_u",
"i16x8.narrow_i32x4_s",
"i16x8.narrow_i32x4_u",
"i32x4.bitmask",
"i32x4.splat",
"i32x4.load_16x4_s",
"i32x4.load_16x4_u",
"i32x4.replace_lane",
"i32x4.extract_lane",
"i32x4.extend_low_i16x8_s",
"i32x4.extend_high_i16x8_s",
"i32x4.extend_low_i16x8_u",
"i32x4.extend_high_i16x8_u",
"i32x4.all_true",
"i32x4.abs",
"i32x4.add",
"i32x4.extadd_pairwise_i16x8_s",
"i32x4.extadd_pairwise_i16x8_u",
"i32x4.sub",
"i32x4.mul",
"i32x4.extmul_low_i16x8_s",
"i32x4.extmul_high_i16x8_s",
"i32x4.extmul_low_i16x8_u",
"i32x4.extmul_high_i16x8_u",
"i32x4.neg",
"i32x4.shl",
"i32x4.shr_s",
"i32x4.shr_u",
"i32x4.eq",
"i32x4.ne",
"i32x4.lt_s",
"i32x4.lt_u",
"i32x4.le_s",
"i32x4.le_u",
"i32x4.gt_s",
"i32x4.gt_u",
"i32x4.ge_s",
"i32x4.ge_u",
"i32x4.min_s",
"i32x4.min_u",
"i32x4.max_s",
"i32x4.max_u",
"i32x4.trunc_sat_f32x4_s",
"i32x4.trunc_sat_f32x4_u",
"i32x4.trunc_sat_f64x2_s_zero",
"i32x4.trunc_sat_f64x2_u_zero",
"i32x4.dot_i16x8_s",
"i64x2.bitmask",
"i64x2.splat",
"i64x2.load32x2_s",
"i64x2.load32x2_u",
"i64x2.replace_lane",
"i64x2.extract_lane",
"i64x2.extend_low_i32x4_s",
"i64x2.extend_high_i32x4_s",
"i64x2.extend_low_i32x4_u",
"i64x2.extend_high_i32x4_u",
"i64x2.all_true",
"i64x2.abs",
"i64x2.add",
"i64x2.sub",
"i64x2.mul",
"i64x2.neg",
"i64x2.shl",
"i64x2.shr_s",
"i64x2.shr_u",
"f32x4.splat",
"f32x4.replace_lane",
"f32x4.extract_lane",
"f32x4.add",
"f32x4.sub",
"f32x4.mul",
"i64x2.extmul_low_i32x4_s",
"i64x2.extmul_high_i32x4_s",
"i64x2.extmul_low_i32x4_u",
"i64x2.extmul_high_i32x4_u",
"i64x2.eq",
"i64x2.ne",
"i64x2.lt_s",
"i64x2.le_s",
"i64x2.gt_s",
"i64x2.ge_s",
"f32x4.neg",
"f32x4.eq",
"f32x4.ne",
"f32x4.lt",
"f32x4.le",
"f32x4.gt",
"f32x4.ge",
"f32x4.abs",
"f32x4.min",
"f32x4.pmin",
"f32x4.max",
"f32x4.pmax",
"f32x4.div",
"f32x4.sqrt",
"f32x4.ceil",
"f32x4.floor",
"f32x4.trunc",
"f32x4.nearest",
"f32x4.demote_f64x2_zero",
"f32x4.convert_i32x4_s",
"f32x4.convert_i32x4_u",
"f64x2.splat",
"f64x2.replace_lane",
"f64x2.extract_lane",
"f64x2.add",
"f64x2.sub",
"f64x2.mul",
"f64x2.neg",
"f64x2.eq",
"f64x2.ne",
"f64x2.lt",
"f64x2.le",
"f64x2.gt",
"f64x2.ge",
"f64x2.abs",
"f64x2.min",
"f64x2.max",
"f64x2.pmin",
"f64x2.pmax",
"f64x2.div",
"f64x2.sqrt",
"f64x2.ceil",
"f64x2.floor",
"f64x2.trunc",
"f64x2.nearest",
"f64x2.promote_low_f32x4",
"f64x2.convert_low_i32x4_s",
"f64x2.convert_low_i32x4_u",
"i32.atomic.load",
"i32.atomic.load8_u",
"i32.atomic.load16_u",
"i32.atomic.store",
"i32.atomic.store8",
"i32.atomic.store16",
"i32.atomic.rmw.add",
"i32.atomic.rmw.sub",
"i32.atomic.rmw.and",
"i32.atomic.rmw.or",
"i32.atomic.rmw.xor",
"i32.atomic.rmw.xchg",
"i32.atomic.rmw.cmpxchg",
"i32.atomic.rmw8.add_u",
"i32.atomic.rmw8.sub_u",
"i32.atomic.rmw8.and_u",
"i32.atomic.rmw8.or_u",
"i32.atomic.rmw8.xor_u",
"i32.atomic.rmw8.xchg_u",
"i32.atomic.rmw8.cmpxchg_u",
"i32.atomic.rmw16.add_u",
"i32.atomic.rmw16.sub_u",
"i32.atomic.rmw16.and_u",
"i32.atomic.rmw16.or_u",
"i32.atomic.rmw16.xor_u",
"i32.atomic.rmw16.xchg_u",
"i32.atomic.rmw16.cmpxchg_u",
"i64.atomic.load",
"i64.atomic.load8_u",
"i64.atomic.load16_u",
"i64.atomic.load32_u",
"i64.atomic.store",
"i64.atomic.store8",
"i64.atomic.store16",
"i64.atomic.store32",
"i64.atomic.rmw.add",
"i64.atomic.rmw.sub",
"i64.atomic.rmw.and",
"i64.atomic.rmw.or",
"i64.atomic.rmw.xor",
"i64.atomic.rmw.xchg",
"i64.atomic.rmw.cmpxchg",
"i64.atomic.rmw8.add_u",
"i64.atomic.rmw8.sub_u",
"i64.atomic.rmw8.and_u",
"i64.atomic.rmw8.or_u",
"i64.atomic.rmw8.xor_u",
"i64.atomic.rmw8.xchg_u",
"i64.atomic.rmw8.cmpxchg_u",
"i64.atomic.rmw16.add_u",
"i64.atomic.rmw16.sub_u",
"i64.atomic.rmw16.and_u",
"i64.atomic.rmw16.or_u",
"i64.atomic.rmw16.xor_u",
"i64.atomic.rmw16.xchg_u",
"i64.atomic.rmw16.cmpxchg_u",
"i64.atomic.rmw32.add_u",
"i64.atomic.rmw32.sub_u",
"i64.atomic.rmw32.and_u",
"i64.atomic.rmw32.or_u",
"i64.atomic.rmw32.xor_u",
"i64.atomic.rmw32.xchg_u",
"i64.atomic.rmw32.cmpxchg_u",
"atomic.fence",
"func.bind",
"ref",
"ref.eq",
"ref.null",
"ref.is_null",
"ref.is_func",
"ref.is_data",
"ref.is_i31",
"ref.as_func",
"ref.as_non_null",
"ref.as_data",
"ref.as_i31",
"ref.func",
"ref.cast",
"ref.cast_static",
"ref.test",
"ref.test_static",
"table.get",
"table.set",
"table.size",
"table.grow",
"table.fill",
"table.init",
"table.copy",
"throw",
"rethrow",
"i32.load",
"i32.load8_s",
"i32.load8_u",
"i32.load16_s",
"i32.load16_u",
"i32.store",
"i32.store8",
"i32.store16",
"i32.const",
"i32.eqz",
"i32.eq",
"i32.ne",
"i32.lt_s",
"i32.lt_u",
"i32.le_s",
"i32.le_u",
"i32.gt_s",
"i32.gt_u",
"i32.ge_s",
"i32.ge_u",
"i32.clz",
"i32.ctz",
"i32.popcnt",
"i32.add",
"i32.sub",
"i32.mul",
"i32.div_s",
"i32.div_u",
"i32.rem_s",
"i32.rem_u",
"i32.and",
"i32.or",
"i32.xor",
"i32.shl",
"i32.shr_s",
"i32.shr_u",
"i32.rotl",
"i32.rotr",
"i32.wrap_i64",
"i32.trunc_f32_s",
"i32.trunc_f32_u",
"i32.trunc_f64_s",
"i32.trunc_f64_u",
"i32.reinterpret_f32",
"i64.load",
"i64.load8_s",
"i64.load8_u",
"i64.load16_s",
"i64.load16_u",
"i64.load32_s",
"i64.load32_u",
"i64.store",
"i64.store8",
"i64.store16",
"i64.store32",
"i64.const",
"i64.eqz",
"i64.eq",
"i64.ne",
"i64.lt_s",
"i64.lt_u",
"i64.le_s",
"i64.le_u",
"i64.gt_s",
"i64.gt_u",
"i64.ge_s",
"i64.ge_u",
"i64.clz",
"i64.ctz",
"i64.popcnt",
"i64.add",
"i64.sub",
"i64.mul",
"i64.div_s",
"i64.div_u",
"i64.rem_s",
"i64.rem_u",
"i64.and",
"i64.or",
"i64.xor",
"i64.shl",
"i64.shr_s",
"i64.shr_u",
"i64.rotl",
"i64.rotr",
"i64.extend_i32_s",
"i64.extend_i32_u",
"i64.trunc_f32_s",
"i64.trunc_f32_u",
"i64.trunc_f64_s",
"i64.trunc_f64_u",
"i64.reinterpret_f64",
"f32.load",
"f32.store",
"f32.const",
"f32.eq",
"f32.ne",
"f32.lt",
"f32.le",
"f32.gt",
"f32.ge",
"f32.abs",
"f32.neg",
"f32.ceil",
"f32.floor",
"f32.trunc",
"f32.nearest",
"f32.sqrt",
"f32.add",
"f32.sub",
"f32.mul",
"f32.div",
"f32.min",
"f32.max",
"f32.copysign",
"f32.convert_i32_s",
"f32.convert_i32_u",
"f32.convert_i64_s",
"f32.convert_i64_u",
"f32.demote_f64",
"f32.reinterpret_i32",
"f64.load",
"f64.store",
"f64.const",
"f64.eq",
"f64.ne",
"f64.lt",
"f64.le",
"f64.gt",
"f64.ge",
"f64.abs",
"f64.neg",
"f64.ceil",
"f64.floor",
"f64.trunc",
"f64.nearest",
"f64.sqrt",
"f64.add",
"f64.sub",
"f64.mul",
"f64.div",
"f64.min",
"f64.max",
"f64.copysign",
"f64.convert_i32_s",
"f64.convert_i32_u",
"f64.convert_i64_s",
"f64.convert_i64_u",
"f64.promote_f32",
"f64.reinterpret_i64",
"i32.extend8_s",
"i32.extend16_s",
"i64.extend8_s",
"i64.extend16_s",
"i64.extend32_s",
"i32.trunc_sat_f32_s",
"i32.trunc_sat_f32_u",
"i32.trunc_sat_f64_s",
"i32.trunc_sat_f64_u",
"i64.trunc_sat_f32_s",
"i64.trunc_sat_f32_u",
"i64.trunc_sat_f64_s",
"i64.trunc_sat_f64_u",
"memory.size",
"memory.grow",
"memory.copy",
"memory.fill",
"memory.init",
"memory.atomic.notify",
"memory.atomic.wait32",
"memory.atomic.wait64",
"i31.new",
"i31.get_u",
"i31.get_s",
"array.new",
"array.new_default",
"array.init",
"array.init_static",
"array.get",
"array.get_s",
"array.get_u",
"array.set",
"array.len",
"array.copy",
"struct.new",
"struct.new_default",
"struct.new_with_rtt",
"struct.new_default_with_rtt",
"struct.get",
"struct.get_s",
"struct.get_u",
"struct.set",
"rtt.canon",
"rtt.sub",
"rtt.fresh_sub",
],
controlInstructions: [
"block",
"loop",
"if",
"else",
"then",
"end",
"do",
"let",
"br",
"br_if",
"br_table",
"br_on_exn",
"br_on_null",
"br_on_non_null",
"br_on_cast",
"br_on_cast_static",
"br_on_cast_fail",
"br_on_cast_static_fail",
"br_on_func",
"br_on_non_func",
"br_on_data",
"br_on_non_data",
"br_on_i31",
"br_on_non_i31",
"call",
"call_indirect",
"call_ref",
"return",
"return_call",
"return_call_indirect",
"return_call_ref",
"try",
"catch",
"catch_all",
"delegate",
"unreachable",
],
escapes:
/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
digits: /\d+(_+\d+)*/,
octaldigits: /[0-7]+(_+[0-7]+)*/,
binarydigits: /[0-1]+(_+[0-1]+)*/,
hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
tokenizer: {
root: [
// whitespace
{ include: "@whitespace" },
// strings
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
[/"/, "string", "@string"],
// numbers (not all of these are generated, but here to be sure)
[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/, "number.float"],
[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/, "number.float"],
[/0[xX](@hexdigits)[Ll]?/, "number.hex"],
[/0(@octaldigits)[Ll]?/, "number.octal"],
[/0[bB](@binarydigits)[Ll]?/, "number.binary"],
[/(@digits)[fFdD]/, "number.float"],
[/(@digits)[lL]?/, "number"],
// variable names
[/\$[^\s\)]*/, { token: "identifier" }],
// instructions and types
[
/[a-z0-9_]+(?:\.[a-z0-9_]+)*/,
{
cases: {
"@types": { token: "type.$0" },
"@keywords": { token: "keyword.$0" },
"@controlInstructions": { token: "controlInstruction.$0" },
"@instructions": { token: "instruction.$0" },
"@default": "identifier",
},
},
],
],
string: [
[/[^\\"]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/"/, "string", "@pop"],
],
whitespace: [
[/[ \t\r\n]+/, ""],
[/(;; )(ERROR |FAILURE )([^\n]*)/, ["comment", "error", ""]],
[/(;; )(WARNING )([^\n]*)/, ["comment", "warning", ""]],
[/(;; )(INFO )([^\n]*)/, ["comment", "info", ""]],
[/(;; )(PEDANTIC )([^\n]*)/, ["comment", "pedantic", ""]],
[/(;; +)(~+|\^)$/, ["comment", "underline"]],
[/(;; )([^\n]*)/, ["comment", ""]],
[/;;[^\n]*/, "comment"],
],
},
},
};
export default WebAssemblyTextLanguage;

87
xrpl-hooks-docs/docs.ts Normal file
View File

@@ -0,0 +1,87 @@
import hooksAccountBufLen from "./md/hooks-account-buf-len.md";
import hooksAccountConvBufLen from "./md/hooks-account-conv-buf-len.md";
import hooksAccountConvPure from "./md/hooks-account-conv-pure.md";
import hooksArrayBufLen from "./md/hooks-array-buf-len.md";
import hooksBurdenPrereq from "./md/hooks-burden-prereq.md";
import hooksDetailBufLen from "./md/hooks-detail-buf-len.md";
import hooksDetailPrereq from "./md/hooks-detail-prereq.md";
import hooksEmitBufLen from "./md/hooks-emit-buf-len.md";
import hooksEmitPrereq from "./md/hooks-emit-prereq.md";
import hooksEntryPointRecursion from "./md/hooks-entry-point-recursion.md";
import hooksEntryPointsNeg from "./md/hooks-entry-points-neg.md";
import hooksEntryPoints from "./md/hooks-entry-points.md";
import hooksFeePrereq from "./md/hooks-fee-prereq.md";
import hooksFieldAddBufLen from "./md/hooks-field-add-buf-len.md";
import hooksFieldBufLen from "./md/hooks-field-buf-len.md";
import hooksFieldDelBufLen from "./md/hooks-field-del-buf-len.md";
import hooksFloatArithPure from "./md/hooks-float-arith-pure.md";
import hooksFloatComparePure from "./md/hooks-float-compare-pure.md";
import hooksFloatIntPure from "./md/hooks-float-int-pure.md";
import hooksFloatManipPure from "./md/hooks-float-manip-pure.md";
import hooksFloatOnePure from "./md/hooks-float-one-pure.md";
import hooksFloatPure from "./md/hooks-float-pure.md";
import hooksGuardCalled from "./md/hooks-guard-called.md";
import hooksGuardInFor from "./md/hooks-guard-in-for.md";
import hooksGuardInWhile from "./md/hooks-guard-in-while.md";
import hooksHashBufLen from "./md/hooks-hash-buf-len.md";
import hooksKeyletBufLen from "./md/hooks-keylet-buf-len.md";
import hooksRaddrConvBufLen from "./md/hooks-raddr-conv-buf-len.md";
import hooksRaddrConvPure from "./md/hooks-raddr-conv-pure.md";
import hooksReserveLimit from "./md/hooks-reserve-limit.md";
import hooksSlotHashBufLen from "./md/hooks-slot-hash-buf-len.md";
import hooksSlotKeyletBufLen from "./md/hooks-slot-keylet-buf-len.md";
import hooksSlotLimit from "./md/hooks-slot-limit.md";
import hooksSlotSubLimit from "./md/hooks-slot-sub-limit.md";
import hooksSlotTypeLimit from "./md/hooks-slot-type-limit.md";
import hooksStateBufLen from "./md/hooks-state-buf-len.md";
import hooksTransactionHashBufLen from "./md/hooks-transaction-hash-buf-len.md";
import hooksTransactionSlotLimit from "./md/hooks-transaction-slot-limit.md";
import hooksValidateBufLen from "./md/hooks-validate-buf-len.md";
import hooksVerifyBufLen from "./md/hooks-verify-buf-len.md";
const docs: { [key: string]: string; } = {
"hooks-account-buf-len": hooksAccountBufLen,
"hooks-account-conv-buf-len": hooksAccountConvBufLen,
"hooks-account-conv-pure": hooksAccountConvPure,
"hooks-array-buf-len": hooksArrayBufLen,
"hooks-burden-prereq": hooksBurdenPrereq,
"hooks-detail-buf-len": hooksDetailBufLen,
"hooks-detail-prereq": hooksDetailPrereq,
"hooks-emit-buf-len": hooksEmitBufLen,
"hooks-emit-prereq": hooksEmitPrereq,
"hooks-entry-point-recursion": hooksEntryPointRecursion,
"hooks-entry-points-neg": hooksEntryPointsNeg,
"hooks-entry-points": hooksEntryPoints,
"hooks-fee-prereq": hooksFeePrereq,
"hooks-field-add-buf-len": hooksFieldAddBufLen,
"hooks-field-buf-len": hooksFieldBufLen,
"hooks-field-del-buf-len": hooksFieldDelBufLen,
"hooks-float-arith-pure": hooksFloatArithPure,
"hooks-float-compare-pure": hooksFloatComparePure,
"hooks-float-int-pure": hooksFloatIntPure,
"hooks-float-manip-pure": hooksFloatManipPure,
"hooks-float-one-pure": hooksFloatOnePure,
"hooks-float-pure": hooksFloatPure,
"hooks-guard-called": hooksGuardCalled,
"hooks-guard-in-for": hooksGuardInFor,
"hooks-guard-in-while": hooksGuardInWhile,
"hooks-hash-buf-len": hooksHashBufLen,
"hooks-keylet-buf-len": hooksKeyletBufLen,
"hooks-raddr-conv-buf-len": hooksRaddrConvBufLen,
"hooks-raddr-conv-pure": hooksRaddrConvPure,
"hooks-reserve-limit": hooksReserveLimit,
"hooks-slot-hash-buf-len": hooksSlotHashBufLen,
"hooks-slot-keylet-buf-len": hooksSlotKeyletBufLen,
"hooks-slot-limit": hooksSlotLimit,
"hooks-slot-sub-limit": hooksSlotSubLimit,
"hooks-slot-type-limit": hooksSlotTypeLimit,
"hooks-state-buf-len": hooksStateBufLen,
"hooks-transaction-hash-buf-len": hooksTransactionHashBufLen,
"hooks-transaction-slot-limit": hooksTransactionSlotLimit,
"hooks-validate-buf-len": hooksValidateBufLen,
"hooks-verify-buf-len": hooksVerifyBufLen,
};
export default docs;

View File

@@ -0,0 +1,6 @@
# hooks-account-buf-len
Function `hook_account` has fixed-size account ID output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,7 @@
# hooks-account-conv-buf-len
Function `util_raddr` has fixed-size account ID input.
This check warns unless the correct size is passed in the input size
parameter (if it's specified by a constant - variable parameter is
ignored).

View File

@@ -0,0 +1,10 @@
# hooks-account-conv-pure
Hooks identify accounts by the 20 byte account ID, which can be
converted to an raddr using the `util_raddr` function. If the account
ID never changes, a more efficient way to do this is precompute the
raddr from the account ID.
This check warns about calls of `util_raddr` with constant input and
proposes to add a tracing statement showing the computed value (so
that the user can use it to replace the call).

View File

@@ -0,0 +1,9 @@
# hooks-array-buf-len
Hook API `sto_subarray` requires non-empty input buffer and takes a
parameter specifying the array index, whose value is limited - the
sought object cannot be found if the limit is exceeded.
This check warns about empty input as well as too-large values of the
index specified in calls to `sto_subarray` (if they're specified by
constants - variable parameters are ignored).

View File

@@ -0,0 +1,5 @@
# hooks-burden-prereq
Hook API `etxn_burden` computes transaction burden, based on (i.a.)
the number of reserved transactions, so a call to it must be preceded
by a call to `etxn_reserve`.

View File

@@ -0,0 +1,6 @@
# hooks-detail-buf-len
Function `etxn_details` has fixed-size sfEmitDetails output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,5 @@
# hooks-detail-prereq
Hook API `etxn_details` serializes emit details, based on (i.a.) the
number of reserved transactions, so a call to it must be preceded by a
call to `etxn_reserve`.

View File

@@ -0,0 +1,6 @@
# hooks-emit-buf-len
Function `emit` has fixed-size transaction hash output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,5 @@
# hooks-emit-prereq
Before emitting a transaction using `emit` Hook API, a hook must set a
maximal count of transactions it plans to emit, by calling
`etxn_reserve`.

View File

@@ -0,0 +1,4 @@
# hooks-entry-point-recursion
Recursive calls are disallowed in the implementation of hook entry
points.

View File

@@ -0,0 +1,4 @@
# hooks-entry-points-neg
Shows error on function definitions with unexpected (that is, neither
`hook` nor `cbak`) names.

View File

@@ -0,0 +1,6 @@
# hooks-entry-points
A Hook always implements and exports exactly two functions: `cbak` and
`hook`.
This check shows error on translation units that do not have them.

View File

@@ -0,0 +1,5 @@
# hooks-fee-prereq
Hook API `etxn_fee_base` estimates a transaction fee, based on (i.a.)
the number of reserved transactions, so a call to it must be preceded
by a call to `etxn_reserve`.

View File

@@ -0,0 +1,9 @@
# hooks-field-add-buf-len
Emplacing a new field into STObject by calling `sto_emplace` requires
enough space to serialize the new STObject into; the API also limits
sizes of the old object and field.
This check warns about insufficient output buffer space as well as
too-large values of the inputs in calls to `sto_emplace` (if they're
specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,6 @@
# hooks-field-buf-len
Hook API `sto_subfield` requires non-empty input buffer.
This check warns about empty input in calls to `sto_subfield` (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,9 @@
# hooks-field-del-buf-len
Erasing a field from STObject by calling `sto_erase` requires enough
space to serialize the new STObject into; the API also limits size of
the old object.
This check warns about insufficient output buffer space as well as
too-large value of the input STObject in calls to `sto_erase` (if
they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,15 @@
# hooks-float-arith-pure
Hooks can compute floating-point values in XFL format by calling
functions `float_multiply`, `float_mulratio`, `float_negate`,
`float_sum`, `float_invert` and `float_divide` and access their
constituent parts by calling `float_exponent`, `float_mantissa` and
`float_sign`. If the inputs of the computation never change, a more
efficient way to do this is to precompute it.
This check warns about calls of the aforementioned functions with
constant inputs and in simple cases proposes to add a tracing
statement showing the computed value (so that the user can use it to
replace the call). It also checks that the divisor passed to
`float_divide`, `float_mulratio` and `float_invert` is not 0 (if it's
specified by a constant - variable parameters are ignored).

View File

@@ -0,0 +1,9 @@
# hooks-float-compare-pure
Hooks can compare floating-point values in XFL format by calling the
`float_compare` function. If the inputs of the comparison never
change, its result is fixed and the function need not be called.
This check warns about calls of `float_compare` with constant inputs
as well as invalid values of the comparison mode parameter (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,10 @@
# hooks-float-int-pure
Hooks can convert floating-point values in XFL format to integers by
calling the `float_int` function. If the inputs of this function never
change, a more efficient way to do this is to precompute the integer
value.
This check warns about calls of `float_int` with constant inputs as
well as invalid values of the decimal places parameter (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,13 @@
# hooks-float-manip-pure
Hooks can directly manipulate floating-point values in XFL format by
calling functions `float_exponent_set`, `float_mantissa_set` and
`float_sign_set`. If the inputs of the update never change, a more
efficient way to do this is to precompute it.
This check warns about calls of the aforementioned functions with
constant inputs and in simple cases proposes to add a tracing
statement showing the computed value (so that the user can use it to
replace the call). It also checks documented bounds of the second
parameter of these functions (if it's specified by a constant -
variable parameter is ignored).

View File

@@ -0,0 +1,5 @@
# hooks-float-one-pure
Hooks can obtain XFL enclosing number 1 by calling the float_one
function. Since the number never changes, a more efficient way is to
use its precomputed value.

View File

@@ -0,0 +1,12 @@
# hooks-float-pure
Hooks can use floating-point values in XFL format, creating them from
mantissa and exponent by calling the `float_set` function. If the
mantissa and exponent never change, a more efficient way to do this is
to precompute the floating-point value.
This check warns about calls of `float_set` with constant inputs and
proposes to add a tracing statement showing the computed value (so
that the user can use it to replace the call). In the special case of
0 mantissa and 0 exponent ("canonical 0"), a replacement value of 0 is
proposed directly, with no need to trace it.

View File

@@ -0,0 +1,5 @@
# hooks-guard-called
Every hook needs to import the guard function `_g` and use it at least once.
[Read documentation](https://xrpl-hooks.readme.io/docs/loops-and-guarding)

View File

@@ -0,0 +1,12 @@
# hooks-guard-in-for
Consider the following for-loop in C:
```c
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
for (int i = 0; GUARD(3), i < 3; ++i)
```
This is the only way to satisfy the guard rule when using a for-loop
in C.

View File

@@ -0,0 +1,11 @@
# hooks-guard-in-while
Like for loops, while loops must have a guard in their condition:
```c
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
int i = 0;
while (GUARD(3), i < 3)
```

View File

@@ -0,0 +1,7 @@
# hooks-hash-buf-len
Functions `util_sha512h`, `hook_hash`, `ledger_last_hash` and `nonce`
have fixed-size hash output.
This check warns about too-small size of their output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-keylet-buf-len
Computing a ripple keylet by calling `util_keylet` requires valid
parameters dependent on the keylet type.
This check does not fully parse these parameters, but warns about
invalid keylet type as well as buffer sizes that cannot be valid (if
they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,8 @@
# hooks-raddr-conv-buf-len
Hook API `util_accid` has upper limit on the length of its input
(because it expects it to be a raddr) and fixed-size account ID
output.
This check warns about invalid sizes of input and output parameters
(if they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,10 @@
# hooks-raddr-conv-pure
Hooks identify accounts by the 20 byte account ID, which can be
converted from a raddr using the `util_accid` function. If the raddr
never changes, a more efficient way to do this is precompute the
account-id from the raddr.
This check warns about calls of `util_accid` with constant input and
proposes to add a tracing statement showing the computed value (so
that the user can use it to replace the call).

View File

@@ -0,0 +1,9 @@
# hooks-reserve-limit
Hook API `etxn_reserve` takes a parameter specifying the number of
transactions intended to emit from the calling hook. Value of this
parameter is limited, and the function fails if the limit is exceeded.
This check warns about too-large values of the number of reserved
transactions (if they're specified by a constant - variable parameter
is ignored).

View File

@@ -0,0 +1,7 @@
# hooks-slot-hash-buf-len
Function `slot_id` has fixed-size canonical hash output.
This check warns about too-small size of its output buffer as well as
invalid values of the slot number parameter (if they're specified by
constants - variable parameters are ignored).

View File

@@ -0,0 +1,7 @@
# hooks-slot-keylet-buf-len
Function `slot_set` has structured keylet input.
This check does not parse the input, but warns about its sizes that
cannot be valid as well as invalid values of the slot number parameter
(if they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,9 @@
# hooks-slot-limit
Hook APIs `slot`, `slot_count`, `slot_clear`, `slot_size`,
`slot_float` and `trace_slot` take a parameter specifying the accessed
slot number. Value of this parameter is limited, and the functions
fail if the limit is exceeded.
This check warns about too-large values of the slot number (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-slot-sub-limit
Hook APIs `slot_subarray` and `slot_subfield` take parameters
specifying parent and child slot numbers. Values of these parameters
are limited, and the functions fail if the limit is exceeded.
This check warns about too-large values of the slot numbers (if
they're specified by a constant - variable parameters are ignored).

View File

@@ -0,0 +1,9 @@
# hooks-slot-type-limit
Hook API `slot_type` takes a parameter specifying the accessed slot
number. Value of this parameter is limited, and the function fails if
the limit is exceeded.
This check warns about too-large values of the slot number as well as
invalid values of the flags parameter (if they're specified by
constants - variable parameters are ignored).

View File

@@ -0,0 +1,6 @@
# hooks-state-buf-len
Functions state and state_set accept fixed-size Hook State key.
This check warns about invalid size of its input buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,6 @@
# hooks-transaction-hash-buf-len
Function `otxn_id` has fixed-size canonical hash output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-transaction-slot-limit
Function `otxn_slot` takes a parameter specifying the accessed slot
number. Value of this parameter is limited, and the function fails if
the limit is exceeded.
This check warns about too-large values of the slot number (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,6 @@
# hooks-validate-buf-len
Hook API `sto_validate` requires non-empty input buffer.
This check warns about empty input in calls to `sto_validate` (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-verify-buf-len
Verifying a cryptographic signature by calling `util_verify` requires
valid public key & data signature.
This check does not fully parse these parameters, but warns about
their sizes that cannot be valid (if they're specified by constants -
variable parameters are ignored).

112
yarn.lock
View File

@@ -886,6 +886,11 @@
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.4.tgz#aaf9b96296150d737b2fefa535ced05ed8013d84"
integrity sha512-sPZYQEIF/SOnLAvaz9lTuydniP+afBMtElRTdYkeV1QtEgvtJ7qolCPjly6O32QI8CbEmP5O/fztMXEDWfEcrg==
"@types/json-schema@^7.0.8":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -898,6 +903,18 @@
dependencies:
"@types/node" "*"
"@types/lodash.uniqby@^4.7.6":
version "4.7.6"
resolved "https://registry.yarnpkg.com/@types/lodash.uniqby/-/lodash.uniqby-4.7.6.tgz#672827a701403f07904fe37f0721ae92abfa80e8"
integrity sha512-9wBhrm1y6asW50Joj6tsySCNUgzK2tCqL7vtKIej0E9RyeBFdcte7fxUosmFuMoOU0eHqOMK76kCCrK99jxHgg==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.179"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5"
integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==
"@types/lodash@^4.14.136":
version "4.14.177"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
@@ -1049,7 +1066,12 @@ aggregate-error@^3.1.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
ajv@^6.10.0, ajv@^6.12.4:
ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1880,6 +1902,11 @@ emojis-list@^2.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
encoding@0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
@@ -2273,6 +2300,11 @@ file-uri-to-path@1.0.0:
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
filesize@^8.0.7:
version "8.0.7"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8"
integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -2833,6 +2865,13 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
javascript-time-ago@^2.3.11:
version "2.3.11"
resolved "https://registry.yarnpkg.com/javascript-time-ago/-/javascript-time-ago-2.3.11.tgz#b488ae765b7f42ff003f3830977a92411ef1fe92"
integrity sha512-R6Ux0IGv7RmAZ7vB04r/gevGaLDJeZY7+6jTYyYsPAF5x4PSv6up+dW2hLK99+OUY8xwdeXPItZFiUppIRUaoQ==
dependencies:
relative-time-format "^1.0.6"
jest-worker@27.0.0-next.5:
version "27.0.0-next.5"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28"
@@ -2882,6 +2921,13 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
json5@^2.1.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
dependencies:
minimist "^1.2.5"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -2988,6 +3034,15 @@ loader-utils@1.2.3:
emojis-list "^2.0.0"
json5 "^1.0.1"
loader-utils@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^2.1.2"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -3058,6 +3113,11 @@ lodash.truncate@^4.4.2:
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
lodash.uniqby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=
lodash@^4.17.15, lodash@^4.17.4:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@@ -3146,7 +3206,7 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.0:
minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
@@ -3608,6 +3668,11 @@ pbkdf2@^3.0.3, pbkdf2@^3.0.9:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phosphor-react@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/phosphor-react/-/phosphor-react-1.3.1.tgz#96e33f44370d83cda15b60cccc17087ad0060684"
@@ -3754,6 +3819,13 @@ queue@6.0.2:
dependencies:
inherits "~2.0.3"
raf@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
dependencies:
performance-now "^2.1.0"
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -3779,6 +3851,14 @@ raw-body@2.4.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-loader@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6"
integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
dependencies:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
re-resizable@^6.9.1:
version "6.9.1"
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.1.tgz#6be082b55d02364ca4bfee139e04feebdf52441c"
@@ -3889,6 +3969,15 @@ react-style-singleton@^2.1.0:
invariant "^2.2.4"
tslib "^1.0.0"
react-time-ago@^7.1.9:
version "7.1.9"
resolved "https://registry.yarnpkg.com/react-time-ago/-/react-time-ago-7.1.9.tgz#df3999f1184b71ab09aaf1d05dda2a19b76e0bb8"
integrity sha512-jN4EsEgqm3a5eLJV0ZrRpom3iG+w4bullS2NHc2htIucGUIr2FbgHXjU0wIkuN9uWM87aFvIfkUDpS/ju7Mazg==
dependencies:
memoize-one "^6.0.0"
prop-types "^15.7.2"
raf "^3.4.1"
react-transition-group@^4.3.0:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@@ -3956,6 +4045,11 @@ regenerator-runtime@^0.13.4:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regexify-string@^1.0.17:
version "1.0.17"
resolved "https://registry.yarnpkg.com/regexify-string/-/regexify-string-1.0.17.tgz#b9e571b51c8ec566eb82b7121744dae0d8e829de"
integrity sha512-mmD0AUNaY/piGW2AyACWdQOjIAwNuWz+KIvxfBZPDdCBAexiROeQxdxTaYAWcIxwtUAOwojdTta6CMMil84jXw==
regexp.prototype.flags@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"
@@ -3969,6 +4063,11 @@ regexpp@^3.1.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
relative-time-format@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/relative-time-format/-/relative-time-format-1.0.6.tgz#4956f6aa161f63cbab8a7f2d8b2e92d7b6a888a3"
integrity sha512-voemOJLxlKun4P1fAo4PEg2WXNGjhqfE/G8Xen4gcy24Hyu/djn5bT5axmhx4MnjynoZ8f0HCOjk3RZpsY6X/g==
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -4162,6 +4261,15 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
dependencies:
"@types/json-schema" "^7.0.8"
ajv "^6.12.5"
ajv-keywords "^3.5.2"
semver@^5.5.0, semver@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"